LoginSignup
10
8

More than 3 years have passed since last update.

はじめての Nuxt.js

Last updated at Posted at 2019-05-30

Nuxt はずっと前から気にはなってて、でも「今回は SSR しないからいいや」とか「今回は静的サイトジェネレーターで作るからいいや」とか思って避けてきたけど、なんか最近 Nuxt 使うって話を 3 回くらい聞いたので、ちょっと使ってみて構成とか眺めてみようと思う

先に使ってみた感想を述べておくと

  • Nuxt の公式ドキュメントがちゃんと作られているので、これだけ見れば大体解決した
  • 後述してますが TypeScript とか Vuex とかとても簡単に導入できるので感動した
  • webpack 周りが隠蔽されてる
    • 前に VueCLI v2 使ってみた時はけっこうビルド周りがむき出しになってた気がする(VueCLI v3 ではこの辺解決されてるっぽい)
  • Vue で SSR する時に Nuxt を使うという印象を持っていたが、SSR モードか SPA モードか選べたので考えが違った
  • フロントエンドチームが多いチームであれば、Angular などのガッチリ系フレームワークも検討しようと思っていたが、Vue が好きなら Nuxt でもいいかもしれない
    • TypeScript のサポートも昔よりはかなり良くなってて、Vue v3 がリリース(今年リリースされる?)されたら Vue 自体が TypeScript で作り直されるので、さらに Vue + Nuxt + TypeScript 環境が良くなっていくのではないかと思う

今回作業したリポジトリは以下
https://github.com/kurosame/event-search

インストール

create-nuxt-appを使うと良さそう
npx でいけるとかすばらしい

npx create-nuxt-app event-search

プロジェクト名は GitHub のリポジトリ名にした

? Project name event-search
? Project description My groundbreaking Nuxt.js project
? Use a custom server framework express
? Choose features to install Progressive Web App (PWA) Support, Linter / Formatter, Prettier, Axios
? Use a custom UI framework vuetify
? Use a custom test framework jest
? Choose rendering mode Universal
? Author name kurosame
? Choose a package manager yarn

とりあえず全部入れといたが、以下に該当する場合はインストールから除外してもいいと思う
(でも環境を CLI ツールで管理する時点で環境周りをメンテナンスすることは基本的に無いと思うので、全部入れてしまっても問題無いと思う)

  • SSR しない
? Use a custom server framework
None
? Choose rendering mode
SPA
  • モバイル対応しない
? Choose features to install
Progressive Web App (PWA) Supportを選択しない
  • HTTP リクエストしない(ほぼ無いと思うが)
? Choose features to install
Axiosを選択しない

このAxiosとは、axiosを拡張したaxios-moduleのことである

  • デザインは全部自前
? Use a custom UI framework
None

サーバーの Node フレームワークはあまり詳しくないが、特に今使っているのが無ければ Express か Koa あたりを選んでおけば良いと思う
Vuetify は 1 年くらい使ってるが、かなり完成度高い UI フレームワークと思っているので、入れた
Linter や Prettier や Jest などのテストフレームワークは必須で入れておいた方が良い

実行

package.json を開くと、いくつかスクリプトが設定してあるが、とりあえず以下を実行

yarn dev

マシン環境にもよるが、まあまあ時間はかかる
スクリーンショット 2019-05-27 13.13.34.png

2 回目以降はnode_modules/.cacheの下にbabel-loaderのキャッシュを作ってる影響からか、少し速くなっている
スクリーンショット 2019-05-27 13.13.47.png

でもホットリローディングをサポートしているので、1 回起動すれば遅さは気にならない
nodemonを使って./serverディレクトリ内のファイルを監視してサーバの再起動を行っている

画面が表示されたら HTML ソースを見てみると、server-rendered="true"及び HTML の各要素がそのままレンダリングされていると思う
ちゃんと SSR されていることが分かる
ちなみに SPA モードであれば、JS が実行されてから画面がレンダリングされるので、読み込む JS ファイルのみが指定してあり、HTML はスタイルなどを除けばほとんど空である

また、実行すると.nuxtというディレクトリができており、サーバを立ち上げた時に読み込ませるコンテンツが格納されている
./server/index.jsを見てみると、Nuxt のインスタンスを作成して、Promise を返すnuxt.renderを Express に Middleware としてセットしている

しばらく開発で使うのは、dev スクリプトだけで良さそう

以下の他のスクリプトはある程度 Nuxt でアプリケーション作った後にちゃんと使ってみようと思う

  • build
    • minify して.nuxt/dist/に格納
  • start
    • production モードでサーバを立ち上げる
  • generate
    • Vue を静的コンテンツとしてビルドしてdist/に格納してくれる
    • Netlify や Firebase などでそのままホスティングできる(はず)
    • VuePress 使ったことがあれば、それ

使ってみる

ちょっとだけ使ってみてやったことや思ったことを書いてみようと思う

TypeScript を使う

yarn add -D @nuxt/typescript ts-node

