Kekeの日記

エンジニア、読書なんでも

ポートフォリオサイトをVue.jsからNuxt.jsに書き換えてサーバーサイドレンダリングをする

f:id:bobchan1915:20181011033300p:plain

本記事

本記事はVue.jsを使ってGoogle App Engineにホストしている静的なサイトをNuxt.jsを使って書き直そう思います。

題材は以前使った自分のポートフォリトサイトです。

keisukeyamashita.com

GoogleCloudPlatform(GCP)で無料枠を使いきり画像などのコンテンツをCloud Storageに置いてありましたが、Embulkをマスターしたことによって簡単に二つ目のアカウントに移行できたので、再度ホストしようと思いました。

目的

今回の移行には、以下のような目的がありました。

コンポーネント設計が自由すぎる

どこまでをどうコンポーネント分けするのかは、Vue.jsやReactを使っている人ならよくあると思います。

僕自身も、学生で、インターンといえばサーバーサイド、インフラ、モバイルアプリしか歩んでこなかったので、フロントエンドを知識がなく、HTMLやCSS、JSは書けるけど正しいかどうか分からないままコンポーネントを作っていました。

SVGを自分で書いた時も、個人的にSVGのViewboxが非常に苦手(理解できない)ので、SVGコンポーネントとして作ってしまってたくらいカオスな情況でした。

本当にアトミックなコンポーネント分けだと、どうしてもコンポーネントのネストがひどくなって、逆に管理も面倒でした。

また、ディレクトリ分けもそうです。 どこにどう置いても良いVue.jsは雑でした。

Nuxt.jsはコンポーネント分けまではやってくれませんが、ある程度ディレクトリ構成などソフトウェアに秩序をくれるため導入しました。

SEO対策とサーバーサイドレンダリング

今ではSEO対策にNuxt.jsが提供するサーバーサイドレンダリングは関係ないということが発表されていて、本当にどうなのかは調査不足のため知りませんが(ここではそれを信じるとしても)数年間、この「サーバーサイドレンダリング」がフロントエンド界隈では盛り上がっていたので知っておく価値はあるのかなと思いました。

またサーバーサイドレンダリングは、大きな容量のjsを返すのではなくて、初期表示を高速化するためにサーバーで処理して返すのは結局はUXにつながり、Googleがクロールするかだけでなく、SEO対策の一環となると思うので、重要だと思いました。

移行する

Nuxt.jsプロジェクトを作成する

npxを使ってNuxt.jsプロジェクトをから作成します。

npx create-nuxt-app keisukeyamashita-portfolio-nuxt

cp /vue/project/path/views/* ./pages

移行により必要なくなったパッケージをuninstallする

以下のように必要のないパッケージをアンインストールします。

ディレクトリを分ける

特に今回は設計に秩序をもたらしたいと思いNuxt.jsを採用したので、ディレクトリ構造について理解する必要がありました。

移行について今回知るべきことは以下の項目でした。

  • /pages: ルーティングファイル。これを元にルーティングし、.vueである必要があります。SPAなので一つしかありません。
  • /static: 静的ファイル。robot.txtやfavicon.icoを入れました。
  • /components: Vueコンポーネントを入れました。
  • plugins: 外部ライブラリなどをimportするためのもの

この時点で以下のようになっています。

.
├── README.md
├── package.json
└── src
    ├── components
    ├── pages
    ├── plugins
    └── static

Plugins

これはvendor.jsに含めたいので以下のようにnuxt.config.jsに書き加えます。

また、これらのプラグインはアプリケーションを表示するためにかならずしも重要ではないためapp.jsには入れずにvendor.jsとしてまとめたいのでwebpackの設定を書き加えます。

...
plugins: [
    '~plugins/hoge'
  ],
...
build: {
    vendor: [
      'vue-lazyload'
    ],
}
...

buildに書くことによってvendor.jsに含まれるようになります。

と思ったのですが、Stackdriver Loggingでみると以下のようなエラーがでます。

{
 insertId:  "5bbe4a710005f68ac8672452"  
 labels: {…}  
 logName:  "projects/keisukeyamashita-portfolio/logs/stdout"  
 receiveTimestamp:  "2018-10-10T18:52:33.413653645Z"  
 resource: {…}  
 textPayload:  "[18:52:33] vendor has been deprecated due to webpack4 optimization"  
 timestamp:  "2018-10-10T18:52:33.390794Z"  
}

この記事をみて、webpack4からは最適化ロジックの変更が内部でありVendorは指定できないようになっています。

medium.com

一部コードを書き直す

例えばdocumentbodyなどはクライアントサイドレンダリングでしかアクセスできないので以下のようなものも当然サーバーサイドレンダリングではできません。

  beforeCreate: function () {
    document.body.className = 'body'
  },

これはDOMの構築などクリティカルレンダリングパスを学ぶとわかると思います。

developers.google.com

ルーティングを実装

/pagesディレクトリを正しく構築できていれば、自動的にrouterは生成されます。今まではvue-routerを使っていたし、ストリングパラメータやバリデーションの方法など、少しAPIが違うことがわかりました。

ここまでできたら、とりあえずローカルで起動できるか確認してみます。

ローカル環境ではサーバーサイドレンダリングをしないので、以下のようにします。

npm run dev

最適化をする

どのファイルが重いかを可視化をすることができます。

nuxt build --analyze

f:id:bobchan1915:20181011040543p:plain

at-uiが必要ないことに気づき、削除しました。

f:id:bobchan1915:20181011041108p:plain

ビルドしてみる

以下のコマンドでビルドをしてください。

npm run build

そして、サーバーサイドレンダリングを試してみてください。

npm start

ただしく表示ができれば終了です。

GAEにホストする

以下のコマンドGAEにデプロイします。

前はVue.jsで静的サイトでしたが、今回はサーバーサイドレンダリングという名前があるようにサーバーが必要になるので少し変更します。

以下のようにapp.yamlには書いています。

runtime: nodejs8
env: standard
service: keisukeyamashita-portfolio-nuxt
instance_class: F2

これでデプロイしています。

gcloud app deploy

特にruntime: nodejs8の場合はnpmを使う必要があり、npm installなど必要ないことがポイントです。

また、これは自分のアプリケーションだからかもしれませんが

Exceeded soft memory limit of 128 MB with 148 MB after servicing 1 requests total. Consider setting a larger instance class in app.yaml.

と出て、メモリ不足のためにinstance_class: F2と指定しています。

確認

以下のように確認することができました。

f:id:bobchan1915:20181011040307g:plain

比較

以下のようにキャッシュなしでのロード時間を計測しました。

  • Nuxt.js: 445ms
  • Vue.js: 859ms