Vuejs APIアクセスはcreatedとmountedのどちらで行う?

created と mounted

どちらもVuejsが提供するライフサイクルフック。たいていのサンプルでは、このライフサイクルフックのどちらかでAPIアクセスをするが、どんな違いがあるんだろう。

lifecycle

  • created

    • インスタンスの初期化が済んで props や computed にアクセスできる
    • DOMにはアクセスできない
  • mounted

    • created + DOMにアクセスできる

APIアクセスは created と mounted のどちらで行う?

APIアクセスはほとんどのライブラリで非同期に行われる。そのため、 created と mounted のどちらでAPIアクセスを開始しようが、レスポンスが返ってきた時点でコールバックが実行される。

上記を踏まえて、レスポンス完了後のコールバックの中で、

  • propsにデータを設定するだけの場合は、 created を使う

    • DOMを構築してる間にも、HTTPの通信を行えるから
    • DOMがでかいと、Edgeだと、mountedよりも体感できるレベルで早くなる
  • DOMにアクセスする必要があるときは、 mounted を使う

    • レスポンスが即返ってきた場合に、DOMの構築が終わっていない可能性がある
    • jQuery 時代をひきずったようなDOMを直接指定するライブラリを使うときはこっち

と良さそう。

検証環境

  "dependencies": {
    "axios": "^0.17.1",
    "vue": "^2.5.2"
  }

json serverを用意する

npm で json-server をインストールし、起動する。

$ json-server --watch db.json
{
  "users": [
  {
    "id": 1,
    "name": "Stark"
  },
  {
    "id": 2,
    "name": "Targaryen"
  },
  {
    "id": 3,
    "name": "Tyrell"
  }
  ]
}

サンプルを用意する

<template>
  <section>
  <h1>ユーザリスト</h1>
  <ul>
    <li v-for="user in users" :key="user.id">
    {{user.name}}
    </li>
  </ul>
  </section>
</template>

<script>
import Axios from 'axios'

export default {
  name: 'Users',
  data () {
    return { users: [] }
  },
  created () {
    const self = this
    Axios.get('http://localhost:3000/users')
    .then((res) => {
      self.users = res.data
    })
  }
}
</script>

実行結果

  • mounted が実行される
  • usersが [] の状態でDOMが作られる(『ユーザリスト』のタイトルだけ先に表示される)
  • レスポンスが返ってきたら users に値が設定される
  • Vueが変更を検知してDOMが再構築される

参考

env_file

コンテナ内で利用できる環境変数を、外部ファイルから読み込める。この変数は、docker-compose.ymlからは利用できない。

$ cat app.env 
SAMPLE_APP_VERSION=1.0.0
version: "3"
services:
  app:
    image: "alpine:latest"
    env_file:
      - app.env
    command: env
$ docker-compose up
Recreating docker_app_1 ... done
Attaching to docker_app_1
app_1  | PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
app_1  | HOSTNAME=6656d72f7508
app_1  | SAMPLE_APP_VERSION=1.0.0
app_1  | HOME=/root

docker-compose run -e XXX=YYY

コンテナ内で利用できる変数をdocker-composeの起動引数で設定できる。この変数は、docker-compose.ymlからは利用できない。

version: "3"
services:
  app:
    image: "alpine:latest"
    command: env
$ docker-compose run -e SAMPLE_APP_VERSION=1 app
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=6ca4fb8fa3f4
TERM=xterm
SAMPLE_APP_VERSION=1
HOME=/root

変数の優先順位

コンテナ内の変数の優先順位

リファレンスの通り。

  1. Compose file,
  2. Environment file,
  3. Dockerfile,
  4. Variable is not defined.

docker-compose.yml で参照するときの優先順位

環境変数 > .envファイル

version: "3"
services:
  app:
    container_name: ${NAME}
    image: "alpine:latest"
    command: env
