本文書ではVue.jsのフレームワークであるNuxt.jsを使ってアプリケーションの開発を効率的に行いたいという人を対象にNuxt.jsのインストールを行った実環境を利用して動作確認を行いながら基本機能の説明を行っています。

Vue.jsのシンプルなコードで記述していますがVue.jsの知識があることを前提に説明を行なっているためVue.jsを知らない人には少し難しい内容になっています。Nuxt.jsを使いこなためにはvue.jsを理解する必要があるのでNuxt.jsを学習する前にVue.jsを先に学習することをお勧めします。
fukidashi

動作確認を行っているNuxt.jsのバージョンはv2.15.7です。現在はNuxt 3のRelease Candidateを利用することができます。最新バージョンのNuxt 3のチュートリアルではないので注意してください。Nuxt 3がリリースされたのでNuxt 3は別記事で公開しています。

本文書を読み終えるとNuxt.jsに含まれる機能の基本的な使用方法を理解することができます。

Vue.jsと同様に人気のあるJavaScriptのフレームワーク(ライブラリ)のReactにはNext.jsというNuxt.jsと似た名前のフレームワークがありNext.jsに関する記事も公開しています。一文字違うだけですが異なるフレームワークなので間違えないようして注意してください。

Nuxt.jsとは

Nuxt.jsはVue.jsのフレームワークです。フレームワークは一般的に使用頻度の高い機能があらかじめ組み込まれて最適化されているため開発を効率的に行うことができます。例えばNuxt.jsにはルーティングのVue Routerやデータ管理のVuex、HTMLのメタタグ管理のvue-metaなどのプラグインを使用するために必要となる設定が事前に行われているため細かな設定を行う必要がなくNuxt.jsをインストールを行うとすぐに利用することができます。

上記のプラグイン(ルーティング、データ管理、メタタグ管理)以外にもNuxt.jsを説明する際に使われる代表的な機能には次のようなものがあります。

  • Sever Side Rendering(サーバサイドレンダリング)
  • ディレクトリ構成
  • Code Splitting
  • SEOへの対策
  • asyncData, fetch Hookでのデータ取得
  • Vuex
  • Plugin, Modules
本文書では実際に動作確認をしながらこれらの機能の理解を深めていきます。
fukidashi

Sever Side Rendering

Vue.jsはJavaScriptで記述されているのでVue.jsで作成されたアプリケーションにブラウザからアクセスした場合にブラウザはサーバからJavaScriptのファイルを受け取りブラウザ側でJavacriptの処理を行うことでページを描写します。そのためHTMLしか理解できないクローラー(インターネット上から情報を収集しデータベースに登録するロボット)からサイトへのアクセスがあった場合、クローラーではJavaScriptの処理を行えないためVue.jsで作成されたページには何も記述されてないサイトと認識されてしまいます。その場合時間をかけて作成し、公開したページもユーザが検索エンジンを利用して見つけることができません。Nuxt.jsではServer Side Rendering(サーバサイドレンダリング)という機能を利用してその問題を回避します。サーバーサイドレンダリングではJavaScriptのレンダリング処理(ブラウザで表示させる内容の処理)をブラウザを含むクライアント側で行うのではなくサーバ側で行いその結果をブラウザに渡します。クローラーが読み込むページにはHTMLで内容が記述されているためクローラーは静的なページと同様に記述されている内容を理解することができます。低速の回線の場合は通常ではJavaScriptファイルをダウンロードするまで取得するデータを含むコンテンツを描写することができませんがサーバサイドレンダリングの場合はサーバサイドでデータの取得を行いデータの含まれたHTMLが戻されるのでページの中身を表示することができます。JavaScriptのデータが重い場合はページが表示された状態でバックグラウンドでデータを取得することになります。サーバサイドレンダリングは最初のアクセス時のみ行われるのでその後のページの遷移ではクライアントからデータを取得してページにそのデータが表示されます。

ディレクトリ構成

ファイル数が少ない小規模のアリケーションの場合は問題になりませんが、アプリケーションの機能が拡張されファイル数が増えてくると作成したファイルをどのディレクトリに保存すべきかわからなくなり混乱してしまうことがあります。Nuxt.jsはベストプラクティスのディレクトリ構成が事前に組まれているのでその構成に従うことで効率よくコンポーネントやファイル管理を行うことができます。

Code Splitting

各ページに必要となるJavaScriptファイルのみ自動でクライアント側のブラウザに渡す機能です。アクセスしたページ内で使用しない余分なコードをブラウザに渡す必要がなくなるためパフォーマンスの向上を図ることができます。

ルーティングの設定

Vue.jsでも Vue Router パッケージを個別にインストールすることでルーティング機能を追加することができますが、新たにルーティングを追加するとルーティングに関する情報を設定ファイル(router.js)に追加していく必要があります。

Nuxt.jsではページ用のディレクトリのpagesにページファイルを保存するだけでそのファイル名に対応するルーティングを自動で設定してくれます(ファイルベースルーティング)。ページを追加する度にVue Routerの設定ファイルを更新する必要がありません。

SEOへの対策

ホームページやブログで利用する場合はアクセス数を増やすためSEO対策を行う必要があります。Nuxt.jsではプラグインのvue-metaが入っているため、SEOへの対策に必要なmetaタグの設定を簡単に行うことができます。

Nuxt.jsを使っているサイト

vue.js、nuxt.jsはさまざまなサイトで利用されています。Vue.jsを作ったEvan Youの以前の講演でルイヴィトンについて触れていました。

ルイビィトンでもNuxt.js
ルイビィトンでもNuxt.js

また最近では発足したばかりのデジタル庁のホームページにも利用されています。Nuxt.jsの動きを確認してみたいのであればデジタル庁のホームページにアクセスしてみるのもいいかもしれません。シンプルなサイトなので本書でNuxt.jsを勉強した後にデジタル庁のサイトのクローンを作成してみるのも勉強になるかもしれません。

デジタル庁のホームページはnuxt利用
デジタル庁のホームページはnuxt利用

Nuxt.jsの基礎

Nuxt.jsのインストール

Nuxt.jsのインストールはnpx create-nuxt-appコマンドで行うことができます。プロジェクトの名前は任意なので好きな名前をつけてください。ここではnuxt-firstと進めます。コマンドを実行するとインストールを進めるかどうか聞かれるので”y”を選択するかEnterボタンを押します。


$ y
Need to install the following packages:
  create-nuxt-app
Ok to proceed? (y) 

インストール中に質問が行われるのでその質問に答える形でインストールを進めていきます。

質問される内容も選択する項目も日々更新されています。本文書では2021年2月のcreate-nuxt-app v3.5.2で実行しています。
fukidashi

npx-nuxt-appでインストールを行うとNuxt.jsを構成するフォルダや設定が行われた状態でインストールされます。もしNuxt.jsをスクラッチからインストールしたい場合はnpm install nuxtでNuxt.jsをインストールすることも可能です。

プロジェクト名の入力

プロジェクト名を聞かれますがインストール時に入力したnuxt-firstがデフォルトの値として設定されるのでそのままEnterを押します。


create-nuxt-app v3.7.1
✨  Generating Nuxt.js project in nuxt-first
? Project name nuxt-first

■言語の選択

JavaScriptかTypeScriptのどちらを利用するか聞かれます。本文書ではJavaScriptを利用するのでそのままEnterを押します。


? Programming language: (Use arrow keys)
❯ JavaScript 
  TypeScript 

パッケージマネージャーの選択

yarnかnpmのパッケージマネジャーを選択します。使用するパッケージマネージャーを選択してください。本文書ではnpmを選択しています。普段利用しているパッケージマネージャーを選択してください。


? Choose the package manager (Use arrow keys)
❯ Yarn 
  Npm 

