JavaScript
vue.js
nuxt.js

nuxt-client-init-module を使って Nuxt.js にクライアントサイド限定の初期化処理を追加する

はじめに

Nuxt.js(以下 Nuxt) を利用した Universal Application 開発において、 nuxtServerInit を利用する機会はそれなりに多いかと思います。よくあるシチュエーションとしては、マスタデータの API からの取得、 middleware と併用した、ログインユーザーの Credential の取得などですが、特定の目的に限らず、幅広く使われていることでしょう。

しかし、ライブラリが localStorage 依存であったり、そもそも Single Page Application(以下 SPA) モードで起動していることで、クライアント側での init 処理が必要となり、自分で plugins に init を構築したり、コードの随所に process.browser での分岐が生まれて困ったことはないでしょうか?

この記事では、そういったシチュエーションにおける悩みを解消し、 SPA / Universal App に共通して、 nuxtServerInit のクライアント版である nuxtClientInit を Nuxt の機能として追加する nuxt-client-init-module をご紹介します。

nuxt-client-init-module について

nuxt-client-init-module は、ルートストアの nuxtClientInit という名称のついた Action を、クライアントサイドでの起動時に dispatch するモジュールとなっています。

引数として context が渡ってくることなど、基本的な I/F は nuxtServerInit と同じように扱うことができる作りとなっています。この記事では、実際に導入しての簡単な使い方をご紹介します。

Repository: https://github.com/potato4d/nuxt-client-init-module


もしよろしければ、 GitHub で Star が得られると喜びます :pray:
https://github.com/potato4d/nuxt-client-init-module

ユースケース

ざっくり以下のような場合に利用することを想定しています。

  • クレデンシャルが localStorage ベースで保持している場合の初期化処理
    • Cognito SDK などが該当
  • そもそも SPA モードで開発していて nuxtServerInit が使えない場合
  • generate モードでの利用を想定しており、 middleware すらも使えない場合

導入方法

既存の Nuxt プロジェクト(ここでは create-nuxt-app を利用した、 Universal モードでの雛形そのままを想定します)に、 NPM ベースで追加します。

なお、 Nuxt は執筆時点(2018/07)では package-lock.json を持たず、 yarn.lock のみを持っているため、 Yarn が利用可能であれば Yarn での導入をおすすめします。

terminal
$ yarn add nuxt-client-init-module

パッケージの導入が完了すると、次は modules に指定する必要があります。 nuxt.config.js の modules に nuxt-client-init-module を追加します。
最新バージョン(0.1.3)時点では、特にオプションは存在しないため文字列形式で OK です。

nuxt.config.js
{
  // ...
  modules: [
    'nuxt-client-init-module'
  ],
  // ...
}

これで利用する準備ができました。

利用方法

ルートストア(store/index.js) に対して、 nuxtClientInit を作成します。
また、ルートストアが存在しない場合は、作成してください。

今回は、実際の変化をみるために、 state を変化させてみます。

store/index.js
export const state = () => ({
  isCalled: false
})

export const getters = {
  isCalled: (state) => state.isCalled
}

export const mutations = {
  setIsCalled(state) {
    state.isCalled = true
  }
}

export const actions = {
  nuxtClientInit({ commit }, context) {
    commit('setIsCalled')
  }
}

実際にクライアントサイドだけで呼び出されているかの確認

せっかくなので pages/index.vue で実際にクライアントサイドだけで呼び出されているか確認してみましょう。
pages/index.vue を以下のように変更してみます。

pages/index.vue
<template>
  <section class="container">
    isCalled: {{isCalled}}
  </section>
</template>

<script>
import { mapGetters } from 'vuex'

export default {
  computed: {
    ...mapGetters(['isCalled'])
  }
}
</script>

これを実行すると、 SSR では 1 枚目の画像、 SPA では 2 枚目の画像のように表示され、ただしく dispatch されていることがわかります。

Screen Shot 2018-08-04 at 11.21.41.png

Screen Shot 2018-08-04 at 11.21.44.png

nuxt-client-init-module の使い方は以上です。

おわりに

Nuxt によって Vue.js を利用した SPA / Universal App はどんどん作りやすくなっていますが、ブラウザ側の進化に併せていくほど、ブラウザでしかできない都合というものは多くなり、結果的にその対処のためにコードの見通しが悪くなることは少なくないかと思います。

Nuxt 開発でそういったシチュエーションが起こった場合は、 nuxt-client-init-module を活用することで、 Nuxt way に存在する nuxtServerInit と同様のインターフェースのもと、 Nuxt のもつ規約に可能な限り近い形で、ブラウザ向けの対処を集約し、管理しやすくなることでしょう。

資料