tsconfig.jsonがあればnuxtコマンドで勝手にデフォルト値を書いてくれるらしい

touch tsconfig.json
npx nuxt

先程インストールしたts-nodeは Node で TS をトランスパイルせずに実行できるツールでこれが無いとnpx nuxtが動かない

これで導入は完了

後は、拡張子を.jsから.tsに変えて中身を書き換えていく
以下はserver/index.jsを TS に修正している例

server/index.ts
import NuxtConfiguration from '@nuxt/config'
...
// Import and Set Nuxt.js options
const config: NuxtConfiguration = require('../nuxt.config.js')
config.dev = !(process.env.NODE_ENV === 'production')
...

TS 導入前は上記のconfig.devが any 型だったが、導入後はちゃんと型定義の boolean 型を参照している

package.json も忘れずに修正

package.json
"scripts": {
-    "dev": "cross-env NODE_ENV=development nodemon server/index.js --watch server",
+    "dev": "cross-env NODE_ENV=development nodemon server/index.ts --watch server",
-    "start": "cross-env NODE_ENV=production node server/index.js",
+    "start": "cross-env NODE_ENV=production ts-node server/index.ts",
}

Vue コンポーネントの TS 化についてはドキュメントではvue-property-decoratorの使用を薦めている

yarn add vue-property-decorator

layouts/default.vueを例に修正すると

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator'

@Component
class Default extends Vue {
  clipped: boolean = false
  drawer: boolean = false
  fixed: boolean = false
  items: { icon: string; title: string; to: string }[] = [
    {
      icon: 'apps',
      title: 'Welcome',
      to: '/'
    },
    {
      icon: 'bubble_chart',
      title: 'Inspire',
      to: '/inspire'
    }
  ]
  miniVariant: boolean = false
  right: boolean = true
  rightDrawer: boolean = false
  title: string = 'Vuetify.js'
}

export default Default
</script>

ちなみにvue-property-decoratorを使わなくても書ける

<script lang="ts">
import Vue from 'vue'

export default Vue.extend({
  data(): {
    clipped: boolean
    drawer: boolean
    fixed: boolean
    ...
  } {
    return {
      clipped: false,
      drawer: false,
      fixed: false,
      ...
    }
  }
})
</script>

Vuex を使う

Nuxt をインストールした時にルート直下に store ディレクトリができており、既に Vuex 入ってる感があるが、中身は空である
Nuxt は store ディレクトリが存在すれば Vuex をインポートしてくれて、store をルートインスタンスに流してくれるらしい

なので

  • Vuex を手動で入れる必要がない
  • どのコンポーネントからでもthis.$storeで参照できる

また、モジュールモードとクラシックモードの 2 つのモードがあるが、クラシックモードは廃止予定らしい
肥大化してくるとモジュールごとに State が参照できた方が良いので、モジュールモードで良さそう

Nuxt 側でモジュールモードとクラシックモードのどちらで Vuex インスタンスが作られるかの判定は
store/index.(js|ts)が存在して、Store インスタンスをエクスポートしていれば、クラシックモード
それ以外はモジュールモード

モジュールモードでstore/index.(js|ts)という名前で作った場合、ルートモジュールという扱いで Store インスタンスの直下に State が存在する
store/モジュール名.(js|ts)という名前で作った場合、名前付きモジュールとして Store インスタンスの modules プロパティ配下に State が存在する

それぞれの State を参照する場合

  • ルートモジュールはthis.$store.state.counterで参照できる
  • ルート以外はthis.$store.state.todos.listで参照できる
    • 上記はstore/todos.(js|ts)というファイルを作った場合

ちなみに私は上記のルートモジュールと呼ばれてる Store インスタンスの直下に State を置くのは 1 度もやったことない

以下のファイルを store ディレクトリ配下に追加

store/sample.ts
interface IState {
  counter: number
}

export const state: IState = {
  counter: 123
}

簡単すぎる例だが、ファイルを追加するだけで Store に counter が保持できる

そして以下のように参照する

適当なVue
<script>
export default {
  mounted() {
    console.log(this.$store.state.sample.counter) // 123
  }
}
</script>

また、Vuex のヘルパー関数も使える

適当なVue
<script>
import { mapState } from 'vuex'

export default {
  computed: {
    ...mapState({
      sample: 'sample'
    })
  },
  mounted() {
    console.log(this.sample.counter) // 123
  }
}
</script>

Linter とコードフォーマッターを使う

TS を使っているが、TSLint は非推奨となるらしいので、ESLint の TS パーサーを使う
.eslintrc.jsを見る限り ES(JS)だけの Linter で良ければ、Nuxt をインストールした時点で出来てるので、後は rules や extends でルールを追加するだけで良さそう

package.jsonを見るとeslint-plugin-prettiereslint-config-prettierが依存パッケージとしてインストールされており、.eslintrc.jsの extends に prettier が設定してあるので、ESLint と Prettier の競合は気にしなくて良さそう

