入る前に
この記事はこの前の記事Nuxt.jsの基本2(Nuxt.jsについて)
の続きになります。
必ず読む必要はないですが、参考までになります。
この記事では、Nuxt.jsの使い方について本当に何も知らない立場から接近してみようと思います。
本当に基本的な内容なので、もうかなり使い慣れてる方にはあんま役にならないと思います。
この記事の目標
Nuxt.jsが基本的に提供してる下記の機能について、軽く説明と、例文を書きながら見について行きたいと思います。
- Nuxt.jsのRouting機能
- Nuxt.jsのStore機能
- Nuxt.jsのLayoutsについて
- Nuxt.jsのMiddlewareについて
- Nuxt.jsのPluginsについて
- Nuxt.jsでの非同期データ取得について
- Nuxt.jsのContextについて
- Nuxt.jsのMetaタグにつにて
Nuxt.js Routing
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'
Vue.use(VueRouter)
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/about',
name: 'About',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
}
]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
export default router
上記のコードはVue.jsのルータ設定ファイルであります。
Vue.jsでは、/src/views
ディレクトリにコンポーネントを作成し、このルータ設定ファイル(/src/router/index.js)
にそのコンポーネントのルータ設定を一度に入力する必要がありました。
しかし、Nuxt.jsではそうする必要はないです!
Nuxt.js Routerの簡単な例を見てみましょう。
ルータには大きく二つの種類があって、「パラメータを受け取るDynamic Route」と「パラメータを受けないBasic Route」であります。
Basic Route
Nuxt.jsは、/pages
フォルダにファイルを作って入れることで自動でRoutingされます。
/pages
に HelloWord.vue
ページを作成してます。
ページ生成後localhost:3000/helloworldに接続してみればHelloWorld.vueページが出力されることを確認できます。
どうすれば可能ですか?
それはページを作成した直ちに.nuxt/router.jsでは自動設定が行われるからです。
import Vue from 'vue'
import Router from 'vue-router'
import { normalizeURL, decode } from 'ufo'
import { interopDefault } from './utils'
import scrollBehavior from './router.scrollBehavior.js'
const _a017cf3c = () => interopDefault(import('../pages/HelloWorld.vue' /* webpackChunkName: "pages/HelloWorld" */))
const _7f5caf40 = () => interopDefault(import('../pages/index.vue' /* webpackChunkName: "pages/index" */))
const emptyFn = () => {}
Vue.use(Router)
export const routerOptions = {
mode: 'history',
base: '/',
linkActiveClass: 'nuxt-link-active',
linkExactActiveClass: 'nuxt-link-exact-active',
scrollBehavior,
routes: [{
path: "/HelloWorld",
component: _a017cf3c,
name: "HelloWorld"
}, {
path: "/",
component: _7f5caf40,
name: "index"
}],
fallback: false
}
export function createRouter (ssrContext, config) {
const base = (config._app && config._app.basePath) || routerOptions.base
const router = new Router({ ...routerOptions, base })
// TODO: remove in Nuxt 3
const originalPush = router.push
router.push = function push (location, onComplete = emptyFn, onAbort) {
return originalPush.call(this, location, onComplete, onAbort)
}
const resolve = router.resolve.bind(router)
router.resolve = (to, current, append) => {
if (typeof to === 'string') {
to = normalizeURL(to)
}
return resolve(to, current, append)
}
return router
}
// .nuxt/router.js
このフィアルを別途触ってもないのに、勝手にRouterとして設定されてます!
Basic Route - ネストされたルーティング
ネストされたルーティングとは、上の図のように、ネストされたコンポーネントのルータ設定を指します。
パスを見て、どのコンポーネントが入れ子になっているかを判断できます。
上の図のような例を実装してみましょう。
<template>
<div>
<div class="container">
Container です。
<nuxt-child />
</div>
</div>
</template>
<script>
export default {
}
</script>
<style scoped>
.container {
width: 300px;
height: 300px;
background-color: pink;
z-index: 0;
}
</style>
// pages/Container.vue
<template>
<div>
<div class="content1">
Content1 です
</div>
</div>
</template>
<script>
export default {
}
</script>
<style scoped>
.content1 {
background-color: lightblue;
margin: 30px;
width: 200px;
height: 200px;
}
</style>
// pages/Container/Container1.vue
<template>
<div>
<div class="content1">
Content2 です。
</div>
</div>
</template>
<script>
export default {
}
</script>
<style scoped>
.content1 {
background-color: lightblue;
margin: 30px;
width: 200px;
height: 200px;
}
</style>
// pages/Container/Container2.vue



