Nuxt.js使ってみた

[福岡でVR・アプリ開発をやっている株式会社OneSmallStep](https://1smallstep.jp)の[@_takeshi_24](https://twitter.com/_takeshi_24)です。

公開から2年が経ち、この記事の内容も少し古くなってきました。

Nuxt.jsやFirebaseを使って、サーバーレスでWebアプリケーションを開発する手順について、アドベントカレンダー「[diffeasyCTO西の24(にし)日連続投稿チャレンジ Advent Calendar 2019](https://qiita.com/advent-calendar/2019/diffeasy24)」の中で、「Nuxt.jsとFirebaseとCloudFunctionsでWebアプリ開発」シリーズとして、連載していきます。
Nuxt.jsとFirebaseなどを使ってWebアプリケーション開発にチャレンジしたい方、是非Qiitaアカウントか[twitterをフォロー](https://twitter.com/_takeshi_24)していただき、ツッコミやいいね！お願いします！

# Nuxt.jsとは
[公式ドキュメント](https://ja.nuxtjs.org/guide)に詳しく書いていますが、Vue.js アプリケーションを構築するためのフレームワークです。

サーバーサイドレンダリングするアプリケーションの開発のために必要な設定があらかじめセットされているのが特徴です。

Nuxt.jsには主に以下のパッケージが含まれています。

- Vue2・・・Vue本体です。
- Vue-Router・・・Vueアプリケーションでのルーティングを管理するパッケージです。
- Vuex・・・Vue版のFluxです。
- Vue Server Renderer・・・Vueアプリケーションをサーバーサイドレンダリングするためのパッケージです。
- Vue-Meta・・・Vueアプリケーションのメタ情報を管理するパッケージです。


## サーバーサイドレンダリングとは？
Vue.jsなどでDOMの構築をクライアント側で行うと、サイトにアクセスした際に一瞬画面が表示されない状態になります。
サーバー側で初期状態のDOMレンダリングを完了した状態で返すことで、サイトにアクセスした際にすぐに画面を描画することができます。
これをサーバーサイドレンダリング(SSR)と呼びます。

# Nuxt.js使ってみた
## vue-cliでNuxtのプロジェクトを作成
では、早速使ってみましょう。
今回はスターターテンプレートを使って作成します。
テンプレートを利用するためには、テンプレートプロジェクトを自動生成してくれるツール「vue-cli」を利用します。

vue-cliがインストールされていない場合は、以下のコマンドを実行してvue-cliをインストールしてください。

```
npm install -g vue-cli
```

vue-cliがインストールされたら、以下のコマンドを実行すると、Nuxtのテンプレートプロジェクトが作成されます。
ここでは「sample」というプロジェクト名で作成します。

```
vue init nuxt-community/starter-template sample
```

プロジェクト名やプロジェクトの説明、作成者を入力します。

```
? Project name sample
? Project description Nuxt.js project
? Author takeshi
```

sampleディレクトリに、プロジェクトが作成されます。
以下のコマンドを実行して、必要なライブラリをインストールします。

```
cd sample
npm install
```

開発サーバーを起動します。

```
npm run dev
```

localhost:3000でNuxtのサンプルアプリが起動します。

```shell-session
> sample@1.0.0 dev /Users/takeshi/projects/sample/sample
> nuxt

  nuxt:build App root: /Users/takeshi/projects/sample/sample +0ms
  nuxt:build Generating /Users/takeshi/projects/sample/sample/.nuxt files... +1ms
  nuxt:build Generating files... +49ms
  nuxt:build Generating routes... +7ms
  nuxt:build Building files... +21ms
  nuxt:build Adding webpack middleware... +110ms
Build completed in 6.958s



 DONE  Compiled successfully in 6963ms                                                                        21:33:18


 OPEN  http://localhost:3000
```

<img width="1440" alt="スクリーンショット 2017-12-15 21.33.41.png" src="https://qiita-image-store.s3.amazonaws.com/0/3003/5d77b291-4a44-664f-1fa0-02cf310b488a.png">

とりあえず、画面出ました！

## ページを追加する
Nuxt.jsではVue-Routerでルーティングを管理します。
従来Vue.jsでSPA作るときもVue-Routerを使っていました。
その際、componentを作成して、Vue-RouterでURIとcomponentをマッチングするための定義を記述していました。

Nuxt.jsでは、pagesディレクトリにページ用のcomponentを配置します。
pagesディレクトリに*.vueファイルを配置することで、自動でルーティングの定義を行ってくれます。

例えば、
/pages/users/index.vue

```html:/pages/users/index.vue
<template>
  <section class="container">
    <div>
      <h1>usersのindexページです。</h1>
    </div>
  </section>
</template>

<script>
</script>
```
を作成するだけで、自動的に/usersのURIとマッチングしてくれて、
http://localhost:3000/users
にアクセスすると、作成した/pages/users/index.vueのページが表示されます。
<img width="489" alt="スクリーンショット 2017-12-15 22.32.01.png" src="https://qiita-image-store.s3.amazonaws.com/0/3003/9ee2de3b-4e7d-f927-0f56-f7124d8eca6b.png">


## Vuexを使う
VuexはVue.js版の[Flux](https://github.com/facebook/flux/tree/master/examples/flux-concepts)(状態管理ライブラリ)です。

従来のVue.jsではstore/index.js内で必要なmoduleをすべて読み込んで、ストアインスタンスをexportしていました。
Nuxt.jsでは２つのストアモードが用意されています。
従来同様のクラシックモードと、新たに導入されたモジュールモードです。
モジュールモードでは、storeディレクトリに配置された*.js ファイルがモジュールに変換されます。

サンプルとして、カウントアップするだけの単純なストアを作成します。
storeディレクトリにcounter.jsを作成します。

```/store/counter.js
export const state = () => ({
  count: 0
})

export const mutations = {
  add (state) {
    state.count += 1
  }
}
```
countの状態をもっていて、それをカウントアップするためのaddという処理があります。

先ほどの、/pages/users/index.vueを改造して、このストアの処理を呼び出します。

```html:/pages/users/index.uve
<template>
  <section class="container">
    <div>
      <h1>usersのindexページです。</h1>
      <p>count={{count}}</p>
      <button @click="addCount">カウントアップ</button>
    </div>
  </section>
</template>

<script>
export default {
  computed: {
    count () { return this.$store.state.counter.count }
  },
  methods: {
    addCount (e) {
      this.$store.commit('counter/add')
    }
  }
}
</script>
```

computed処理の中で、counterストアのcountの値を返しています。

```js
this.$store.state.counter.count
```

「カウントアップ」ボタンがクリックされると、methodsのaddCount処理が実行され、counterストアのadd処理がコミットされています。

```js
this.$store.commit('counter/add')
```

修正した画面を開いて、「カウントアップ」ボタンをクリックすると、counterストアのcountの状態が更新されているのがわかります。
<img width="499" alt="スクリーンショット 2017-12-15 23.04.20.png" src="https://qiita-image-store.s3.amazonaws.com/0/3003/de59a4af-b4dd-1c96-22ec-d5cec15e2256.png">

#まとめ
一通り使ってみた感想です。

従来のVue.jsでSPAを作る際は、自由度があるが故に、自分でVue-Routerをもってきたり、Vuexをもってきたり、最初のプロジェクトを作る時にはいろいろ手探りしながらやっていました。

それらがNuxt.jsには最初から含まれているので、フロントエンド開発に慣れていない方でもすごくとっつきやすい印象です。

ルーティングが自動で生成されるのも手軽で良いですね。

[公式ドキュメント](https://ja.nuxtjs.org/)に日本語で詳しく説明がありますので、書くことねーなー、と思いながらこの記事を書き進めました。

目標と期限を宣言し、共有し、繋がる[達成.me](https://tassei.me)もNuxt.jsで作っていますので、ぜひ使ってみてください！