TS 用にLinterを使う場合の修正箇所は以下

yarn add -D @typescript-eslint/eslint-plugin
.eslintrc.js
parserOptions: {
-    parser: 'babel-eslint'
+    parser: '@typescript-eslint/parser'
},

-  plugins: ['prettier'],
+  plugins: ['@typescript-eslint', 'prettier'],
package.json
"scripts": {
-    "lint": "eslint --ext .js,.vue --ignore-path .gitignore .",
+    "lint": "eslint --ext .ts,.js,.vue --ignore-path .gitignore .",
}

また、ESLint のルールだと以下のような import は誤検知して、「no-unused-vars」を出してしまう

import NuxtConfiguration from '@nuxt/config'

この場合、以下のように ESLint のルールは握りつぶして、@typescript-eslintの方のルールを有効化する必要があるらしい
こちらの記事に書いてありますが、いくつかこのような誤検知するルールがあるらしいので、その都度同様の対応が必要

.eslintrc.js
rules: {
  'no-unused-vars': 'off',
  '@typescript-eslint/no-unused-vars': 'error'
}

Airbnb の ESLint ルールを入れてみる

yarn add -D eslint-config-airbnb-base
.eslintrc.js
extends: [
  'airbnb-base',
...
]

いい感じに動いているが、Vue とか Vuex は Nuxt に直接依存しており、こちらでpackage.jsonに書く必要が無いため、以下のような Lint エラーが出てる

.../event-search/pages/index.vue
  63:1  error  'vuex' should be listed in the project's dependencies. Run 'npm i -S vuex' to add it  import/no-extraneous-dependencies

.../event-search/plugins/vuetify.js
  1:1  error  'vue' should be listed in the project's dependencies. Run 'npm i -S vue' to add it  import/no-extraneous-dependencies

上記は以下のように対応可能

.eslintrc.js
settings: {
  'import/core-modules': ['vue', 'vuex']
},

他にも Nuxt と ESLint で競合するルールはありそう

vue-router を使う

Nuxt ではエントリーポイントとなる JS などで routes を書いてnew VueRouterをやる必要はない
pages ディレクトリ配下にルーターから直接呼ばれるコンポーネントを配置するだけで良い

以下の 2 つを配置

pages/sample/index.vue
<template>
  <span>サンプルインデックス!</span>
</template>
pages/sample/test.vue
<template>
  <span>サンプルテスト!</span>
</template>

それぞれ以下のようにしてルーティングさせる

適当なVue
<nuxt-link to="/sample">Go sample index</nuxt-link>
<nuxt-link to="/sample/test">Go sample test</nuxt-link>

ファイル名がindex.vueであれば、ディレクトリ名が path になり、それ以外のファイル名であれば、ディレクトリ名/ファイル名が path になる
先程の Vuex とルールが似ている

パラメータ付きの動的ルーティングもファイル名もしくはディレクトリ名の先頭にアンダースコアを付けることで実現できるらしい

nuxt-link というコンポーネントは router-link を extends したコンポーネントとなっており、nuxt-link を使わずに今まで通り<router-link>を使って実装することも可能
ただし、nuxt-link を使うと画面表示領域にリンクがあれば、そのリンク先のコンテンツを先読みしてくれるので、遷移が速くなる

試しに、以下のコードを適当な Vue の下の方に配置して、ファーストビューから見えないようにして、スクロールしてリンクを表示させるとindex.vueだけプリフェッチされることが分かる
Chrome DevTool の Network タブで確認すると分かりやすい

適当なVue
<nuxt-link to="/sample">Go sample index</nuxt-link>
<router-link to="/sample/test">Go sample test</router-link>

その他

選択した UI フレームワークでサンプルコードが作られている

私は Vuetify を選んだのだが、割としっかりサンプルコードが Vuetify で作られてて、全部の UI フレームワーク分作られてると思うとちゃんとしてるなって思った

テストのサンプルがほぼ無い

Logo コンポーネントの 1 ケースしかテストのサンプルコードが無い
E2E のサンプルコードは無い

でも最近は Vue のテスト周りのエコシステムが充実してきて、ドキュメントも豊富なので、問題ないと思う

Nuxt のアップデートは簡単にできるのだろうか

Angular みたいなマイグレーション機能はたぶん無いと思うが、Nuxt とか CLI 系のツールを使っているとアップデートが楽にできるとかなりありがたい
簡単にネットで調べた限りは Nuxt を最新にして出たエラーを潰してるっぽい

さいごに

主にツールの導入になってしまい、まだコードはほとんど書いてないのですが、これから ToDo アプリ程度のものは作ってみて、Nuxt を学んでみようかと思います
続きは別記事でまた書こうかなと思います

フロントエンドは全部 1 から作る派なのですが、たまに自動で環境作ってくれる系ツールを使うとそのお手本のような構成に勉強になることがあります
また、今後フロントエンドの環境を作る上で活かせることもあると思うので、試しに使ってみるのもありかなと思いました

10
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
8