ネストされたルーティングも自動的に設定されました。
親コンポーネントと同じ名前のディレクトリを作成した後
そのディレクトリ内にサブコンポーネントを作成しました。
親コンポーネント内に子コンポーネントが入る場所に 定義してくれれば終了。
Nuxt.jsが自動的にルーティングを完了します。
...
routes: [{
path: "/Container",
component: _130fa16f,
name: "Container",
children: [{
path: "Container1",
component: _183fdd80,
name: "Container-Container1"
}, {
path: "Container2",
component: _1823ae7e,
name: "Container-Container2"
}]
}, {
path: "/HelloWorld",
component: _a017cf3c,
name: "HelloWorld"
}, {
path: "/",
component: _7f5caf40,
name: "index"
}],
...
// .nuxt/router.js
ルーティング設定ファイルを見ると、親ルータコンテナに子属性が追加され、子ルータが定義されていることを確認できます。
Dynamic Route
アンダーバー+ファイル名の形式でファイルを生成すると、そのファイル名のパラメータを受け取ります。

<template>
<div>
<div>Editing Product {{ $route.params.product_id }}</div>
</div>
</template>
<script>
export default {
}
</script>
<style scoped>
</style>
// pages/Products/edit/_product_id.vue
product_idというルートのパラメータを受け取り、画面に振りかけるコードです。
自動コンパイル後に.nuxt/router.jsを確認してみると、以下のようなコードが追加されているはずです。
{
path: "/Products/edit/:product_id?",
component: _b6428b84,
name: "Products-edit-product_id"
}
product_idをパラメータとして受け取るという設定が追加されました。

localhost:3000/products/edit/1 でアクセスすれば、パスの params を受けて画面に散らすことを確認できます。
パラメータの検証も可能です。
validate({ params, query, store }) {
return true // if the params are valid
return false // will stop Nuxt.js to render the route and display the error page
}
Nuxt.jsが提供するvalidate()メソッドを使用すると、パラメータの検証が可能です。
validate()は、新しいルータにナビゲートする前に呼び出します。
Nuxt context オブジェクトをargument
として持ちます。 詳細はこちら
<template>
<div>
<div>Editing Product {{ $route.params.product_id }}</div>
</div>
</template>
<script>
export default { // この部分追加↓
validate({ params }) {
// must be a number
return /^\d+$/.test(params.product_id)
}
}
</script>
<style scoped>
</style>
// pages/Products/edit/_product_id.vue

product_idを数字だけ受け取るように検証し、localhost:3000/products/edit/testにアクセスすると上記のように404エラー
を出します。
Nuxt.js Store
Nuxtはpagesディレクトリと同様の構造でストアを構築できます。
Storeは大きくClassic
モードとModule
モードを提供します。
(Store機能が必要ない場合は、Storeフォルダを消去するだけです)
Classic
Classicモードはstore
ディレクトリにindex.js
を必要とします。 (Vuex設定ファイル)このindex.js
はVuexインスタンスを返すexport
関数を実装するだけです。
これにより、通常のVueプロジェクトでVuexを使用するのと同じように、必要に応じてストアを作成できます。
import Vuex from 'vuex'
const createStore = () => {
return new Vuex.Store({
state: ...,
mutations: ...,
actions: ...
})
}
export default createStore
Module
Moduleモードはstore
ディレクトリにindex.js
などを必要とします。(必ずしもindex.js
である必要はありません。目的のモジュールの名前空間で指定)
しかし、モジュールモードでは、このファイルからルートstate/mutations/actions
をexport
するだけです。
export const state = () => ({})
たとえば、 store
ディレクトリ内に product.js
を作成し、以下のように実装すると、
product
という名前空間が生成され、モジュール化ができます。
export const state = () => ({
_id: 0,
title: 'Unknown',
price: 0
})
export const actions = {
load ({ commit }) {
setTimeout(
commit,
1000,
'update',
{ _id: 1, title: 'Product', price: 99.99 }
)
}
}
export const mutations = {
update (state, product) {
Object.assign(state, product)
}
}
// store/product.js
<template>
<div>
<h1>View Product {{ product._id }}</h1>
<p>{{ product.title }}</p>
<p>Price: {{ product.price }}</p>
</div>
</template>
<script>
import { mapState } from 'vuex'
export default {
created () {
this.$store.dispatch('product/load')
},
computed: {
...mapState(['product'])
}
}
</script>
// pages/product/view.vue
productモジュールのid、title、priceを画面に表示させるコードです。
約1秒後にloadアクションを経てstateが更新されることが確認できます。

