Nuxt.jsとHerokuでお手軽にSSRを体験する

これは、Misoca Advent Calendar 2017 の6日目のエントリーです。

SSR

フロントエンドを駆使したアプリケーションを構築すると、 どこかのタイミングでSSR(ServerSideRendering)と向きあうことがある。

最近はクローラが優秀なので、SSRを行わないSPAでもいい感じにインデックスしてくれたりするので、そこまで神経質になることもないことが多いですが、ファーストビューの速度などを突き詰めていくと、やっぱり選択肢としては使える状態にしておきたいところ。

実際にやろうとすると簡単なものではなく、Railsの場合はreact_railsのようなgemに頼ったりすることになったり、頼っても大変だったりするのが現実だと思う。

Next.js & Nuxt.js

といったわけで、Next.js/Nuxt.jsの名前をちらほら見かけるようになったと思う。

オフィシャルのガイドなどをざっと見て「ふむふむ」程度で終わっていたけど、触りだけでも一度試してみることにした。

個人的に、以前はReactがメインだったが、最近ではVueを使うことのほうが多いので、今回はNuxt.jsのほうをやってみることにする。

Nuxt.js

https://ja.nuxtjs.org/guide

Nuxt.js とはユニバーサルな Vue.js アプリケーションを構築するためのフレームワークです。

だそうです。

本気でVueでのSSR可能なSPAを組もうと思うと、以下のような要素が出てくる。

  • Vue
  • vue-router
  • vue-server-renderer
  • vuex
  • babel
  • webpack
  • 場合によってはexpressなど

その上で、アプリケーション自体の構成からルーティングルールを検討した上で、サーバサイド、フロントエンドどちらのレンダリングでも耐えうる形で作っていく必要があり、普通につらい。

Nuxt.jsを使うことで、ざっくりこのあたりを隠蔽してくれるので、とりあえずの出だしとして細かいことを考えずにコードを書き始めることができる。

Nuxt.jsの敷いたレール上に乗るぶんには楽できるけど、カスタマイズしようと思うと細かい知識が必要になるよ、みたいな話なので、考え方としてはRailsWayとかに近いのかなって印象。

やってみよう!

ゴール

  • Heroku上でNuxt.jsで作ったアプリケーションのSSRを確認できる

やらないこと

  • 凝ったカスタマイズは一切しない

インストールする

スターターテンプレートというものが用意されているので、そちらを利用する。

https://ja.nuxtjs.org/guide/installation

$ npm install -g vue-cli 
$ vue init nuxt-community/starter-template nuxt-trial

# 色々聞かれるけどデフォルトで良いのでEnter連打

$ cd nuxt-trial
$ yarn install

試しに動かしてみる。

$ npm run dev

http://localhost:3000/ にアクセスすると、以下のようなページが表示される。

f:id:mugi1:20171203001108p:plain

なんか動いてるようだ。

ちなみにnuxtのコードを見てみると、サーバ自体はconnectを利用してるっぽかった。

このままHerokuに放り込んでみる

インストールされたものはとりあえずあとで見るとして、このままHerokuに放り込んで動くかやってみよう。

FAQにずばりそのままのページが用意されている。

https://ja.nuxtjs.org/faq/heroku-deployment

心を無にして上から叩く。 (herokuのcliはインストールしてあるものとする)

$ heroku create
$ heroku config:set NPM_CONFIG_PRODUCTION=false
$ heroku config:set HOST=0.0.0.0
$ heroku config:set NODE_ENV=production

package.jsonに追記して、Heroku側でよしなにビルドしてくれるようにscriptsを足す。

  ~
  "scripts": {
    ...
    "heroku-postbuild": "npm run build"
  },
  ~

デプロイする

git push heroku master

これでブラウザからHerokuのアプリケーションを見てみると、ローカルと同様に動いていることが確認できる。

簡単。

SSRをやってみよう

では、肝心のSSRがちゃんと動くかを確認してみる。

確認のために以下のようなページを作ってみる。

  • 2ページあって行き来できる
  • ページごとに初期化時に何らかのAPIのデータを取得して表示する

API自体はなんでもいいので、THE COLOR APIというサイトから適当にカラー情報を取得することにする。

http://www.thecolorapi.com/

コンポーネントをつくる

非同期データのためにaxiosを追加しておく。

yarn add axios

色情報を取得するAPIとカラースキーマを取得するAPIの2つがあるので、その2つをページにしてみる。 内容は、ランダムなカラーをもとにデータを表示する超簡易的なもの。

pages/color.vue

<template>
  <section class="container">
    <div>
      <span>color</span>
      <span>|</span>
      <span><nuxt-link to="/scheme">scheme</nuxt-link></span>
    </div>

    <div><img :src="data.image.named" /></div>
  </section>
</template>

<script>
import axios from 'axios'

export default {
  async asyncData () {
    const hex = Math.floor(Math.random() * 16777215).toString(16)
    const { data } = await axios.get(`http://www.thecolorapi.com/id?hex=${hex}`)
    return { data }
  }
}
</script>

pages/schema.vue

<template>
  <section class="container">
    <div>
      <span><nuxt-link to="/color">color</nuxt-link></span>
      <span>|</span>
      <span>scheme</span>
    </div>

    <div><img :src="data.image.named" /></div>
  </section>
</template>

<script>
import axios from 'axios'

export default {
  async asyncData () {
    const hex = Math.floor(Math.random() * 16777215).toString(16)
    const { data } = await axios.get(`http://www.thecolorapi.com/scheme?hex=${hex}`)
    return { data }
  }
}
</script>

この内容でコミットし、改めてHerokuにpushする。

動きを確認してみる

/color

f:id:mugi1:20171203001140p:plain

/scheme

f:id:mugi1:20171203001455p:plain

ちゃんと動いてるっぽい。

(ちなみに画面上で遷移しようとすると、クライアント上でhttpsからhttpのXHRとなるので、Mixed Contentのエラーで落ちる。対処するのが面倒なので、Chromeの場合はURLバー横のアイコンから強制的に許可して回避する。)

Chrome DevToolsのNetworkタブを使って、画面上のリンクで遷移した場合のリクエストを見てみても、初回表示以外はxhrリクエストになっていることがわかる。

f:id:mugi1:20171203001635p:plain

肝心のSSRはということで、ダイレクトアクセスした直後にページのソースコードを見てみる。

f:id:mugi1:20171203002004p:plain

API経由で取得する画像へのURLがファーストビューとなるHTMLに含まれていることが確認できる。

というわけで

これだけであれば、たいした苦労もなく、SSR可能なVue.jsアプリケーションをHerokuで動かすことができた。

使い込んでいくと細かいカスタマイズなどで苦労することはありそうだけど、この簡単さは強力だな〜という印象。

もう少しちゃんとしたアプリケーションを作って検証してみてもいいかなという気持ちになった。

明日は @oosawatechによる「野生化」の話です。

野生化...!?