UIフレームワークの選択

使用するUI(ユーザインターフェイス)のフレームワークを選択します。Bootstrap, Bulma, Vuetify.jsなどを選択することができます。使用予定のUIフレームワークを選択してください。本文書では何も利用しないのでNoneを選択しています。


? Choose UI framework (Use arrow keys)
❯ None 
  Ant Design Vue 
  BalmUI 
  Bootstrap Vue 
  Buefy 
  Chakra UI 
  Element 
  Framevuerk 
  Oruga 
  Tachyons 
  Tailwind CSS 
  Windi CSS 
  Vant 
  View UI 
  Vuetify.js 

モジュールの選択

使用するモジュールを選択することができます。3つはそれぞれ機能の異なるモジュールなのでこれまでとは異なり複数のモジュールが選択可能です。選択しなくても問題ありませんが、本文書ではAxiosのみ選択してインストールを進めます。後ほどインストールすることも可能です。


? Nuxt.js modules: (Press <space> to select, <a> to toggle all, <i> to invert se
lection)
❯◯ Axios - Promise based HTTP client
 ◯ Progressive Web App (PWA)
 ◯ Content - Git-based headless CMS

Lintツールの選択

Javscript, CSSの記述間違いやフォーマットの整形に利用できるLintを選択することができます。 モジュールと同様にすべて選択することも選択しないことも可能です。本文書では何も選択せずに進めます。


? Choose linting tools (Press <space> to select, <a> to toggle all, <i> to inver
t selection)
❯◯ ESLint
 ◯ Prettier
 ◯ Lint staged files
 ◯ StyleLint
 ◯ Commitlint

ESLint?ってという人には下記の文書がおすすめです。

ESLintをインストールしてsinglequoteやextra semicolonに関するエラーが発生した場合は下記のコマンドを実行することで自動修正を行ってくれます。


%  npm run lint:js -- --fix

テストフレームワークの選択

テストフレームワークを選択することができます。本文書では何も選択せずに進めます。


? Choose test framework (Use arrow keys)
❯ None 
  Jest 
  AVA 
  WebdriverIO 
  Nightwatch 

レンダリング方法の選択

Universal(SSR)とSingle Page Appの2つからレンダリング方法を選択することが可能です。SSRはServer Side Rendering(サーバーサイドレンダリング)の略です。Universal(SSR)を選択するとサーバ側でレンダリングを行い、Single Page Appを選択するとブラウザ側でレンダリングを行います。今回はUniversal(SSR)を選択します。


? Choose rendering mode (Use arrow keys)
❯ Universal (SSR) 
  Single Page App

■developementターゲットの選択

デプロイするターゲットを聞かれるますがNode.js hostingを選択します。


? Deployment target: (Use arrow keys)
❯ Server (Node.js hosting) 
  Static (Static/JAMStack hosting) 

developementツールの選択

Visual Studio Codeを利用している場合は、jsconfig.jsonを選択を行ってください。