$ cat .env 
NAME=hoge
$ export NAME=fuga
$ docker-compose up
Recreating hoge ... done
Attaching to fuga
fuga   | PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
fuga   | HOSTNAME=68c4de49dd38
fuga   | HOME=/root
fuga exited with code 0

動的な値を利用したい

docker-compose.yml

docker-compose.ymlで動的な値を利用したければ、docker-compose を実行する前に、環境変数を設定すれば良い。

version: "3"
services:
  app:
    container_name: "app-${ID}"
    image: "alpine:latest"
    command: env
$ export ID=$(date  '+%Y%m%d')
$ docker-compose up
Creating app-20180311 ... 
Creating app-20180311 ... done
Attaching to app-20180311
app-20180311 | PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
app-20180311 | HOSTNAME=10df3a31c3ff
app-20180311 | HOME=/root
app-20180311 exited with code 0

Dockerコンテナ

Dockerコンテナで動的な値を利用したければ、environmentで環境変数を指定しておき、

version: "3"
services:
  app:
    image: "alpine:latest"
    environment:
      - TIMESTAMP
    command: env

docker-compose 実行前に、環境変数を設定すれば良い。

$ export TIMESTAMP=$(date '+%Y%m%d')
$ docker-compose up
Recreating compose_app_1 ... 
Recreating compose_app_1 ... done
Attaching to compose_app_1
app_1  | PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
app_1  | HOSTNAME=4f2282348e06
app_1  | TIMESTAMP=20180311
app_1  | HOME=/root

あるいは、env_fileで読み込むファイルをdocker-compose実行前に作成する。

version: "3"
services:
  app:
    image: "alpine:latest"
    env_file:
      - app.env
    command: env
$ cat > app.env  << FIN
TIMESTAMP=$(date '+%Y%m%d')
FIN
$ docker-compose up
Starting compose_app_1 ... 
Starting compose_app_1 ... done
Attaching to compose_app_1
app_1  | PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
app_1  | HOSTNAME=4f2282348e06
app_1  | TIMESTAMP=20180311
app_1  | HOME=/root
compose_app_1 exited with code 0

参考

パケットキャプチャ

LBのeth0(クライアント側NIC)のパケットをキャプチャする。

# tcpdump port 80 -i eth0 -w eth0.pcap

LBのeth1(バックエンド側NIC)のパケットをキャプチャする。

# tcpdump port 80 -i eth1 -w eth1.pcap

上記から、LBで接続元IPや接続先IPの変換はするものの、TCPコネクションが1本しか張られていないことがわかる。(接続元のエフェメラルポート番号がそのまま接続先に渡されており、また、時刻から、LBはパケットを横流ししているようになっている)

MACアドレス変換方式(DSR方式)

構成図は以下のとおり。

以下を参考に、LBとしてipvsadm を利用する。
http://momijiame.tumblr.com/post/71390424576/centos-65-%E3%81%A7-lvs-ipvs-%E3%81%AE-direct-server-return

設定

LBでipvsadmの設定をする。

]# iptables -L
]# ipvsadm -A -t 192.168.11.107:80 -s lc
]# ipvsadm -a -t 192.168.11.107:80 -r 192.168.11.108:80 -g
]# ipvsadm -L
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  192.168.11.107:http lc
  -> 192.168.11.108:http          Route   1      0          0

次に、バックエンドサーバでhttpdを起動し、クライアントからHTTPリクエストを投げる。

パケットキャプチャ

LBのパケットをキャプチャする。

# tcpdump port not 22 -w lb.pcap

戻りパケットがLBを経由しないため、Wiresharkがパケットが欠けてると警告を出している。

バックエンドサーバのパケットをキャプチャする。

# tcpdump port 80 -w backend.pcap

接続先IPアドレスがLBのIPアドレスになっている。また、接続元IPアドレスがクライアントのアドレスになっている。

上記から、戻りパケットだけはLBを経由せずにクライアントにダイレクトに返っていることがわかる。まさにDirect Server Return…

参考