注意
上記の例では、loadという偽のAPIを実装して使用しました。
ここでの問題は、1秒後にstateが更新されるまで0、Unknownなどの初期化データが見えるということです。
おそらく、実際のAPIでもresponseが完了するまで初期値が表示されます。
私たちはこの問題を解決するためにNuxtが提供するfetchを使うことができます。
詳細は以下で説明します!
Nuxt.js Layouts
Nuxt.jsはnavbar、footer、headerなどのレイアウト機能を提供します。
一般的なVueプロジェクトのApp.vueに似た機能です。
<template>
<div>
<h1>Admin Layout</h1>
<nuxt />
</div>
</template>
<script>
export default {
}
</script>
<style scoped>
</style>
// layouts/admin-layout.vue
<template>
<div>
admin page
</div>
</template>
<script>
export default {
layout: 'admin-layout'
}
</script>
<style scoped>
</style>
// pages/admin.vue
layouts
ディレクトリにadmin-layout.vue
というレイアウトファイルを作成し、
pages
ディレクトリにadmin.vue
というページを作成しました。
admin.vue
にlayout:'admin-layout'
と定義するだけで、そのファイルはadmin-layout.vue
の<nuxt />
要素に配置できます。

重要なことは、レイアウトファイルに必ず<nuxt />
要素を含める必要があるということです。
Nuxt.js Middleware
middleware
はpages
またはlayouts
をレンダリングする前に実行できる機能です。
ミドルウェアが解決されるまで、ユーザーに何も表示しません。
これは、Vuexリポジトリで有効なログインを確認したり、一部のパラメータを検証したりするために使用できます。 (validate()
メソッドの代わりに)
Nuxt.js Plugins
pluginsディレクトリを使用すると、アプリケーションが作成される前にVueプラグインを登録できます。
これにより、Vueインスタンスのアプリ全体で共有し、すべてのコンポーネントからアクセスできます。
たとえば、vue-notificationsプラグインを注入するとします。
- npm i vue-notifications (ここまでVueと同じ)
- pluginsディレクトリにvue-notifications.jsファイルを掘り下げて入力します。
- import Vue from 'vue' import VueNotifications from 'vue-notifications' Vue.use(VueNotifications)
- nuxt.config.js に plugins: ['~/plugins/vue-notifications'] を入力
終わりです。
Nuxt.js 非同期データ取得
Nuxt.jsは、コンポーネントのmounted
Hookからデータを取得するように、クライアント側でデータをロードするための既存のVueパターンをサポートします。
しかし、Universal Appを実装している場合は、サーバー側のレンダリング中にデータをレンダリングできるようにNuxt.js関連のフック(fetch
、asyncData
)を書く必要があります。
asyncData
- コンポーネントデータを設定する前に非同期処理ができるようにします。
- コンポーネントをロードする前に呼び出されます。
-
pages
コンポーネントでのみ使用可能です。 -
Context
オブジェクトを最初の引数として受け取り、それを使用して一部のデータを取得してコンポーネントデータとして返すことができます。 - 戻り値はコンポーネントの
data
とマージされます。 - コンポーネントを初期化する前に実行されるため、メソッド内で
this
を介してコンポーネントインスタンスにアクセスできません。
export default {
async asyncData({ params }) {
const { data } = await axios.get(`https://my-api/posts/${params.id}`);
return { title: data.title };
},
};
fetch
- ページがレンダリングされる前にデータをストアに入れるために使用されます。
- すべてのコンポーネントで利用可能です。
- コンポーネントをロードする前に呼び出されます。
- Contextオブジェクトを最初の引数として受け取り、そのデータをストアに入れることができます。
- return値はPromiseです。
- Promiseを返すと、Nuxtはレンダリング前にPromiseが終了するのを待ちます。
上のStore Moduleの例に続いて説明します。
<注意>のセクションで説明しましたが、あるApiを介してデータをインポートするとき、responseが来るまで初期値がそのまま露出されるという問題がありました。
この問題は、上記のfetch
の特徴の6番「Promise を返すと、Nuxt はレンダリング前に Promise
が終了するのを待つ」で解決できます。
/store/product.js
のload
アクションメソッドがPromiseを返すように変更し、(async/awaitを書いても問題ありません)
/pages/products/view.vue
ファイルをfetch
で修正してみました。
export const state = () => ({
_id: 0,
title: 'Unknown',
price: 0
})
export const actions = {
load ({ commit }) {
return new Promise(resolve => {
setTimeout(() => {
commit('update', { _id: 1, title: 'Product', price: 99.99 })
resolve()
}, 1000)
})
}
}
export const mutations = {
update (state, product) {
Object.assign(state, product)
}
}
// store/product.js
<template>
<div>
<h1>View Product {{ product._id }}</h1>
<p>{{ product.title }}</p>
<p>Price: {{ product.price }}</p>
</div>
</template>
<script>
import { mapState } from 'vuex'
export default {
fetch() {
this.$store.dispatch('product/load')
},
computed: {
...mapState(['product'])
}
}
</script>
<style scoped>
</style>
// pages/product/view.vue

