はじめに
この記事はLinkbal Advent Calendar 2019の3日目の記事です。
今年は弊社のサービスのリニュアルでVue.js
初心者の僕はNuxt.js
でプロジェクト最初からリリースまで携わりました。
今回はその経験と学んだことについて紹介したいと思います。
TL;DR
この記事は僕の経験からいくつかメモや学んだことを共通したいものです。
一番伝えたいのはこの注意点です。
項目
事前に知っておくべきこと
Nuxt.jsはVue.jsに基づいたフレームワークなので、もちろん事前にVue.js公式ライブラリ(vue, vuexやvue-router)の基礎知識が必要です。その以外に、Vueと同様、Nuxt.jsもライブサイクルがありますので、
以下のNuxtのサーバーサイドレンダリング(SSR)ライブサイクルを知っておいたほうがいいと思います。
参考: https://medium.com/@onlykiosk/the-complete-nuxt-guide-940751e1a6a5
-
nuxtServerInit
このアクションはVuex
のストアに定義されたら、リクエストが来たら、ルートを問わずにNuxt.jsはそれを呼び出します。これはユーザー認証やサーバーサイドから共通のデータの取得などのために、使われます。
僕の場合はnuxtServerInit
にリクエストのUserAgentでデバイスの判定、ユーザー認証や全ページの共通のデータ取得の処理を入れました。
注意: Nuxtの公式のドキュメントにも書いてありますが、 このnuxtServerInit
アクションはPromiseを返すかasync/awaitを使う必要です。
Note: Asynchronous nuxtServerInit actions must return a Promise or leverage async/await to allow the nuxt server to wait on them.
-
middleware
: 上記の図に書いてあるNuxt.jsのデフォルトのmiddlewareの以外に、カスタマイズのmiddlewareを作り、使うことができます。 -
validate
: ページのパラメーターやクエリーのバリデーション -
asyncData
とfetch
はページレンダリングする(コンポーネントを初期化する)前に、非同期の処理でデータを取得するためのメソッドです。
簡単設定でカスタマイズ、拡張しやすい
Nuxt.jsのメインの設定ファイルnuxt.config.js
で簡単に色々な設定を変更、追加できます。例えば: ページのhead(metaやtitleなど)、環境変数やwebpackなど
Router
Nuxt.js
はpages
ディレクトリにコンポーネントを定義したら、自動的にルーティングを生成してくれるってNuxt.js
の一つ特徴ですが、
実際のアプリケーションを作る時に、リダイレクトのルートや一つページ(コンポーネント)に対する複数ルートなど場合があるでしょうか?
その時に、Routerはカスタマイズが必要になりますね。
Nuxt.js
はvue-router
を使っているので、vue-router
のフォーマットと以下のような簡単設定でカスタマイズできます。
また、全体ページに適用したいmiddleware
はnuxt.config.js
にも定義できます。
// router/customRoutes.js
const CUSTOM_ROUTES = [
// 既存のページの別ルート
{
name: 'custom-event-page',
path: '/custom/',
component: '@/pages/event.vue'
},
// リダイレクトのルート
{
path: '/source_page/:id?',
redirect: {
path: '/destination_page/:id?',
query: { hoge: 'fuga'}
}
}
]
// nuxt.config.js
const customRoutes = require('./router/customRoutes')
module.exports = {
router: {
middleware: 'commonMiddleware', // middleware/commonMiddleware.js
extendRoutes (routes) {
customRoutes.forEach(route => {
routes.push(route)
})
}
},
}
Plugins
Nuxt.js
プラグインはVue.js
アプリケーションがインスタンス化される前に実装されます。
外部のパッケージやVueプラグインをNuxt.js
アプリケーションで使用したい場合、プラグインを使う必要です。
SSRをサポートしないVueプラグインがありますので、その時にクライアント側のみ使うようにする必要です。
// nuxt.config.js
plugins: [
{ src: '~/plugins/no-ssr-plugin', ssr: false },
],
そして、Vueインスタンスに注入したい場合、プラグインを使う必要です。
僕の場合は、ページのasyncData
でエラーの時に、エラーのハンドリングと非同期の処理でデータを取得するため使いました。
// plugins/error-handler.js
export default ({ store }, inject) => {
inject('error', async (errorCode) => {
await errorHandler(store, errorCode)
// エラーコードによりエラーのページにリダイレクトする
error({ statusCode: errorCode })
})
}
// pages/component.vue
asyncData ({ app, store, params }) {
// エラーの場合
await app.$error(errorCode)
}
Modules
Nuxt.js
のコア機能を簡単に拡張し、インテグレーションを加えるNuxt.js
の拡張機能です。
この拡張機能のおかげで、Nuxtコミュニティが広くなって、たくさん便利な機能がシェアされています。
次はいくつか便利なモジュールを紹介したいと思います。
便利なモジュール
Nuxtコミュニティでたくさん便利なモジュールを作ってくれて、シェアされています。
その中に使っていた2つNuxt.js
チームの公式モジュールを紹介したいと思います。
@nuxt/axios
簡単にAxios
とNuxt.js
とを統合するモジュールです。
こちらはNuxt.js
で簡単にAxios
が使えるだけではなく、プラグインでaxios
のinterceptors
を登録できます。
// plugins/axios
export default function ({ $axios, res, store }) {
// リクエストの前処理
$axios.onRequest(config => {
config.withCredentials = true
return config
})
// エラーをハンドリングする
$axios.onError(error => {
const code = parseInt(error.response && error.response.status)
if (code === 500) {
// handle error
}
if (process.env.NODE_ENV !== 'production') console.error(error)
})
// 返ってくる時の処理
$axios.onResponse(response => {
// SSRの時に、responseのヘッダーをチェックして、クライアント側に返すヘッダーをセットする
if (response.headers && response.headers['xxxx'] && process.server) {
res.setHeader('yyyyy', response.headers['yyyy'])
}
})
}
@nuxtjs/google-tag-manager
Nuxt.js
アプリケーションでGoogle Tag Manager(GTM)やGoogle Analytics(GA)が使えるようにするモジュールです。
Vue.js
のSSRのWebアプリケーションですが、初回のアクセスだけサーバーサイドレンダリングされて、その以降通常のVue.js
のアプリケーションになるので、GTMやGAタグのスクリプトをここままに使えないです。
このモジュールを使って、GTMやGA側で以下の設定を適切に変更すれば、問題なく使えます。
GTMやGA側の設定は詳しくないけど、主な変更点はイベントトリガーだと思います。
このモジュールのデフォルトのページビューイベントはnuxtRoute
です。
注意点
この記事で一番伝えたいことはNuxt.js
の使用の時の注意点です。以下の2つあります。
Vuexストアのstate
の値はfunction
でなければならない
Vue.js
アプリケーションでVuexストアのstate
定義はObjectで普通じゃないでしょうか?Vuex
の公式exampleもそうですし。
ただし、Nuxt.js
のSSRでそうするとで不要な共有状態が発生する可能性があります。この問題が発生すれば、サーバーサイド側から取ってくるデータを対象外(ユーザー)に送ってしまう可能性があります。
詳しくはこのNuxt.js
のgithubのissueを見てください。
Regardless of the mode, your state value should always be a function to avoid unwanted shared state on the server side.
↑この注意点はNuxt.js公式ページにも記載してあります。
参考: https://nuxtjs.org/guide/vuex-store/
- ストア定義の例
BAD
// store/state.js
export default {
state_hoge: null,
state_fuga: null
}
// store/index.js
import Vuex from 'vuex'
import state from './state'
import getters from './getters'
import mutations from './mutations'
import * as actions from './actions'
const createStore = () => {
return new Vuex.Store({
state,
getters,
mutations,
actions
})
}
export default createStore
GOOD
// store/state.js
export default () => ({
state_hoge: null,
state_fuga: null
})
// store/index.js
import Vuex from 'vuex'
import createState from './state'
import getters from './getters'
import mutations from './mutations'
import * as actions from './actions'
const createStore = () => {
return new Vuex.Store({
state: createState(),
getters,
mutations,
actions
})
}
export default createStore
asyncDataのデータが再利用されてしまう
詳しくはこのNuxt.js
のgithubのissueを見て下さい。
簡単説明なら、以下のようです。
event
ページのasyncData
関数がこんな感じです。
asyncData ({ $axios }) {
const res = await $axios.$get('/hoge_api_path')
const data = { common: res.data.common }
if (res.data.special_flg) {
data.isSpecial = true
data.specialData = res.data.special_data
}
}
- まず、
events/123
にアクセスしたら、asyncData
の返り値は以下の通りです。
{
common: "hogehoge",
isSpecial: true,
specialData: "hogedata"
}
- 次、
events/456
にアクセスしたら、asyncData
の返値は以下の通りです。
{
common: "fugafuga"
}
Nuxt.js
はasyncData
のデータとVueコンポーネントに定義しているデータにマージして、Vueコンポーネントのデータになる仕組みで、再利用されてしまうので、events/456
ページのデータは以下のようになってしまいます。
{
common: "fugafuga",
isSpecial: true,
specialData: "hogedata"
}
ですから、asyncData
の返り値はifで分けず、絶対に同じフォーマットみたいでなければならない。
GOOD
asyncData ({ $axios }) {
const res = await $axios.$get('/hoge_api_path')
return {
common: res.data.common,
isSpecial: res.data.is_special,
specialData: res.data.is_special ? res.data.special_data : null
}
}
終わり
今回は自分のメモみたいでNuxt.js
でVue.jsのSSRのWebアプリケーションの制作について学んだことや得たことを紹介しました。
ご拝読いただきありがとうございます。
次回はNuxt.js
/Vue.js
のWebアプリケーションのパフォーマンスの最適化について書く予定です。