SSR する時にフロントエンドからバックエンドのエンジニアに最小限伝えておきたい Nuxt.js の挙動

## というわけで

実際に手を動かしてもらった方が早いので Tutorial を用意しました。

## 準備

vue-cli がなければ install しておく

```sh
% npm install -g vue-cli
```

## デモプロジェクトを用意する

```sh
mkdir practice-nuxt-docker && cd $_
vue init nuxt-community/starter-template myapp 
touch myapp/Dockerfile docker-compose.yml
cd myapp
yarn init -y
yarn
yarn nuxt build # 重要
cd ..
```

create `myapp/Dockerfile`

```sh
RUN yarn add nuxt
WORKDIR /app
ENV HOST 0.0.0.0
EXPOSE 3000
CMD yarn nuxt start
```

create `Dockerfile`

```yml
version: '3' 
services:
  nuxt:
    build: myapp
    tty: true
    volumes:
      - "./myapp:/app"
    ports:
      - "3000:3000"
```

```sh
docker-compose build
docker-compose up
```

http://localhost:3000 へアクセスして動作確認します。`nuxt build` が必要ですのでデプロイツールを構築する人にちゃんと伝えておきましょう。

## 最初の Request は Node.js が HTML を返しますが、その直後にブラウザ側でレンダリングされている

以下のように修正します。 

```diff
% git diff                                                               [master ~/samurai/practice-nuxt-docker/myapp]
diff --git a/myapp/pages/index.vue b/myapp/pages/index.vue
index cf60434..2e2e2b5 100644
--- a/myapp/pages/index.vue
+++ b/myapp/pages/index.vue
@@ -8,6 +8,8 @@
       <h2 class="subtitle">
         Nuxt.js project
       </h2>
+      <h3 v-if="isClient()">Client</h3>
+      <h3 v-else>Server</h3>
       <div class="links">
         <a
           href="https://nuxtjs.org/"
@@ -28,6 +30,11 @@ import AppLogo from '~/components/AppLogo.vue'
 export default {
   components: {
     AppLogo
+  },
+  methods: {
+    isClient() {
+      return (typeof window !== 'undefined' && window.document)
+    }
   }
 }
```

Nuxt.js のアプリを起動します。(以下の例は docker を経由しない例です)

```sh
cd myapp
yarn nuxt build
yarn nuxt start
```

この状態で `http://localhost:3000` を Reload すると一瞬 Server と表示されますが、直後に Client に置き換わる挙動を確認する事ができます。これ以降はブラウザ側で JavaScript が実行されます。

最初のリクエストの段階では Node.js が Javascript を処理するため、このタイミングでは localStorage へのアクセスはできません。access_token の保持などは cookie を使うと良いでしょう。素直に `@nuxtjs/axios` を使っていれば Header の情報はそのままバックエンドに渡されるはずです。たぶん。

https://axios.nuxtjs.org/options#proxyheaders 

## nuxt build が生成しているファイルの行方

`nuxt build` を実行すると以下のように圧縮された js ファイルが生成されます。

```
Hash: 893dbac70d5b02802558
Version: webpack 4.22.0
Time: 6225ms
Built at: 2018-10-23 12:02:07
                  Asset       Size  Chunks             Chunk Names
41297c5455157e9ca810.js   2.18 KiB       3  [emitted]  runtime
714f38d18aae84388e71.js   3.84 KiB       2  [emitted]  pages/index
               LICENSES  422 bytes          [emitted]  
a136c91fe4110122940e.js   31.1 KiB       0  [emitted]  app
ca15e729f68a6493bcb3.js    134 KiB       1  [emitted]  commons.app
```

これは以下の場所に配備されます。

```sh

% ls -l myapp/.nuxt/dist/client                                                
total 360
-rw-r--r--  1 okamuuu  staff    2228 10 23 12:02 41297c5455157e9ca810.js
-rw-r--r--  1 okamuuu  staff    3928 10 23 12:02 714f38d18aae84388e71.js
-rw-r--r--  1 okamuuu  staff     423 10 23 12:02 LICENSES
-rw-r--r--  1 okamuuu  staff   31824 10 23 12:02 a136c91fe4110122940e.js
-rw-r--r--  1 okamuuu  staff  137438 10 23 12:02 ca15e729f68a6493bcb3.js
```

そして以下の URL で配信されます。このファイルを `nuxt start` で動いているプロセスが返すのは効率が悪い気がするのでどうやってデプロイしてキャッシュしたりするかなどをインフラエンジニアに相談してみてください。

```
http://localhost:3000/_nuxt/41297c5455157e9ca810.js
http://localhost:3000/_nuxt/714f38d18aae84388e71.js
http://localhost:3000/_nuxt/LICENSES
http://localhost:3000/_nuxt/a136c91fe4110122940e.js
http://localhost:3000/_nuxt/ca15e729f68a6493bcb3.js
```

## asyncData

非同期処理を SSR する場合、Nuxt.js では asyncData に記述するだけで簡単に実装できます。

```sh

mkdir myapp/pages/posts
touch myapp/pages/posts/index.vue
```

create `pages/posts/index.vue`

```javascript

<template>
  <div class="container">
    <h1>Posts</h1>
    <ul>
      <li v-for="(post, index) in posts" :key="index">
        <nuxt-link :to="{ name: 'posts-id', params: { id: post.id } }">{{ post.title }}</nuxt-link>
      </li>
    </ul>
    <p><nuxt-link to="/">Back to home page</nuxt-link></p>
  </div>
</template>

<script>
import axios from 'axios'

export default {
  asyncData({ req, params }) {
    return axios.get('https://jsonplaceholder.typicode.com/posts')
      .then((res) => {
        return { posts: res.data.slice(0, 5) }
      })  
  }
}
</script>
```

`nuxt` あるいは `nuxt build && nuxt start` でプロセスを起動してから以下の curl コマンドを実行してください。非同期処理で取得している情報が HTML で記述されてる事を確認できます。

```
curl -s http://localhost:3000/posts
```

## 他にも何かあれば

追記します。

* 非同期処理であっても Node.js のプロセスで rendering して HTML を返す挙動が若干イメージしづらいようなので `asyncDate について` を追記しました。