Nuxt.js 入門

## はじめに

この記事は、[Vue.js Advent Calendar 2016](http://qiita.com/advent-calendar/2016/vue) 12日目の代打記事です。

## Nuxt.jsとは？

https://nuxtjs.org/

> Few hours after the announcement of Next.js, the idea of creating server-rendered Vue.js applications the same way as Next.js became inevitable: Nuxt.js was born.

[Next.js](https://zeit.co/blog/next)のVue.js版。

特徴としては以下の点が挙げられている。

+ No need to write Webpack/Babel configuration 
+ No need to create a node.js server
+ Writing *.vue files, because it rocks
+ Creating new routes by adding a file in the pages/ directory
+ Accessing the server data inside the routes components easily


> + Webpack/Babelの設定書く必要なし
+ node.jsサーバを準備する必要なし
+ *.vueファイルを書くだけでOK
+ `pages`ディレクトリにファイル追加すると自動ルーティング
+ ルートコンポーネント内のサーバデータに簡単アクセス


## アプリケーション作成

公式ドキュメント: [Installation](https://nuxtjs.org/guide/installation)

まず[vue-cli](https://github.com/vuejs/vue-cli)をインストールしていない場合は、グローバルオプションでインストールしてください。

```bash
$ npm install -g vue-cli
```

プロジェクト名は`[nuxt-example]`としましょう。

```bash
$ vue init nuxt/starter nuxt-example
$ cd nuxt-example
$ npm install # or $ yarn
$ npm run dev
```
http://localhost:3000 にこのような画面が表示されていればOKです。

![スクリーンショット 2016-12-18 3.27.43.png](https://qiita-image-store.s3.amazonaws.com/0/19111/95c78c47-eae5-24d5-08ef-c686e51b1cd3.png "スクリーンショット 2016-12-18 3.27.43.png")

※ `vue-cli`を使わない場合は、[Installation](https://nuxtjs.org/guide/installation)「Starting from scratch」を参照

## ディレクトリ構成

公式ドキュメント: [Directory Structure](https://nuxtjs.org/guide/directory-structure)

### ミニマム構成
```
|-- pages
    |-- index.vue
|-- package.json
```

+ `/pages`フォルダに`index.vue`がありさえすればOK
+ `index.vue`がアプリケーションのルート(`/`)にルーティング
+ [Hello-World](https://nuxtjs.org/examples/)サンプルが参考に

### 推奨構成
```
|--- assets
    |--- global.css
|--- components
    |--- nav.vue
|--- pages
    |--- index.vue
|--- plugins
    |--- filters.js
|--- static
    |--- favicon.ico
|--- store
    |--- index.js
|--- nuxt.config.js
|--- package.json
```

+ `/store`フォルダを作成することで、[Vuex](https://github.com/vuejs/vuex)が利用可能

### エイリアス

| ALIAS | DIRECTORY |
|:-----------:|:------------:|
|~|/|
|~assets|/assets|
|~components|/components|
|~pages|/pages|
|~plugins|/plugins|
|~static|/static|
|~store|/store|

このエイリアスを利用することで、以下のように記述できる。

```html
<img src="~static/img/logo.png" alt="Logo"/>
```


## ルーティング

公式ドキュメント: [Routing](https://nuxtjs.org/guide/routing)

+ `Nuxt.js`は[vue-router](https://github.com/vuejs/vue-router)を利用
+ `pages`フォルダに従って、ルーティングを作成

### 基本

このディレクトリ構成からは、次のようなvue-routerの設定が作られる。


```
|-- pages
    |-- posts
        |-- index.vue
        |-- welcome.vue
    |-- about.vue
    |-- index.vue
```

```
routes: [
  {
    path: '/posts',
    component: '~pages/posts/index.vue'
  },
  {
    path: '/posts/welcome',
    component: '~pages/posts/welcome.vue'
  },  
  {
    path: '/about',
    component: '~pages/about.vue'
  },
  {
    path: '/',
    component: '~pages/index.vue'
  }
]
```

### 応用
`_`プレフィックスを与えることで、`Nuxt.js`のルーティングシステムに検知されないファイルを作成可能。

```
|-- pages
    |-- _about.vue
    |-- index.vue
```

```
routes: [
  {
    path: '/',
    component: '~pages/index.vue'
  }
]
```

## サンプル集

2016/12/18時点では、ドキュメントには以上の項目しか用意されてない。
用意されている[EXAMPLES](https://nuxtjs.org/examples)から、`Nuxt.js`でなにができるかを見ていきましょう。

### SEO HTML Head

https://github.com/nuxt/nuxt.js/tree/master/examples/head-elements

+ [vue-meta](https://github.com/declandewet/vue-meta)を利用して、`header`と`attribute`を更新ができる

```nuxt.config.js
module.exports = {
  head: {
    titleTemplate: '%s - Nuxt.js',
    meta: [
      { charset: 'utf-8' },
      { name: 'viewport', content: 'width=device-width, initial-scale=1' },
      { hid: 'description', name: 'description', content: 'Meta description' }
    ]
  }
}
```

### Cached Components

https://github.com/nuxt/nuxt.js/tree/master/examples/cached-components

+ [lru-cache](https://github.com/isaacs/node-lru-cache)を利用して、オブジェクトのキャッシュができる

```nuxt.config.js
module.exports = {
  cache: true
}
```

### Custom Loading

https://github.com/nuxt/nuxt.js/tree/master/examples/custom-loading

+ デフォルトの`progress bar`をカスタマイズできる
+ 自分で作成した`loading component`をデフォルトの代わりに設定できる

```nuxt.config.js
module.exports = {
  loading: 'components/loading.vue'
}
```

### Custom Routes

https://github.com/nuxt/nuxt.js/tree/master/examples/custom-routes

+ [Routing](https://nuxtjs.org/guide/routing)で見てきた以外に、ルーティングを独自に設定できる

```nuxt.config.js
module.exports = {
  router: {
    routes: [
      { name: 'user', path: '/users/:id(\\d+)', component: 'pages/_user' }
    ]
  },
  build: {
    vendor: ['axios']
  }
}
```

### Global CSS

https://github.com/nuxt/nuxt.js/tree/master/examples/global-css


+ 以下の形式でグローバルCSSを設定できる
	+ 外部モジュール
	+ cssへのコンパイルが必要な外部モジュール
	+ 内部ファイル

```nuxt.config.js
const { resolve } = require('path')

module.exports = {
  css: [
    // Load a node.js module
    'hover.css/css/hover-min.css',
    // node.js module but we specify the lang
    { src: 'bulma', lang: 'sass' },
    // Css file in the project
    // It is important to give an absolute path
    resolve(__dirname, 'css/main.css')
  ]
}
```

### Layouts

https://github.com/nuxt/nuxt.js/tree/master/examples/extend-app

+ `/layouts/app.vue`でグローバルなレイアウトを設定できる

```/layouts/app.vue
<template>
  <nuxt-container>
    <img src="logo.png"/>
    <nuxt/>
  </nuxt-container>
</template>

<style scoped>
img {
  position: fixed;
  top: 20px;
  right: 20px;
  z-index: 1000;
}
</style>
```

### Plugins

https://github.com/nuxt/nuxt.js/tree/master/examples/plugins-vendor

+  `build.vendor`に利用する外部モジュールを指定
+ アプリケーションが起動する前に定義する必要があるものは`plugins`として設定

```nuxt.config.js
module.exports = {
  build: {
    vendor: ['axios', 'mini-toastr', 'vue-notifications']
  },
  plugins: [
    '~plugins/vue-notifications.js'
  ]
}
```

### Routes transitions

https://github.com/nuxt/nuxt.js/tree/master/examples/routes-transitions

+ トランジション効果の設定ができる
+ 詳しくはVue.jsのドキュメント[トランジション効果 - vue.js](https://jp.vuejs.org/v2/guide/transitions.html)を参照

### Async Datas

https://github.com/nuxt/nuxt.js/tree/master/examples/async-data

+ Promise
+ callback

を利用した非同期通信の書き方の紹介


### Vuex Store

https://github.com/nuxt/nuxt.js/tree/master/examples/vuex-store

+ [Vuex](https://github.com/vuejs/vuex)を使ってStateパターンを実装
+ `/store`フォルダ配下にファイルを設置していく

```store/index.js
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

const store = new Vuex.Store({
  state: {
    counter: 0
  },
  mutations: {
    increment (state) {
      state.counter++
    }
  }
})

export default store
```

### Testing

https://github.com/nuxt/nuxt.js/tree/master/examples/with-ava

+ [ava](https://github.com/avajs/ava): テストフレームワーク
+ [jsdom](https://github.com/tmpvar/jsdom): DOMシミュレートライブラリ

を利用してテストが書ける。

```index.test.js
/*
** Test with Ava can be written in ES6 \o/
*/
import test from 'ava'
import jsdom from 'jsdom'
import { createServer } from 'http'
import { resolve } from 'path'

let nuxt = null
let server = null

// Init nuxt.js and create server listening on localhost:4000
test.before('Init Nuxt.js', (t) => {
  const Nuxt = require('../../../')
  const options = {
    rootDir: resolve(__dirname, '..'),
    dev: false
  }
  nuxt = new Nuxt(options)
  return nuxt.build()
  .then(function () {
    server = createServer((req, res) => nuxt.render(req, res))
    server.listen(4000, 'localhost')
  })
})

/*
** Example of testing only the html
*/
test('Route / exits and render HTML', async t => {
  let context = {}
  const { html } = await nuxt.renderRoute('/', context)
  t.true(html.includes('<p class="red-color">Hello world!</p>'))
  t.is(context.nuxt.error, null)
  t.is(context.nuxt.data[0].name, 'world')
})

/*
** Example of testing via dom checking
*/
test('Route / exits and render HTML', async t => {
  const window = await nuxt.renderAndGetWindow(jsdom, 'http://localhost:4000/')
  const element = window.document.querySelector('.red-color')
  t.not(element, null)
  t.is(element.textContent, 'Hello world!')
  t.is(element.className, 'red-color')
  t.is(window.getComputedStyle(element).color, 'red')
})

// Close server and ask nuxt to stop listening to file changes
test.after('Closing server and nuxt.js', t => {
  server.close()
  nuxt.close()
})
```
### Production deployment

+ next.js同様に[now](https://zeit.co/now)の利用を推奨


## あわせて読みたい
+ [Next.js](http://qiita.com/nkzawa/items/1e0e93efd13fb982c8c0)
+ [Vue.js向けSSRフレームワークnuxt.jsを触ってみた](http://qiita.com/YukiYonekura/items/17820d46a0b68a0c2fe8)


## おわりに

Nuxt.jsはまだまだ発展途上のプロダクトですが、Vue.jsで開発する上でのデファクトスタンダードを学べるいい機会になると思いました。

Nuxt.jsをフォローして、ぜひこれから注目していきましょう！

+ https://github.com/nuxt/nuxt.js
+ https://twitter.com/nuxt_js