私たちはproduct/load
が実行されるまでレンダリングされませんが、responseが来ると画面にproduct/load
の結果値が浮かぶと予想しました。
しかし、結果として、product/load
アクションメソッドは実行されませんでした。 なぜですか?
理由は fetch
、asyncData
のようなSupercharged
メソッドはVueコンポーネントが生成される前に実行されるため、コンポーネントthis
を指さない。 そのため、上記の例のthis.$store
はundefined
状態です。 コンポーネントが生成された後にはthisの使用が可能です。
それでは、fetch
やasyncData
を使用するときにどのようにStore
にアクセスできますか?
すぐにContext
を使用すればよいです。
Nuxt.js Context
Nuxtは、すべてのメソッドにContextという非常に便利なオブジェクトを含む引数を提供します。
ここにはアプリ全体で参照する必要があるすべてがあります。 つまり、Vueがコンポーネントへの参照を最初に生成するのを待つ必要はありません。
(Contextが何を持っているのかは公式ドキュメントを参照してください。)
上記のfetchの例では、コンテキストを構造化し、ここでStoreを抽出します。
export default {
fetch ({ store }) {
return store.dispatch('product/load')
},
computed: {...}
}
// pages/product/view.vue

初期値が開かず、responseが来てからレンダリングになる様子が確認できました。
Nuxt.js Meta タグ
Vue.jsではSEOのためのメタタグを付けるためにvue-metaなど外部ライブラリを利用しなければならなかったが、Nust.jsでは、別のライブラリを追加せずにメタデータを設定できます。 基本的にvue-metaライブラリが搭載されているからです。
Global 設定
export default {
head: {
title: 'my website title',
meta: [
{ charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
{
hid: 'description',
name: 'description',
content: 'my website description'
}
],
link: [{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }]
}
}
// nuxt.config.js
Nuxt.js設定ファイル(nuxt.config.js)にメタデータを設定すると、アプリケーションのすべての基本タグを定義できます。
SEOの目的で基本的なタイトルと説明タグを追加したり、ビューポートを設定したり、ファビコンを追加するのに非常に便利です。
上記のコードのように設定すると、すべてのページに同じタイトルと説明が表示されます。
Local設定
コンポーネントァイルの<script>
タグ内のメソッドを使用して、ページごとのメタデータを追加することもできます。
<script>
export default {
head: {
title: 'Home page',
meta: [
{
hid: 'description',
name: 'description',
content: 'Home page description'
}
],
}
}
</script>
// methodでも可能
<template>
<h1>{{ title }}</h1>
</template>
<script>
export default {
data() {
return {
title: 'Home page'
}
},
head() {
return {
title: this.title,
meta: [
{
hid: 'description',
name: 'description',
content: 'Home page description'
}
]
}
}
}
</script>
まとめ
いかがでしょうか??
これで少しは、Nuxt.jsがどんな感じで動作するのか、どんなことをすればやりたいことができるのか、などは理解できますかね?
Vue.jsを少しでも使って作業したことがある人にはこんなこと本当に大したことでもないかもしれませんが、自分みたいに、何年間ずっとサーバーや、インフラ環境のみ触った人にはそれも時間かかる部分だと思います。
自分と似てる状況で働いてる方にある程度役に立てたらそれで嬉しいです。
もう、次が最後に記事になります。
次回は、静的検査ツールのESLint と、そのLintとペアーとしてよく使われてる、Prettierについて軽く整理してみようと思います。