nuxt.js

Nuxt.js プロジェクトで便利だったモジュール・テクニックなど

Nuxt.js (1.4) を使ったプロダクトをローンチしたので、便利だったモジュール・テクニックなどをまとめます。

Bootstrapを便利に使う

bootstrap 4 を便利に使うために、Bootstrap Vue が存在する。モジュールとしてロードできる仕組みが存在するので、以下の方法で利用が可能です。

yarn add bootstrap
yarn add bootstrap-vue

cssの設定で、CSSをそのまま利用するかを選択できる。 (デフォルトは true)

nuxt.config.js
module.exports = {
  modules: [
    ['bootstrap-vue/nuxt', { css: false }]
  ]
}

今回、bootstrap のテーマを変更したいため、 css: false にして、以下の用に assets を読み込むことにしました。

assets/bootstrap.scss
@import '~bootstrap/scss/bootstrap';

// bootstrap のデザインカスタマイズ

html {
  font-size: 14px;
}
// ...
nuxt.config.js
module.exports = {
  css: ['@/assets/bootstrap.scss']
}

この方法だと、ファイルサイズがやや大きくなってしまうので、個別に必要なコンポーネントとディレクティブを呼ぶ方法を利用したいのですが、残念ながら Nuxt.js 上では動作しない問題があります。(Issue参照のこと)

よく使うコンポーネントと、諸注意

Modal

モーダルダイアログを出したり、消したりする機能を提供する。b-modal コンポーネントの利用が便利。

Form Checkbox

単体のチェックボックス、もしくはグループを管理することができる。b-form-checkbox にはIDを正しく付与しないと、SSRモードでは動作しない(チェックを入れようとしてもチェックが入らない)ので注意。解決策は2つあります。

  1. IDを正しく付与する: b-form-checkbox を利用するときは、id属性を指定する。 (おすすめ)
  2. b-form-checkbox を利用するときは、no-ssr コンポーネントを利用する。
<no-ssr>
  <b-form-checkbox v-model="check">Check</b-form-checkbox>
</no-ssr>

この現象は、b-form-radio コンポーネント利用時にも発生します。

トーストUIの追加

Bootstrap4 には、トーストUIが存在しないため、別のモジュールを利用しました。
@nuxtjs/toast モジュールを利用することで、vue-toasted を簡単に利用することができます。

yarn add @nuxtjs/toast
nuxt.config.js
module.exports = {
  modules: [
    ['@nuxtjs/toast']
  ],
  // toasted のデフォルト挙動設定
  toast: {
    // 右上にtoastを表示
    position: 'top-right',
    // 特に指定しなくても5秒で消えるように設定
    duration: 5000
  }
}

あとは、アプリケーション内では、必要なタイミングで以下を実行するだけで、toastが利用できるようになります。

this.$toast.show('保存しました')

image.png

i18n

Nuxt.js 上で i18n を行うには、vue-i18n を利用するのが便利。翻訳データに YAML を利用するには、少し手を加える必要あり。 nuxt.js で vue-i18n を使えるようにする にて知見をまとめています。

ユニバーサルなCookieの利用

Nuxt.js では、サーバサイドでの動作・クライアントサイドの動作が存在するので、Cookieを利用する場合は要注意でした。仮に、asyncData() にて Cookie を利用する場合は、以下のようにヘンテコなコードを書く必要が出てきてしまいます。

<script>
  export default {
    asyncData ({req}) {
      let hoge = null
      if (process.server) {
        // サーバサイドレンダリングの場合
        hoge = req.cookies.hoge
      } else {
        // クライアントの場合
        // document.cookie を parse するなど
      }
      // ...
    }
  }
</script>

Cookie情報のparseには、vue-cookieの利用などが、便利ですが、サーバサイドとクライアントサイドで条件分岐してしまう面倒さは避けられません。
そこで、cookie-universal-nuxt を利用することで、この煩わしさから開放されます。

yarn add cookie-universal-nuxt
nuxt.config.js
module.exports = {
  modules: [
    ['cookie-universal-nuxt', {parseJSON: false}]
  ]
}
<script>
  export default {
    asyncData({app}) {
      const hoge = app.$cookies.get('hoge')
      // ...
    }
  }
</script>

Google Tag Manager の利用

Google Analytics の設定や、Intercom、各種マーケティングツールを利用については、開発者だけでなくマーケターによる設定などにも対応させるため、Google Tag Manager (GTM) を利用しています。
Nuxt.js で GTM を利用する場合、 @nuxtjs/google-tag-manager が便利です。

yarn add @nuxtjs/google-tag-manager

nuxt.config.js では、モジュール設定で、GTMのIDを指定する。

nuxt.config.js
module.exports = {
  modules: [
    ['@nuxtjs/google-tag-manager', {id: 'GTM-XXXXX'}]
  ]
}

このとき、GTMは NODE_ENV=production の環境でしか動かないことに留意が必要です。

GTM経由で、ページ遷移時に pageview を発生させるために、plugins を作ります。
これは、悩みどころですが、ページタイトル(title)が、routerの afterEach が発生した直後では、変更されていないことがありました。vue-meta による反映タイミングの問題かと思いますが、Google Analytics はページタイトル情報を dt パラメータとして送付するため、違うものを送られるのは解析上困る可能性があります。
ここでは、500msほど後に、changeurl イベントを発生させる挙動にして対処しました。なかなか苦しい対処な気もします。

plugins/gtm.js
export default ({app}) => {
  const DELAY_CHANGE_URL = 500
  let created = false
  const gtm = app.gtm = {
    push (object = {}) {
      if (!window.dataLayer) return
      window.dataLayer.push(object)
    }
  }

  gtm.push({event: 'pageview'})

  app.router.afterEach(() => {
    Vue.nextTick(() => {
      setTimeout(() => {
        if (created) gtm.push({event: 'changeurl'})
        created = true
      }, DELAY_CHANGE_URL)
    })
  })
}
nuxt.config.js
module.exports = {
  plugins: [
    '~/plugins/gtm.js'
  ]
}

GTM のほうでは、changeurl, pageview イベントを受け、Google Analytics の pageview イベントを発生させるように設定します。

image.png

AWS Lambda 上で動作させる

以前紹介した方法で、AWS Lambda上で動作させ、特に問題なく1ヶ月が経過しました。ほぼタダでした。(悲しいような。嬉しいような。)
Nuxt.js の仕組みとして最初の1回目だけLambdaが実行され、ページ遷移時は呼び出されません。実行回数はPVというよりは、訪問回数で推測することができます。