ここが変だよ Nuxt.js: No.1 ルーティング
TweetNuxt.js はそれ以外のライブラリ、特に React に慣れている人間には奇妙に感じる点が非常に多い。といっても職業エンジニアの仕事は、与えられた条件下で必要な機能を実装することだ。フレームワークの文句を言うことではない。そこで Nuxt.js の特殊な手法と、これに対応する React, Vue での実装方法を併置することで、Nuxt.js 以外のライブラリの経験者の理解を早める手助けをしたい。まずはルーティング。
react-navi の場合
React の router に関しては react-router のシェアが圧倒的だが、日本で活動する James Nelson さんが作っている Navi というルーターのドキュメントを私が翻訳しているのでこれを例にとる。
James さんとは JavaScript の勉強会でお会いし、ドキュメントを日本語訳して欲しいと頼まれたので、ガイドを今日本語にしている。まずは Getting Started が公式に採択された。
Navi の特徴は多機能な点だ。多機能であればいいということはないし、React の思想からいえば React-router のような最小限のライブラリこそが正当なのかもしれないが、ちょっとしたアプリケーションを作る際に、誰もがだいたい実装することになる機能はある。そういった機能が最初から用意され、かなり使いやすいのが Navi の利点だ。
以下のコードは、ルーティングを設定するファイル内で、必要なデータをフェッチしている。URL Parameter を使った非同期処理は、どの部分で実装するか迷うところだが、シンプルにページを遷移する際におこなう、という設計は小規模なアプリケーションであれば比較的見通しはいいと思われる。
また、全て JavaScript の仕様、記法の範囲であって、react-navi だけのための特殊な考え方は必要ない。
import React from "react";
import Top from "/src/Top";
import Item from "/src/Item";
import { mount, route } from "navi";
const routes = mount({
"/": route(async () => {
return {
view: <Top />
};
}),
"/item/:id": route(async req => {
let id = req.params.id;
// ここで値をフェッチすることもできる
// const item = await getItem(id);
return {
view: <Item id={id} />
};
})
});
export default routes;
vue-router の場合
ルートを作る
import Vue from "vue";
import Router from "vue-router";
import HelloWorld from "../components/HelloWorld";
import Test from "../components/Test";
Vue.use(Router);
const routes = [
{ path: "/", component: HelloWorld },
{ path: "/test", component: Test },
{ path: "/test/:id", component: Test } // Dynamic Route Matching
];
const router = new Router({
routes,
mode: "history" // 初期設定は hash モードになっているので、history に変更
});
export default router;
router とベースとなる Component を食わせる
import Vue from "vue";
import router from "/src/router";
import App from "./App.vue";
Vue.config.productionTip = false;
// router と、ベースとなる App を仕込む
new Vue({
router,
render: h => h(App)
}).$mount("#app");
ベースとなるコンポーネントに <router-view/>
を仕込む
<router-view/>
に routing 結果が出力される。例によって Vue は use によって仕込んだコンポーネントは明示的に読み込まずとも使用できる。
<template>
<div id="app">
<div>
<ul>
<li>
<router-link to="/">Go to Top</router-link>
</li>
<li>
<router-link to="/test">Go to Test</router-link>
</li>
<li>
<router-link to="/test/12">Go to Test/12</router-link>
</li>
</ul>
</div>
<!-- ここにルーティング結果が出る -->
<router-view></router-view>
</div>
</template>
<script>
export default {
name: "App"
};
</script>
<style>
</style>
データのフェッチ
あまり使ったことがないが、一応存在する。 遷移後に実行するなら created で、遷移前なら beforeRouteEnter を使うとのこと。beforeRouteEnter を使ったフェッチは直感的とは言いづらいと感じる。Vue-router の to, from, next 仕様を把握しないと使いづらい。
nuxt の場合
まず nuxt は初めての人のためにプロジェクトの作り方。
yarn create nuxt-app my-app-name # 名前は任意
色々聞かれる
自分は以下のようにした。
- サーバーはデフォルトにしてみた
- PWA, Linter, Prettier, Axios は全て使うにしてみた
- UI は後からいれてもいいと思うので何も使わない
- テストは慣れている Jest で
- Universal に。だいたいみんな server side rendering をしたくて nuxt を使っていると思うので SPA でっはなく Universal で。
yarn dev
普通にこれで立ち上がる。自分の場合最初からリンターがひっかかっていてよくわからなかったが、一旦全部消したところ正常に動いた。
pages dir 以下にファイルを .vue を作ると勝手にパスが作られる
router ライブラリを自分で追加をすることはない。最初から用意されているし、むしろこれ以外の選択肢は基本的にないものと思われる。Gatsby.js や Next.js と同じだ。/pages 以下にファイルを作れば良い。内部で自動的に vue-router を構築しているとのこと。
Dynamic routes
このドキュメントに書いてあるが _
をファイル名につけたものがダイナミックマッチに対応する。これはディレクトリ名でもファイル名でもだ。
pages/
--| _slug/
-----| comments.vue
-----| index.vue
--| users/
-----| _id.vue
--| index.vue
will automatically generate:
router: {
routes: [
{
name: 'index',
path: '/',
component: 'pages/index.vue'
},
{
name: 'users-id',
path: '/users/:id?',
component: 'pages/users/_id.vue'
},
{
name: 'slug',
path: '/:slug',
component: 'pages/_slug/index.vue'
},
{
name: 'slug-comments',
path: '/:slug/comments',
component: 'pages/_slug/comments.vue'
}
]
}
params を使う
_id
としているので、この部分が params として参照できる。
<template>
<div>
<h2>test/:id</h2>
<div>id: {{ id }}</div>
</div>
</template>
<script>
export default {
name: 'Index',
computed: {
id() {
return this.$route.params.id
}
}
}
</script>
<style scoped></style>
ディレクトリ名に _
をつける
/super-yusuke
に移動すると super-yusuke と表示されるはずだ。
<template>
<div>
<h2>/:userName</h2>
<div>userName: {{ userName }}</div>
</div>
</template>
<script>
export default {
name: 'Index',
computed: {
userName() {
return this.$route.params.userName
}
}
}
</script>
<style scoped></style>
nuxt.js のルーティングの特徴まとめ
- pages 以下のディレクトリ/ファイル構造、ディレクトリ名/ファイル名で決まる。
- dynamic matching はディレクトリ名/ファイル名の冒頭に
_
をつける。 - URL params にアクセするするには、
this.$route.params.userName