layoutsディレクトリによるレイアウトの共通化
- 本格的なアプリケーション開発を行っていく前にNuxt.jsの機能について理解しておこう
レイアウト構築機能について
-
共通レイアウト
- Vue.jsによるSPA開発の悩みの種
-
共通レイアウトについての規約がある
- 簡単な切り分け
- 宣言的
- 複雑な管理いらず
レイアウトのルールとdefault.vueの編集
-
default.vue
- 特にレイアウト指定のない場合のフォールバック
<nuxt />
タグのところにpages内のVueコンポーネントが入る
複数のレイアウトの管理
- アプリケーション全体ではなく、カテゴリ別のテンプレートを設けられる
- pagesの中のVueコンポーネントで
layout
を指定する
layouts/single.vue
<template>
<div>
<span>
single layout
</span>
<AppNavigation />
<hr />
<nuxt />
<nuxt-link to="/">
トップに戻る
</nuxt-link>
<hr />
<footer>
footer
</footer>
</div>
</template>
<script>
import AppNavigation from "~/components/AppNavigation.vue";
export default {
components: {
AppNavigation
}
};
</script>
pages/child.vue
<script>
export default {
layout: "single"
};
</script>
いま見ているページのレイアウトはVue.js Devtoolsでも確認可能
レイアウトファイル設計のベストプラクティス
-
default.vueにはアプリケーション全体で使われるレイアウトを
- フォールバック
- いちいち書かなくていいのでミスが減る・管理が楽に
-
特別なものには名前付きのものを
- トップページとか
- 完全に隔離
Nuxt.jsのライフサイクル
- Incoming Request
-
nuxtServerInit
- VuexストアのルートにnuxtServerInitが定義されているときのみ
- 必ず入れておきたいデータの格納
- 簡単な認証
-
middleware
- マスタデータの取得
- 複雑な認証
- グローバルで動かすべき単一の専門的な機能
- validate()
-
asyncData() & fetch()
-
asyncData
- SSRでつかうやつ(前章)
-
fetch
- asyncDataと同様の機能で、Vueコンポーネントのローカルステートに影響がないやつ
- ページ単位のミドルウェア的な
-
- Vue.jsの通常のライフサイクル
middlewareによるグローバルなフックの登録
-
ルーティングに割り込んで様々な処理を行う
- UAに合わせたリダイレクト
- 認証・認可
middlewareを利用した認証の必要なルーティングの実装
-
universal-cookie
- フロントエンド・サーバーサイド両方でCookieを扱えるUniversalなライブラリ
import Cookies from "universal-cookie";
export default {
methods: {
login() {
const cookies = new Cookies();
cookies.set("credential", "true", { maxAge: 90 });
this.$router.push("/");
}
}
};
ミドルウェアの作成とグローバルへの登録
middleware/auth.js
export default () => {
if (process.browser) {
console.log("console.log() on browser");
} else {
console.log("console.log() on SSR server");
}
};
- 関数をデフォルトエクスポートするjs
nuxt.config.js
}
},
+ router: {
+ middleware: ["auth"]
+ }
+ };
- ページ更新すると、サーバーに下記ログが出力される
console.log() on SSR server 08:20:55
ミドルウェアの認証の実装
import Cookies from "universal-cookie";
export default ({ req, route, redirect }) => {
console.log(route.path);
if (["/"].includes(route.path)) {
return;
}
const cookies = req ? new Cookies(req.headers.cookie) : new Cookies();
const credential = cookies.get("credential");
// 認証済でログインページにアクセスしたらrootにリダイレクト
if (credential && route.path === "/login") {
return redirect("/");
}
// 未認証でroot・ログインページ以外にアクセスしたらloginにリダイレクト
if (!credential && route.path !== "/login") {
return redirect("/login");
}
};
- /authed-routeにアクセス
- /loginにリダイレクトされる
- ログインボタン押下
- /に遷移
- /loginにアクセス
- /にリダイレクトされる
- /authed-routeにアクセス
- 「認証が必要なページ」が表示される
ミドルウェアの魅力と注意点について
-
できること
-
アプリケーション全域に記述したい処理を簡単に書ける
- Vuexストアの読み出し
- Responseオブジェクトの改変
-
-
ブートストラップロジックとアプリケーション本体のはざま
- 両方の情報にアクセスでき、柔軟な機能追加が可能
-
半面、責務をまたぐコードが生まれるので注意
- むやみに増やすとアーキテクチャが密結合になり見通しが悪くなる
プラグインによるVue.jsプラグイン資産の有効活用
- pluginsディレクトリが用意されている
-
自前でutilsディレクトリとかを掘るのに比べての利点
-
Nuxt.jsの規約に則って開発できる
- CoCなフレームワークなので、規約に則ってこそ生産性が上がる
-
SSR時に呼び出すか否かをオプションで切り替え可能
- SSR時はwindowやdocumentオブジェクトがないため使えないものがある
- process.browserで分岐するよりもキレイ
-
プラグインの実装と登録
plugins/logger.js
export default ({ app }) => {
app.router.beforeEach((to, from, next) => {
console.log(`[ROUTER] move to '${to.fullPath}'`);
next();
});
};
nuxt.config.js
router: {
middleware: ["auth"]
},
+ plugins: ["~/plugins/logger"]
+ // 同じ意味
+ // plugins: [
+ // {
+ // src: "~/plugins/logger",
+ // ssr: true
+ // }
+ // ]
-
ssr
オプション- デフォルト
true
- windowやdocument等、browserでないと存在しないものに依存している
= サーバサイドで動かすとエラーになる場合はssr: false
とする - SPAモードではサーバサイドで動かさないので、明示的に
false
と指定する必要はない
- デフォルト
- 今回はページ遷移でログを仕込んだ
- 実際の開発では、Google Analyticsやmixpanel等を仕込むことが多くある
- 既存のVue.jsプロジェクトのプラグイン資産を活用したい場合などに有用
Vuexのモジュールモードを活用したオートローディング
-
前章ではクラシックモードでVuexを利用した
- deprecated
- Nuxt.js ver3から使えなくなる
-
規約に沿ってファイル名をつけ記述し、適切にexportを行うだけで、名前空間付きVuexモジュールができあがる
-
Vue.jsあるあるの解消
- モジュールの読み込み漏れ
- namespacedの付け忘れ
-
モジュールモードの仕組みについて
index.js
export const state = () => ({
isLoading: false
});
export const mutations = {
setIsLoading(state, isLoading) {
state.isLoading = isLoading;
}
};
users.js
export const state = () => ({
list: []
});
export const mutations = {
addUser(state, user) {
state.list.push(user);
}
};
export const actions = {
addUser({ commit }, { user }) {
commit("addUser", user);
}
};
下記Vuexストアと等価
new Vuex.Store({
state: {
isLoading: false
},
mutations: {
setIsLoading(state, isLoading) {
state.isLoading = isLoading;
}
},
modules: {
users: {
state: {
list: []
},
mutations: {
addUser(state, user) {
state.list.push(user);
}
},
actions: {
addUser({ commit }, { user }) {
commit("addUser", user);
}
}
}
}
});