Nuxt.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 を使った非同期処理は、どの部分で実装するか迷うところだが、シンプルにページを遷移する際におこなう、という設計は小規模なアプリケーションであれば比較的見通しはいいと思われる。

Reac-navi の場合のコード

また、全て 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 の場合

コード実例

ルートを作る

/router/index.js
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 を食わせる

main.js
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 によって仕込んだコンポーネントは明示的に読み込まずとも使用できる。

App.js
<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 は初めての人のためにプロジェクトの作り方。

App を作る
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:
自動的に出力される vue-router の設定
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 として参照できる。

/pages/test/_id.vue
<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 と表示されるはずだ。

/pages/_userName/index.vue
<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