? Development tools: (Press <space> to select, <a> to toggle all, <i> to invert 
selection)
❯◯ jsconfig.json (Recommended for VS Code if you're not using typescript)
 ◯ Semantic Pull Requests
 ◯ Dependabot (For auto-updating dependencies, GitHub only)

■GitHubのユーザ名の入力

GitHubのユーザ名を聞かれるのでGitHubのユーザ名を入力します。そのまま何も名前を入力せずに進むこともできます。


? What is your GitHub username? 

■バージョンコントールの選択

Gitか何か利用しないか選択することができます。Gitを選択してEnterを押します。


? Version control system: (Use arrow keys)
❯ Git 
  None 

以上でインストールするために必要な質問は終わります。ここからパッケージのインストールが始まります。

インストールが完了すると以下の画面が表示されます。



🎉  Successfully created project nuxt-first

  To get started:

	cd nuxt-first
	npm run dev

  To build & start for production:

	cd nuxt-first
	npm run build
	npm run start

nuxt-firstディレクトリに移動して、npm run devコマンドを実行します。本番環境で利用する場合はnpm run buildを実行してnpm run startを実行します。


$ cd nuxt-first/
$ npm run dev
 ・
 ・
   ╭───────────────────────────────────────╮
   │                                       │
   │   Nuxt @ v2.15.7                      │
   │                                       │
   │   ▸ Environment: development          │
   │   ▸ Rendering:   server-side          │
   │   ▸ Target:      server               │
   │                                       │
   │   Listening: http://localhost:3000/   │
   │                                       │
   ╰───────────────────────────────────────╯
ℹ Preparing project for development                                   
ℹ Initial build may take a while                                      
✔ Builder initialized                                                 
✔ Nuxt files generated                                                
✔ Client
  Compiled successfully in 4.29s

✔ Server
  Compiled successfully in 3.84s

ℹ Waiting for file changes                                            
ℹ Memory usage: 207 MB (RSS: 266 MB)                                  
ℹ Listening on: http://localhost:3000/   

ブラウザでlocalhost:3000にアクセスすると下記の画面が表示されます。

Nuxt.jsのトップページ
Nuxt.jsのトップページ

古いバージョンcreate-nuxt-appではプロジェクトの名前のnuxt-firstの文字とNuxt.jsのロゴが表示された画面が表示されていました。

Nuxt.jsの初期画面
Nuxt.jsの初期画面

Nuxt.jsのディレクトリ

インストールで作成されたNuxt.jsのプロジェクトフォルダがどのようなディレクトリ構成をしてどのようなファイルが保存されているかを確認しましょう。

プロジェクトディレクトリのnuxt_firstをVisual Studio Code(VS Code)で開いています。

Nuxt.jsのディレクトリ構成
Nuxt.jsのディレクトリ構成

.nuxtとnode_modeluesディレクトリを除きデフォルトで実行されるファイルが入っているディレクトリはlayouts, components, pagesとstaticのみです。storeにはmdファイルのみ入っており必須でないこととstoreディレクトリの説明が記述されています。

pages, componetsディレクトリのファイルを確認すると初期ページの構成を理解することができます。ブラウザで確認した初期ページはpagesのindex.vue、componentsのTutorial.vueによって構成されています。各ディレクトリには役割がありその中にファイルを保存することでアプリケーションを構築していきます。

index.vueファイルにTutorialコンポーネントが入っており表示されている内容はTutorial.vueファイルの中に記述されています。


<template>
  <Tutorial/>
</template>

<script>
export default {}
</script>

Nuxt.jsのディレクトリ構成(旧)

旧バージョンでのNuxt.jsのディクレトリ構成です。現在npx create-app-nuxtを実行すると最新版が利用されるのでこちらになることはありません。

インストールしたNuxt.jsがどのようなディレクトリ構成をしてどのようなファイルが保存されているのかを確認していきましょう。

プロジェクトディレクトリのnuxt_firstをVisual Code Editorで開きます。

Nuxtディレクトリ構成
Nuxtディレクトリ構成

.nuxtとnode_modeluesディレクトリを除きデフォルトで実行されるファイルが入っているディレクトリはlayouts, components, pagesとstaticのみです。そのほかのディレクトリはmdファイルのみ入っています。mdファイルには各ディレクトリの使用に関する簡単な説明が記載されています。

日本語訳を加えていますが例えばassetsのREADME.mdファイルには以下が記述されています。


# ASSETS

**This directory is not required, you can delete it if you don't want to use it.**
//このディレクトリは必須ではないので必要でないから削除することができる。

This directory contains your un-compiled assets such as LESS, SASS, or JavaScript.
//このディレクトリはLESS, SASS, JavaSCriptのコンパイルの行われていないアセットを保存します。

More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/assets#webpacked).
//このディレクトリの使用方法についての詳細はリンクのドキュメントを確認してください。

layouts, pages, componentsディレクトリのファイルを確認するとデフォルトページの構成がわかります。ブラウザで確認したデフォルトのページは、layoutsのdefault.vue、pagesのindex.vue、componentsのlogo.vueファイルで構成されています。各ディレクトリには役割がありその中にファイルを保存することでアプリケーションを構築していきます。

トップページとvueファイルの関係
トップページとvueファイルの関係

layoutsディレクトリのファイルには各ページに共通となるナビゲーションバーやサイドバーなどアプリケーション全体のレイアウトに関するコードを記述するファイルを保存します。pagesはページ(URL)毎に異なる内容を記述するファイルを保存します。componentsディレクトリは再利用可能なコンポーネントファイルを保存します。そのためデフォルトでは再利用される可能性の高いLogo.vueファイルが保存されています。

上記の3つ以外のディレクトリにはどのようなファイルを保存していくのか説明しておきます。

  • assets・・・コンパイルが行われていないSASS, LESSやFontなどを保存
  • middleware・・・layoutやページがレンダリングする前に実行されるカスタム関数を保存。ユーザ認証などに使うことができます。
  • plugins・・・Vueインスタンスが起動する前にロードするJavaScriptプラグインを保存
  • static・・・robots.text, favicon、画像ファイルを保存されています。もしimage.pngを保存すると/image.pngでアクセスすることが可能です。
  • store・・・Vuex Storeに関するファイルが保存されています。
  • .nuxt・・・コンパイルを実行すると作成されます。
  • node_modelues・・・next.jsで使われるJavaScriptのパッケージが保存されています。

Nuxt.jsの動作確認

ここからは実際にファイルを追加/更新することでnuxt.jsの使い方を確認していきます。

ここからはnpm run devコマンドを実行した状態でファイルの追加/更新を行ってください。
fukidashi

pagesへのファイルの追加

新しいページを作成するためにpagesディレクトリの中にabout.vueファイルを作成します。


<template>
    <h1>About Page</h1>
</template>

ブラウザでlocalhost:3000/aboutにアクセスすると下記のページが表示されます。

about.vueをpagesに作成後ブラウザでアクセス
about.vueをpagesに作成後ブラウザでアクセス

Nuxt.jsでは追加したabout.vueに対応するルーティングを手動で追加する必要はありません。pagesディレクトリにabout.vueファイルを作成するだけでその名前に対応するURLに対して自動でルーティングが設定されるためです。

追加したaboutについてのルーティング情報は、.nuxtディレクトリのrouter.jsの中で確認することができます。


// 中略
export const routerOptions = {
  mode: 'history',
  base: decodeURI('/'),
  linkActiveClass: 'nuxt-link-active',
  linkExactActiveClass: 'nuxt-link-exact-active',
  scrollBehavior,

  routes: [{
    path: "/about",
    component: _15bd78f0,
    name: "about"
  }, {
    path: "/",
    component: _357553b5,
    name: "index"
  }],

  fallback: false
}

// 中略
nuxt.jsを利用せずvue-cliコマンドでVueプロジェクトを作成した場合は、コンポーネントを作成後にrouter.jsファイルにルーティングを手動で追加する必要があります。
fukidashi

layoutsのdefault.vueの確認

layoutsディレクトリが存在しない場合はdefault.vueファイルを中身を確認します。templateタグにはdivタグの中にNuxtタグが入っているだけです。このNuxtタグの部分にpagesの中にあるページファイル(XXXX.vue)のコンテンツが組み込まれることになります。その証拠にnuxtタグを削除するとブラウザには何も表示されません。


<template>
  <div>
    <Nuxt />
  </div>
</template>
Vue Routerを利用した経験がある人であればNuxtタグがVue routerのrouter-viewと考えてください。
fukidashi

default.vueファイルにはNuxtタグ以外にもコンポーネントやhtmlタグを追加することができます。htmlタグを入力してどのような変化があるか確認してみましょう。


<template>
  <div>
    <nav>ここにナビゲーションバーを入れる</nav>
    <nuxt />
  </div>
</template>

“/”、”/about”のどちらのページアクセスしても上部に追加したテキストが表示されるのが確認できます。layoutsのファイルはpageコンポーネントの外側に存在しすべてのページで共通なコンポーネントとして存在することがわかります。

layoutsのdefalut.vueにテキストを追加
layoutsのdefalut.vueにテキストを追加
layoutsのdefalut.vueにテキストを追加
旧バージョンの場合に表示された画面

NavBarコンポーネント(NavBar.vue)を作成し、各ページへのリンクを作成します。NavBarコンポーネントはcomponetsディレクトリの下に作成します。リンクにはnuxt-linkタグを使います。vue-routerのrouter-linkに対応します。


<template>
  <div>
    <nav>
    <nuxt-link to="/">Home</nuxt-link>
    <nuxt-link to="/about">About</nuxt-link>
    </nav>
  </div>
</template>
nuxt-linkタグをrouter-linkタグに変更しても動作します。nuxt-linkでは後ほど出てくるprefetchの機能があります。ページを開かなくても先にそのページのJavaScriptファイルをFetch(取得)しておく機能です。またaタグに変更しても動作はしますが、リンクをクリックする度にブラウザのリロードが行われページを構成するすべての情報をリロード毎に取得します。通常のHTMLページ間での移動と同じ動作になります。
fukidashi

layoutsのdefault.vueに作成したNavBar.vueをimportします。デフォルトではdefault.vueにはscriptタグがないので追加してください。


<template>
  <div>
    <nav-bar />
    <nuxt />
  </div>
</template>

<script>
import NavBar from '@/components/NavBar.vue';
export default {
  components:{
    NavBar,
  }
}
</script>
//略

各ページの上部にHomeとAboutへのリンクが表示されます。ここまでの処理で簡単に個別ページとそれらのページへのリンクが貼れることがわかります。ボタンを押すとページがリロードすることなくスムーズにページを移動することができます。

NavBarを追加
NavBarを追加

別のレイアウトファイルの設定

default.vueファイルを使ってナビゲーションバーなどのページ共通のレイアウトを作成することができます。複数のページが存在する場合にdefault.vueではない異なるレイアウトを設定したい場合もあるかと思います。その場合にも別のファイルをlayoutsディレクトリに作成することで対応することができます。

layoutsフォルダにoriginal.vueファイルを作成し、以下の内容を記述します。default.vueとはnuxtタグは共通ですがそれ以外は別のコンポーネントやタグを追加することができます。


<template>
  <div>
    <p>オリジナル</p>
    <nuxt />
  </div>
</template>

別のレイアウトが作成できたら、それを利用するページファイルで作成したレイアウトを指定する必要があります。about.vueファイルにlayoutプロパティを追加し、値には作成したレイアウトファイルの名前を指定します。


<template>
  <h1>About Page</h1>
</template>
<script>
export default {
  layout: "original"
};
</script>

ブラウザで確認するとAbout Pageの上にオリジナルが表示された画面が表示されます。

originalのlayoutsファイルを設定
originalのlayoutsファイルを設定

ページ毎に異なるレイアウントが設定できることがわかりました。

オートインポート

Nuxt.jsはコンポーネントのオートインポートの機能を備えているので、Default.vueファイルので中ではcomponentsディレクトリからNavBar.vueファイルのインポートを行っていましたがそれらのコードを削除してもNavBarコンポーネントを利用することができます。


<template>
  <div>
    <nav-bar />
    <nuxt />
  </div>
</template>
//スクリプトタグを削除

コンポーネントのオートインポートの設定はNuxt.jsファイルの設定ファイルであるnuxt.config.jsから行うことができます。

nuxt.config.jsファイルを開きファイル中盤にあるcompoents:trueを見つけてください。componentsの値によってオートインポートの行うかどうかの設定を行うことができます。trueからfalseに変更を行ってください。


  // Auto import components: https://go.nuxtjs.dev/config-components
  components: false,

falseに設定するとindex.jsで利用しているTutorialコンポーネントが見つからないため画面には何も表示されずブラウザのデベロッパーツールのコンソールを確認するとエラーが表示されます。index.vueファイルで利用されているコンポーネントフォルダにあるTutorail.vueファイルもオートインポート機能が利用されているためimportの処理が記述されていませんん。

index.vueではLogoコンポーネントのimportの記述がないのにログが表示されていたので不思議に思っていた人もいるかもしれませんがオートインポートによりindex.vueファイルでのimportの明示なしでLogoコンポーネントを利用しています。
fukidashi

SSRの確認

nuxt.jsの特徴の1つであるSever Side Rendering(SSR)ですが、インストール時にUniversalを設定しているので自動で有効化されています。SSRを使用しないSingle Page Application(SPA)の場合とどのような違いがあるのか確認していきます。

SSRとSPAの設定変更

インストール後もnuxt.config.jsファイルのmodeパラメータを変更するとSSRとSPAの切り替えを行うことができます。パラメータの値はuniversalとspaの2つです。デフォルトではmodeパラメータはnuxt.config.jsには存在せずデフォルト値のuniversalが設定されています。明示的に設定を行いたい場合はmodeパラメータを下記のように追加する必要があります。


export default {
  mode: 'universal', //or spa

universalはSSRを使用、spaはSSRを使用しない設定です。

SSRの動作確認

SSRが実際に動作しているかどうかはdivタグにdata-server-rendered=”true”が設定されているかで確認できます。Chromeブラウザであれば”ページのソースを表示”で確認してください。

ブラウザからアクセスするURLは/aboutでファイルは下記のAbout.vueファイルを利用します。


<template>
  <div>
    <h1>Aboutページ</h1>
    <h2>{{ subtitle }}</h2>
  </div>
</template>

<script>
export default {
  data(){
    return {
      subtitle: 'この文書はNuxt.jsの基本的な使用方法について説明しています。'
    }
  },
}
</script>

SSR設定(universal)の場合はページのソールを確認するとbodyタグの中にdata-server-rendered=”true”を見つけることができます。


<div data-server-rendered="true" id="__nuxt">

SSR設定なし(spa)の場合は、data-server-renderedタグを見つけることはできません。


<div id="__nuxt">

また、SSRの設定の場合はブラウザ上からページのソースを見ると通常のHTML文を確認することができます。SSR設定ありとなしでソースの内容が全く異なることをしっかり理解してください。


<div data-server-rendered="true" id="__nuxt">
  <div id="__layout">
    <div>
      <div>
        <nav>
        <a href="/" class="nuxt-link-active">Home</a> 
        <a href="/about" class="nuxt-link-exact-active nuxt-link-active">About</a>
        </nav>
      </div>
    <div>
    <h1>Aboutページ</h1> 
    <h2>この文書はNuxt.jsの基本的な使用方法について説明しています。</h2>
  </div>
</div>

SSR設定なしの場合は、上記のようなタグはページのソースを確認しても見つけることができません。このことからもSSRではサーバ側で処理が行われてページ情報がブラウザに送信されていることがわかります。

違いをしっかり確認したい人はブラウザからのアクセスではなくcurlコマンドなどを利用してSSR設定ありとSSR設定なしで違いがわかるので実行してみてください。curl http://localhost:3000/aboutで実行可能です。
fukidashi

データの取得

Nuxt.jsではサーバサイド側でデータの取得を行いたい時にasyncData、fetchという2つのHookが準備されています。asyncData, fetchは初回にアクセスした時にのみサーバサイドでデータを取得しますがその後ページを遷移する場合はクライアントサイドでデータ取得を行います

最初にasyncData, fetch Hookを利用して外部リソースからデータの取得方法を確認します。その後mounted Hookとの違いを確認します。動作確認を行うために無料で利用できるJSONPlaceholderを利用します。https://jsonplaceholder.typicode.com/posts/にアクセスすると記事の一覧を取得することができます。

fetch HookとJavaScriptのfetch関数は異なるものなので注意してください。
fukidashi

pagesフォルダの中にpostsフォルダを作成してその中にindex.vueファイルを作成します。ファイルを作成すると自動でルーティングの設定が行われ/posts/にアクセスするとindex.vueファイルの中身が表示されます。

asyncData Hookでのデータ取得

ayncDataを利用して取得したデータはデータプロパティをindex.vueで定義しなくてもasyncDataの中で取得して戻されるデータを利用することができます。asyncDataの引数にはcontextが入っているのでその中から@nuxt/axiosモジュールを利用($axios)してデータの取得を行なっています。contextの中身がどのようなものが含まれているかはドキュメントから確認することができます。contextにはasyncData Hook, fetch Hook, pluginなどからアクセスすることができます。


<template>
  <div>
    <h1>記事一覧</h1>
    <ul>
      <li v-for="post in posts" :key="post.id">
        {{ post.title }}
      </li>
    </ul>
  </div>
</template>

<script>
export default {
  async asyncData({ $axios }) {
    const posts = await $axios.$get(
      "https://jsonplaceholder.typicode.com/posts/"
    );
    return { posts };
  },
};
</script>

/posts/にアクセスするとブラウザには記事一覧が表示されます。

asyncDataで取得したデータ
asyncDataで取得したデータ

データがサーバサイドで取得されていることを確認するためにページのソースを確認します。戻されるデータの中に記事一覧のタイトルが含まれていることが確認できます。

postsで表示される中身のソースを確認
postsで表示される中身のソースを確認

asyncDataではサーバサイド側でデータが取得できることがわかりました。

fetch Hookでのデータ取得

fetch Hookの場合はdataプロパティでpostsを定義しておく必要があります。fetch Hookの処理ではVueのインスタンスにアクセスすることができます。その理由については後ほど説明をします。

fetch HookでもasyncDataと同様にcontextにアクセスすることは可能ですが、contextの$axiosを利用すると以前の動作の異なるfetch Hookとして動作が行われるためthis.postsにデータが保存されることはありません。現行のfetch Hookの中ではcontextは利用せずVueインスタンスからアクセスできるthis.$axiosを利用します。
fukidashi

<template>
  <div>
    <h1>記事一覧</h1>
    <ul>
      <li v-for="post in posts" :key="post.id">
        {{ post.title }}
      </li>
      {{
        posts
      }}
    </ul>
  </div>
</template>

<script>
export default {
  data() {
    return {
      posts: [],
    };
  },
  async fetch() {
    const posts = await this.$axios.$get(
      "https://jsonplaceholder.typicode.com/posts/"
    );
    this.posts = posts;
  },
};
</script>

fetch Hookにはパラメータで設定を変更を行うことができ、fetchOnServerをfalseに設定するとサーバサイドでのデータ取得を停止することができます。その場合はクライアントサイドでデータ取得を行います。


async fetch() {
  const posts = await this.$axios.$get(
    "https://jsonplaceholder.typicode.com/posts/"
  );
  this.posts = posts;
},
fetchOnServer: false,

$fetchStateを使うことでデータ取得のステータスを確認することができます。pendingを確認することで現在データの取得中なのか完了したかを確認することができます。取得中の間は画面には”Fetching post…”が表示されます。


<template>
  <div>
    <h1>記事一覧</h1>
    <p v-if="$fetchState.pending">Fetching posts...</p>
    <ul>
      <li v-for="post in posts" :key="post.id">
        {{ post.title }}
      </li>
    </ul>
  </div>
</template>

mounted Hookでのデータ取得

fetch, asyncData Hookではなくmounted Hookでデータを取得し表示できることも確認しておきます。記事一覧が表示されますがサーバサイドでデータ取得を行っていないためページのソースを見ると記事一覧の情報はありません。


<template>
  <div>
    <h1>記事一覧</h1>
    <ul>
      <li v-for="post in posts" :key="post.id">
        {{ post.title }}
      </li>
      {{
        posts
      }}
    </ul>
  </div>
</template>

<script>
export default {
  data() {
    return {
      posts: [],
    };
  },
  async mounted() {
    const posts = await this.$axios.$get(
      "https://jsonplaceholder.typicode.com/posts/"
    );
    this.posts = posts;
  },
};
</script>

asyncDataとfetch Hookの違い

mounted HookとasyncData, fetch Hookの違いはサーバーサイドとクライアントサイドでデータを取得するという違いがあることはページのソースからも理解できたかと思います。ではasyncDataとfetchにはどのような違いがあるのでしょう。一つ目の大きな違いはasyncDataはページコンポーネントでしか利用することができません。もう一つの違いはfetch HookはVueインスタンスが作成された後に実行されるためにVueのインスタンスにアクセスを行うことができます。そのためfetch Hook内のコードではthisを利用して$axios、postsを利用することができる上、コンポーネントの中でも実行することができます。

下記のNuxt.jsのLifecycle Hooksを見ることでそれぞれのHookがどこで実行されるかがわかります。意味がわからない箇所もあるかと思いますがasyncDataあとにVueのLifecycle Hookが実行されcreatedの後にfetch Hookが実行されていることがわかります。以前のバージョンのfetch HookはasyncDataと同じ場所で実されることもわかりVueのインスタンスにアクセスできないので取得したデータを定義したデータプロパティに保存することができないことも理解することができます。

Nuxt.jsのライフサイクルフックの図
Nuxt.jsのライフサイクルフックの図

Code Splittingの確認

次はnuxt.jsの重要な機能の1つでもあるCode Splittingの動作確認を行います。

これまではコンパイルする際にnpm rum devで実行していましたが、今回はnpm run buildを実行します。buildは開発ではなくプロダクションの際に利用するコマンドです。


 $ npm run build

実行すると各ページに対応するJavaScriptファイルが作成されることがわかります。


Hash: 05e6076e1a50a9aa5a87
Version: webpack 4.41.2
Time: 2042ms
Built at: 2019-12-17 11:04:21
                  Asset       Size  Chunks                         Chunk Names
2814fe35faaaea370267.js  535 bytes       5  [emitted] [immutable]  pages/users/_id
29f5954878d4b3ae7aed.js  327 bytes       2  [emitted] [immutable]  pages/_category/index
58af663e6a323ef3e634.js   3.13 KiB       4  [emitted] [immutable]  pages/index
5a43840719e5e546f15b.js  348 bytes       1  [emitted] [immutable]  pages/_category/_article
5b0826655db24e4715d9.js  447 bytes       3  [emitted] [immutable]  pages/about
b128386ad9c15e74cdcd.js  272 bytes       6  [emitted] [immutable]  pages/users/list
              server.js   28.1 KiB       0  [emitted]              app
   server.manifest.json  435 bytes          [emitted]              
Entrypoint app = server.js

Code Splittingでページ毎に分けられたJavaSciptファイルはそのページに対応するJavaScriptファイルのみブラウザに送信されてくるのかを確認してみましょう。

v2.4.0からはviewportに<nuxt-link>タグが入っている場合は、自動でprefetchされます。そのためルートにアクセスすると<nuxt-link>タグでリンクされているAboutページのJavaSciptファイルも一緒に取得されます。prefetchされないように設定することが可能です。nuxt-linkタグにno-prefetchを設定します。


<nuxt-link to="/about" no-prefetch>About</nuxt-link>

no-prefetchを設定したら、npm run buildとnpm run startを実行します。

JavaScriptファイルの受信についてはブラウザの開発ツールのネットワークを利用します。

まずトップページへのアクセスを行います。開発ツールのNetworkを見ると6つのファイルが送られてきていることが確認できます。一番最後はfavicon.icoです。

トップページをアクセスした時に送られてくるデータ
トップページをアクセスした時に送られてくるデータ

次にトップページにあるリンクからaboutページへアクセスします。アクセスした瞬間にfavicon.icoの下に1つ増えていることが確認できます。

トップからaboutページに移動
トップからaboutページに移動

中身を確認するとAboutページに関するJavaScriptファイルであることがわかります。

送られてきたファイルの中身
送られてきたファイルの中身

これらの結果からCode Splittingでページ毎に分割されたJavaScriptファイルはアクセスするページ毎にそのページに対応するJavaScriptファイルを送信することがわかりました。

ルーティングの設定

先ほどAboutページを作成することでルーティングについて説明を行いましたが、もう少し掘り下げてNuxt.jsの特徴の1つでもあるルーティングの自動設定を確認していきます。

実際にページを作成しながらどのような動作になるのかみていきましょう。

階層ページを作成

Aboutページはpagesディレクトリの直下にAbout.vueファイルを作成してブラウザからURL:/aboutでアクセスすることでAboutページの内容を表示することができました。次はURLが/users/listという階層のある場合のルーティング方法について確認します。

pagesディレクトリの中にusersディレクトリを作成し、その下にlist.vueファイルを作成します。


<template>
    <h1>ユーザ一覧</h1>
</template>

nuxt.jsが自動でルーティングを作成してくれるので、ファイル作成後ブラウザで/users/listにアクセスするとlist.vueのtemplateタグに記述した内容が表示されます。このようにnuxt.jsを利用すると階層的なルーティングも自動で行ってくれます。

usersの下にlist.vueファイルを作成
usersの下にlist.vueファイルを作成

.nuxtディレクトリのrouter.jsファイルを確認すると追加したuserのlist情報が追加されています。


routes: [{
  path: "/about",
  component: _15bd78f0,
  name: "about"
}, {
  path: "/users/list",
  component: _06ba341c,
  name: "users-list"
}, {

/users/にアクセスを行なってページを表示させたい場合はusersディレクトリの中にindex.vueファイルを作成することで実現できます。

さらにpagesディレクトリにusers.vueファイルを作成してNuxtChildコンポーネントを利用することでusers/index.vueとusers/lists.vueファイルの外側にusers.vueファイルに記述した内容を表示させることができます。


<template>
  <div>
    <h1>I am the parent view</h1>
    <NuxtChild />
  </div>
</template>

http://localhost:3000/users/listにアクセスするとusers.vueファイルに記述した内容が表示されることを確認できます。

NuxtChild Componentの確認
NuxtChild Componentの確認

routes.jsファイルに記述されているルーティングの名前users-list(name:”users-list”)を使ってNavBar.vueにリンクを追加します。router-linkタグと同様にnuxt-linkでもnameを使ってリンクを設定することが可能です。


<template>
  <div>
    <nav>
    <nuxt-link to="/">Home</nuxt-link>
    <nuxt-link to="/about">About</nuxt-link>
    <nuxt-link :to="{ name: 'users-list' }">User List</nuxt-link>
    </nav>
  </div>
</template>

ブラウザで確認するとUsersのリンクが追加され、クリックするとユーザ一覧が表示されます。

nameをリンクで設定
nameをリンクで設定

Dynamic Routes(動的ルート)

listではユーザの一覧を表示させるページでしたが、/users/1、/users/2のようにid(パラメータ)を受け取ってユーザ毎のページを作成したい場合はどのように行えばいいのでしょう。

_id.vueをusersディレクトリの下に作成することで実現することができます。

ファイル名は必ず_id.vueである必要はなくusernameをパラメータとして受け取る場合は_username.vueと任意の名前をつけることができます。ファイル名の前には必ず_(アンダーバー)が必要です。
fukidashi

/users/1, /users/2のようにURLに入力したIDは、this.$route.params.idで取得することが可能です。_id.vueファイルでIDを取得し、ブラウザに表示させます。


<template>
    <h1>ユーザID: {{ this.$route.params.id }}</h1>
</template>

/users/38でアクセスするとブラウザ上にはthis.$route.params.idで取得したIDが表示されます。

dynamic routesによるIDの取得
dynamic routesによるIDの取得

通常はaxios等でサーバから取得したデータを使ってusersの一覧を取得しますが今回はusersデータを予め用意してアクセスしてきたIDを使って表示するユーザ情報を変えます。


<template>
    <h1>ユーザID: {{ user.name }}</h1>
</template>

<script>
export default {
    data(){
        return {
            id: this.$route.params.id,
            users: [
                { 
                    id: 1, name: 'John Doe'
                },
                {
                    id: 2, name: 'Kevin Smith',
                },
                {
                    id: 3, name: 'Harry Bosch'
                },
            ],
        }
    },
    computed:{
        user(){
            return this.users.find(user => user.id == this.id);
        } 
    }
}
</script>

computedプロパティでブラウザで入力されたIDとデータプロパティのusersのidをチェックしています。/users/2でアクセスするとusersの中でidを2を持つユーザ名が表示されます。

dataプロパティとcomputedプロパティを利用
dataプロパティとcomputedプロパティを利用

this.$routeの中身

/user/2にアクセスするとthis.$routerparams.idからURLに入力した”2″の値を取得することができることがわかりました。ライフサイクルフックmountedを利用してthis.$routeにはどのような値が入っているのかも確認しておきましょう。


mounted() {
  console.log(this.$route);
}

オブジェクトの中にはparamsだけではなくfullPathなどの情報を保存されていることがわかります。$routeからparams以外の値も取得できることがわかりました。

$routeの中身
$routeの中身

ライフサイクルフックcreated

先ほどはthis.$routeの中身を確認するためにライフサイクルフックのmountedを利用しましたがcreatedを利用して情報を取得してみましょう。


created() {
  console.log(this.$route);
}

mountedフックとは異なり、Nuxt SSRという名前でメッセージが表示されます。SSRはServer Side Renderingの略でサーバ側でも同様にconsole.logの情報を出力することができます。サーバ側とクライアント側で2つの同じ内容が表示されています。

ライフサイクルフックcreated
ライフサイクルフックcreated

npm run devコマンドを実行しているコンソールにもthis.$routeの情報が表示されます。

nuxt.jsはmountedはクライアント側だけで実行され、createdはサーバ側とクライアント側で実行されることがわかりました。

複数のパラメータを受け取り

例えばブログサイトの階層(/{カテゴリー名}/{記事名})のようにURLから2つのパラメータ(カテゴリー名、記事名)を受け取りたい場合の方法を確認します。

pagesディレクトリに_categoryフォルダを作成しその中に_article.vueファイルを作成します。

作成後、/vue/start_nuxt_jsというURLにアクセスすると”vue”はthis.$route.params.category, “start_nuxt_js”はthis.$route.parames.articleで取得することができます。

/vue/にアクセスしてカテゴリーvueの記事一覧のみ取得したい場合は_categoryディレクトリの下にindex.vueファイルを作成することでthis.$route.parames.categoryからカテゴリーvueを取得可能です。


<template>
  <h1>カテゴリー:{{ this.$route.params.category }}</h1>
</template>

<template>
  <h1>{{ this.$route.params.category }}/{{ this.$route.params.article }}</h1>
</template>

prefetchの設定

nuxt-linkタグを利用することでデフォルトでprefetchの機能を利用することができます。prefetchはviewportに入ったリンクのデータをページにアクセス前に事前に取得できる機能です。

nuxt-linkタグでprefetchを停止したい場合はno-prefetchかPropsのprefectchの値をfalseに設定します。


<NuxtLink to="/about" no-prefetch>About page not prefetched</NuxtLink>
<NuxtLink to="/about" :prefetch="false">About page not prefetched</NuxtLink>

グローバルにprefectの停止を行いたい場合はnuxt.config.jsファイルで設定を行います。


  router: {
    prefetchLinks: false
  }

グローバルで停止したprefetchをあるリンクだけ実行したい場合はprefetchを以下のように設定することで可能です。


<NuxtLink to="/about" prefetch>About page not prefetched</NuxtLink>

prefetchの設定方法がわかったけど実際にどのような動作で本当にprefetch機能は動作しているのか知りたいという人もいるかと思います。動作確認は簡単なのでprefetchを理解するためにも自分で一度動作確認を行なってください。

prefectを設定している場合の動きを確認します。ブラウザのデベロッパーツールのネットワークタブを開いてトップページでリロードを行なってください。

ダウンロードされるjavascriptファイルの中にabout.jsファイルが存在することが確認できます。

Nuxt.jsのprefetch
Nuxt.jsのprefetch

次にダウンロードの履歴を削除するためにクリアボタンをクリックしてください。駐車禁止マークのようなボタンです。

ダウンロード履歴を削除
ダウンロード履歴を削除

ネットワークタブの履歴がクリアされるのでクリアした後にaboutページのリンクをクリックして移動してください。移動してもネットワークタブには何も表示されません。prefetchの設定(デフォルト設定)を行っている場合ではトップページにうアクセスするとabout.jsファイルがダウンロードされaboutページに移動しても何もダウンロードが行われないということがわかりました。

次にprefetchの機能を停止します。NavBarのaboutページへのnuxt-linkタグにno-prefetchを追加します。


<nuxt-link to="/about" no-prefetch>About</nuxt-link>

トップページに行きリロードしてデベロッパーツールのネットワークタブを確認してください。先ほどに表示されていたabout.jsファイルが表示されなくなりました。

prefecthを解除後のネットワーク状況
prefecthを解除後のネットワーク状況

次にダウンロードの履歴をクリアしてAboutページに移動してネットワークタブを確認します。今後はabout.jsファイルがAboutページにアクセスしたからダウンロードされることがわかります。

about.jsファイルがダウンロード
about.jsファイルがダウンロード

これらの2つの結果からprefetchを設定している場合はアクセス前に事前にabout.jsファイルをダウンロードすることがわかりました。

ルーティングのカスタマイズ

next.config.jsファイルでrouterプロパティを追加することでカスタマイズすることが可能で例えばベースのURLのセンタ等にappを入れいた場合はbaseを追加することで変更が可能です。


export default {
//略
      router: {
        base: "/app/",
      },
    };

}

アプリケーションのベースのURLがhttp://localhost:3000/へのアクセスからhttp://localhost:3000/appへと変わります。http://localhost:3000/へアクセスするとhttp://localhost:3000/appにリダイレクトとされます。

extendRoutesを利用することでルーティングを手動設定することができます。手動で設定可能なのでpagesディレクトリ以外にもページファイルを保存することができます。例えばadminディレクトリを追加しその下にindex.vueファイルを追加し/adminでアクセスを行なった場合にindex.vueの内容を表示させたい場合は下記のように記述することが実現することができます。


export default {
//略
  router: {
    extendRoutes(routes, resolve) {
      routes.push({
        name: "admin",
        path: "/admin/",
        component: resolve(__dirname, "admin/index.vue"),
      });
    },
  },
}

その他にも設定が可能なのでドキュメントを確認しながら動作を行ってみてください。

SEO対策

Nuxt.jsではvue-metaライブラリを利用しています。metaタグにはさまざまなものがありますが、ここではtitleとdescriptionタグに注目して設定方法を確認していきます。

デフォルトの設定は、nuxt.config.jsファイルに記述されています。グローバル設定なのでnuxt.config.jsファイルで設定した値はすべてのページでデフォルトの設定値となります。ページの要約を記述するdescriptionなどの値は各ページで上書きするため重複するのを避けるため識別子としてhidを設定しています。


  /*
  ** Headers of the page
  */
  head: {
    title: process.env.npm_package_name || '',
    meta: [
      { charset: 'utf-8' },
      { name: 'viewport', content: 'width=device-width, initial-scale=1' },
      { hid: 'description', name: 'description', content: process.env.npm_package_description || '' }
    ],
    link: [
      { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }
    ]
  },

headによるtitleタグ追加

作成したAboutページのタイトルを見るとnuxt.config.jsで設定したグローバル設定のデフォルト値であるnuxt-firstが設定されています。タイトルはブラウザのタブで確認することができます。

デフォルトのタイトル
デフォルトのタイトル

各ページでローカル設定を行うことができます。About.vueファイルのscriptタグの中にheadプロパティを追加して、titleをAbout Pageに設定します。


<template>
    <h1>About Page</h1>
</template>

<script>
export default {
    head(){
        return {
            title: 'About Page',
        }
    }
}
</script>

ブラウザで確認するとタブがAbout Pageになっていることが確認できます。

titleタグ、metaタグはブラウザからページのソースや開発ツールのElements(要素)を見ることで確認することができます。
fukidashi
titleタグがAbout Pageに
titleタグがAbout Pageに

dynamic routesのページにおけるタグの追加

ルーティングで設定したuserの個別ページにtitleタグをつける方法を確認します。computedプロパティを利用することで動的にtitleを変更することが可能です。


export default {
  head(){
    return{
      title: this.user.name,
    }
  },
  data(){
    return {
      id: this.$route.params.id,
      users: [
        { 
            id: 1, name: 'John Doe'
        },
 //中略
      ],
    }
  },
  computed:{
    user(){
      return this.users.find(user => user.id == this.id);
    } 
  }
}

metaタグのdescription設定

metaタグのdescriptionに記述した内容はtitleと同様にSEOでは非常に重要はタグです。descriptionにはページの要約を記述します。

titleと同様にheadプロパティの中に記述します。


<script>
export default {
  head(){
    return {
      title: 'Aboutページ',
      meta:[
        { hid: 'description', name: 'description', content: 'これはAboutページです。'}
      ]
    }
  }
}
</script>

descriptionはブラウザ上には表示されないので、ページのソースを見ることで確認することができます。

ソースからdescriptionを書くにする

nuxt.config.jsでのメタタグ設定

nuxt.config.jsファイルにmetaタグに関する設定を確認することができます。各ページでheadを設定しない場合はこの値が各ページに利用されます。各ページの設定はローカル設定と呼ばれnuxt.config.jsでの設定はグローバル設定と呼ばれます。


export default {
  // Global page headers: https://go.nuxtjs.dev/config-head
  head: {
    title: 'nuxt-first',
    htmlAttrs: {
      lang: 'en'
    },
    meta: [
      { charset: 'utf-8' },
      { name: 'viewport', content: 'width=device-width, initial-scale=1' },
      { hid: 'description', name: 'description', content: '' }
    ],
    link: [
      { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }
    ]
  },

ブログなどのページでは”ページ名 | サイト名”という形式でtitleが設定される場合がよくあります。この場合はグローバル設定とローカル設定を組み合わせることができます。titleTemplateを下記のように利用します。%sに各ページのtitleが入ります。langもenからjaに設定しておきましょう。


  head: {
    title: 'nuxt-first',
    titleTemplate: "%s | Nuxt-First",
    htmlAttrs: {
      lang: 'ja'
    },

ブラウザで確認するとタブに”Aboutページ|Nuxt-First”と表示され期待した表示になっていることが確認できます。

Nuxt.jsのタイトルの設定
Nuxt.jsのタイトルの設定

langについてはソースを見ることで”en”から”ja”に変更されていることが確認できます。

langの変更はソースで確認
langの変更はソースで確認

Single Page Applicationモードの場合のタグ

nuxt.config.jsファイルのmodeパラメータをuniversalからspaに変更するとタグがどのように変化するのか確認してみましょう。


export default {
  mode: 'spa', // or universal

ページのソースコードを見るとデフォルトのThis is a first nuxt.js project”が表示されます。しかし、開発ツールのElementでタグを確認すると”これはAboutページです。”になっています。

理由はSever Side Renderingではmetaタグが設定された状態でクライアント側にページデータが送信されますが、Single Page Applicationの場合はページデータが送られた後にJavaScriptによってmetaタグが更新されるためです。ここからもSever Side Renderingではサーバ側で処理が行われることを示しています。

ここまでの内容で最初に上げたnuxt.jsの機能であるSever Side Rendersing, ディレクトリ構成、Code Splitting, SEOの対策, ルーティングの設定について説明を行うことができました。次は下記の文書でaxiosの使い方を確認しておきましょう。

Vuexの設定

Vuexを利用したい場合はstoreディレクトリが準備されているのでindex.jsファイルを作成するとVuexの利用が可能になります。storeディレクトリにindex.jsファイルを作成した瞬間にリビルドが行われ.nuxtディレクトリの下にstore.jsファイルが作成されます。

Vuexについては下記の文書で公開しているので参考にしてください。

index.jsファイルにstate関数を追加してcountの値を0にします。


export const state = () => ({
  count: 0,
});

設定後index.vueファイルで設定した値にアクセスができるか確認します。


<template>
  <div>count:{{ $store.state.count }}</div>
</template>

<script>>
export default {};
</script>

ブラウザで確認するとcountの値0が表示されます。

Vuexのstoreのcountの値を表示
Vuexのstoreのcountの値を表示

actionsとmutationsをstore/index.jsファイルに追加します。


export const state = () => ({
  count: 0,
});

export const mutations = {
  increment(state) {
    state.count++;
  },
};

export const actions = {
  increment({ commit }) {
    commit("increment");
  },
};

index.vueファイルからstateのcountを更新できるようにボタンを追加し、ボタンをクリックすることでdispatchによりactionsのincrementを実行します。


<template>
  <div>
    <div>count:{{ this.$store.state.count }}</div>
    <button> @click="addCount">Add</button>
  </div>
</template>

<script>>
export default {
  methods: {
    addCount() {
      this.$store.dispatch("increment");
    },
  },
};
</script>

ボタンをクリックするたびにcountの値が1増えることが確認できます。

dispachでincrementを実行しcountの値を増やす
dispatchでincrementを実行しcountの値を増やす

アプリケーションが大きくなってくるとindex.jsファイルのみにstate, mutations, actions, gettersを記述していくと管理が難しくなってきます。

ファイルを分割

state, mutations, actions, gettersを別ファイルにわけることができます。


export default () => ({
  count: 0,
});

export default {
  increment(state) {
    state.count++;
  },
};

export default {
  increment({ commit }) {
    commit("increment");
  },
};

index.jsファイルと同様にVuexを利用することができます。

namespaceを利用

さらにstoreを機能や役割毎に分けて管理したい場合はディレクトリを作成してその下にstate.js, mutaions.js, actions.js, getters.jsファイルを作成することもできます。storeの下にcountディレクトリを作成して先ほど作成したstate.js, mutations.js, actions.jsファイルを移動させてください。移動あとにindex.jsファイルstateとdispatch関数の引数を変更します。


<template>
  <div>
    <div>count:{{ this.$store.state.count.count }}</div>
    <button @click="addCount">Add</button>
  </div>
</template>

<script>
export default {
  methods: {
    addCount() {
      this.$store.dispatch("count/increment");
    },
  },
};
</script>

先ほどまでと同様にAddボタンをクリックするとcountの値が1増えます。

このようにNuxt.jsでもVuexを利用することができます。

Plugins

Nuxt.jsの機能を拡張したい場合にPlugins(プラグイン)を利用することができます。プラグインは自分で関数を作成して機能を追加したい場合、VueのプラグインをNuxt.jsで利用したい場合、外部のパッケージやモジュールを利用したい場合に利用することができます。

Vueのプラグインを利用

2番目の方法が一番簡単でVueで利用することができるプラグインを見つけます。ここではv-tooltipというプラグインを利用します。

npm installコマンドでv-tooltipのインストールを行います。


 % npm install v-tooltip

pluginsフォルダをプロジェクトフォルダの直下に作成してvue-tooltip.jsファイルを作成して以下を記述します。


import Vue from "vue";
import VTooltip from "v-tooltip";
Vue.use(VTooltip);

プラグインはnuxt.config.jsにpluginsという項目があるのでそこに作成したファイルを追加する必要があります。


plugins: ["~/plugins/vue-tooltip.js"],

ここまでの設定が完了したらプラグインで登録したv-tooltipをv-tooltipのドキュメント通りに利用するだけです。


<template>
  <div>
    <h1>Pluginの動作確認</h1>
    <button> v-tooltip="'TOPにメッセージが表示されます'">TOP</button>
  </div>
</template>

ブラウザにTOPボタンが表示されるのでマウスオーバーすると設定した文字列が表示されますがCSSが適用されていないのでずれて表示されます。プラグインの設定が目的なのでCSSはnode_modules¥v-tooltip¥publicのtest.htmlに適用されているものをstyleタグの中に直接設定します。


<template>
  <div>
    <h1>Pluginの動作確認</h1>
    <button> v-tooltip="'TOPにメッセージが表示されます'">TOP</button>
  </div>
</template>

<script>>
export default {};
</script>

<style>>
.tooltip {
  display: block !important;
  padding: 4px;
  z-index: 10000;
}

.tooltip .tooltip-inner {
  background: black;
  color: white;
  border-radius: 16px;
  padding: 5px 10px 4px;
}

.tooltip .tooltip-arrow {
  display: none;
}

.tooltip[aria-hidden="true"] {
  visibility: hidden;
  opacity: 0;
  transition: opacity 0.15s, visibility 0.15s;
}

.tooltip[aria-hidden="false"] {
  visibility: visible;
  opacity: 1;
  transition: opacity 0.15s;
}

.tooltip.danger .tooltip-inner {
  background: red;
}

.tooltip.caution .tooltip-inner {
  background: yellow;
  color: black;
}
</style>

スタイルを設定するとTOPボタンにマウスオーバーするとTooltipが表示されます。

Tooltipの表示
Tooltipの表示

自作のプラグインを設定

プラグインを利用することでVueインスタンス(クライアントサイド)とContext(サーバーサイド)に関数、値をInject(追加)することができます。Nuxt.jsのドキュメントのPluginの説明にあるようにconsole.logで関数の引数に渡して文字列を出力するhello関数をinjectする場合は下記のように行うことができます。

hello.jsファイルをplugisフォルダの中に作成します。hello.jsファイルでは関数をexportします。引数にはcontext, injectが入っているのでinjectを利用してVueインスタンスとContextに追加します。injectの第一引数はkeyで第二引数はvalueです。


export default (context, inject) => {
  const hello = (msg) => console.log(`Hello ${msg}!`);
  inject("hello", hello);
};

プラグインは必ずnuxt.config.jsファイルに追加する必要があります。


plugins: ["~/plugins/vue-tooltip.js", "~/plugins/hello.js"],

injectした関数はクライアントサイドではinjectのkeyを利用してthis.$hello、サーバサイドではcontext.$helloで実行することができます。

index.jsのmountedで設定するとブラウザのデベロッパーツールのコンソールにHello mounted!が表示されます。ライフサイクルフックのcreatedで設定するとnpm run devコマンドを実行しているコンソールとブラウザのデベロッパーツールのコンソールにHello mounted!が表示されます。ライフサイクルフックのbeforeCreateとcreatedのみが両方で実行され、残りのフックはクライアンド側でのみ実行されるためです。


<template>
  <div>
    <h1>Pluginの動作確認</h1>
  </div>
</template>

<script>
export default {
  mounted() {
    this.$hello("mounted");
  },
};
</script>

Contextというものがなにか知りたい人もいるかと思います。その場合はhello.jsにconsole.log(context)を設定してみましょう。


export default (context, inject) => {
  const hello = (msg) => console.log(`Hello ${msg}!`);
  inject("hello", hello);
  console.log("context", context);
};

contextの中身は下記の通りです。$helloがcontextの直下とappの下に追加されていることがわかります。そのためサーバサイドではcontext.$hello, クライアントサイドではthis.$hello(thisはVueインスタンスなのでapp)で実行できることがわかります。

contextの中身の確認
contextの中身の確認

その他

npm run buildコマンド

プロダクション用にビルドするためにはnpm run buildコマンドを実行します。npm run devとは異なりさまざまなファイルが作成されます。javascriptファイルにはアルファベットと数字が含まれるランダムな名前がつけられています。javascriptファイルが同じ名前である場合はブラウザのキャッシュに保存されているファイルは更新されません。毎回異なるファイル名が付けられるためキャッシュの問題を避けることができます。


 % npm run build
//略
Hash: 1566ea56592fdc7c3d81
Version: webpack 4.46.0
Time: 6564ms
Built at: 2021/06/16 10:53:35
                         Asset       Size  Chunks                         Chunk Names
../server/client.manifest.json   9.08 KiB          [emitted]              
                    073471e.js   58.5 KiB       0  [emitted] [immutable]  app
                    1b46a97.js   3.03 KiB    4, 2  [emitted] [immutable]  pages/index
                    5ba75f5.js   1.43 KiB       2  [emitted] [immutable]  components/logo
                    7bdd669.js    209 KiB       1  [emitted] [immutable]  commons/app
                      LICENSES  407 bytes          [emitted]              
                    c6fff5e.js   2.32 KiB       5  [emitted] [immutable]  runtime
                    e9c3b69.js  283 bytes       3  [emitted] [immutable]  pages/about
 + 2 hidden assets
Entrypoint app = c6fff5e.js 7bdd669.js 073471e.js

Hash: d2b584bee43fb7f53532
Version: webpack 4.46.0
Time: 572ms
Built at: 2021/06/16 10:53:35
               Asset       Size  Chunks             Chunk Names
  components/logo.js    5.4 KiB       1  [emitted]  components/logo
      pages/about.js   1.17 KiB       2  [emitted]  pages/about
      pages/index.js   11.5 KiB    3, 1  [emitted]  pages/index
           server.js   88.6 KiB       0  [emitted]  app
server.manifest.json  391 bytes          [emitted]  
 + 4 hidden assets
Entrypoint app = server.js server.js.map
ℹ Ready to run nuxt start 

ビルド後にnpm startコマンドを実行します。npm run devコマンドとは異なりビルドが完了しているのですぐに起動します。npm startコマンドで起動している場合はファイルの更新を行ってもnpm run devコマンドの実行時のようにホットリロードでリアルタイムに更新させることはありません。更新した内容を反映させるためには再度npm run buildを実行してビルドする必要があります。npm startコマンドを再実行しても変更は反映されません。

チルダによるパス設定

dataフォルダを作成してjsonファイルを用意した場合にそのファイルの内容を取得する際にimportを利用します。チルダをつけることでプロジェクトフォルダをルートとしたパスを設定することができます。


import users from '~/data/users.json'

チルダを利用しない場合はimportするファイルからの相対パスを設定する必要があります。/components/user/のしたいにファイルからimportしたい場合。


import users from '../../data/users.json'