Nuxt.jsとContentfulでブログ作ってみた

これは [Vue.js #3 Advent Calendar 2017 – Qiita](https://qiita.com/advent-calendar/2017/vue3) 21日目の記事です。

![nuxt_ctf.jpg](https://qiita-image-store.s3.amazonaws.com/0/58268/f0db5484-307c-e898-e918-54157ba9f362.jpeg)


* デモ：https://hisako135.github.io/nuxt_blog/
* リポジトリ：https://github.com/hisako135/nuxt_blog

---
Nuxt.js 使ってみたいけど何をつくろうかな〜と思案していたらば、『[SEO、OGP……Vue.js製SPAの「困った」を解決できる「Nuxt.js」が便利だ！](https://www.webprofessional.jp/nuxt-js-universal-vue-js/)』という記事でブログを作っていたので、なんだかちょうどよさそうと思い以下を追加してやってみることにしました。

1. Contentful から記事を取得する
1. トップページに配置するコンポーネント（ components/PostPreview.vue ）をつくる
1. 記事一覧ページと個別記事ページをつくる
1. `nuxt generate` で静的ファイルを生成する
1. GitHub Pages にデプロイ

Nuxt.js ってどんなもんじゃ？という方の参考になれば幸いです :upside_down: 

## 0. とりあえず準備
### 0-1. Contentful にアカウントを作ったり

[Contentful](https://www.contentful.com/)：ドイツのスタートアップ Contentful が開発した、 API に焦点を当てたコンテンツ管理システム（CMS）


以下の記事を参考にして、

* [Getting started](https://github.com/kaydwithers/blog-in-5-minutes-demo/blob/master/docs/GETTING-STARTED.md)
* [Contentful in five minutes](https://www.contentful.com/r/knowledgebase/creating-a-website-in-five-minutes/) 
*  [Integrate Contentful with Nuxt.js](https://www.contentful.com/developers/docs/javascript/tutorials/integrate-contentful-with-vue-and-nuxt/) 

以下までを終わらせる。

* アカウント作成
* スペース作成
* Content Delivery API キー作成
* API キーを .contentful.json に設定

### 0-2. Nuxtのアプリケーションのベースのセットアップをしたり
『[SEO、OGP……Vue.js製SPAの「困った」を解決できる「Nuxt.js」が便利だ！](https://www.webprofessional.jp/nuxt-js-universal-vue-js/)』の以下までをちまちまとやって、アプリケーションの大枠を作る。

1. 「アプリケーションのセットアップと設定」
1. 「ページレイアウトの操作」
1. 「ブログのTOPページを作成する」
1. 「Aboutページの作成」

### 0-3. 必要なパッケージを追加インストールしたりすると

```
npm install --save contentful
npm install --save vue-markdown
npm install --save-dev push-dir
```

### 0-4. こんなディレクトリになっているのではないかな...？

```
-| .nuxt/
-| components/
-| layouts/
-| middleware/(今回は使わない)
-| node_modules/
-| pages/
---| about.vue
---| index.vue
-| plugins/
---| contentful.js
-| static/
-| store/(今回は使わない)
-| .contentful.json
-| .editorconfig
-| .eslintrc.js
-| .gitignore
-| nuxt.config.js
-| package.json
-| README.md
```

## 1. Contentful から記事を取得する

`pages/index.vue` で非同期データを取得します。

```javascript:pages/index.vue
asyncData ({ env }) {
  return client.getEntries({
    'content_type': env.CTF_BLOG_POST_TYPE_ID,
    order: '-fields.publishDate',
    'limit': 3
  }).then(entries => {
    return {
      posts: entries.items
    }
  }).catch(console.error)
}
```

> :hushed: へ〜、 Nuxt.js には asyncData メソッドっていうのがあるのね。
> https://ja.nuxtjs.org/guide/async-data

## 2. トップページに配置するコンポーネント（components/PostPreview.vue）をつくる

`pages/index.vue` で取得した非同期データをコンポーネントに渡します。

```javascript:pages/index.vue
<PostPreview :posts="posts"></PostPreview>
```

```javascript:components/PostPreview.vue
export default {
  props: ['posts']
}
```

> :rolling_eyes: あ、 asyncData メソッドはページコンポーネントだけで、普通(？)のコンポーネントは持たないのね...。
> https://ja.nuxtjs.org/faq/async-data-components/

## 3. 記事一覧ページと個別記事ページをつくる
Nuxt.js は `pages/` ディレクトリ内の Vue ファイルの木構造に沿って、自動的に vue-router の設定を生成してくれます。

記事一覧ページのURLは `https://xxxxxxx/posts/` としたいので、 `pages/` 配下に `posts/` ディレクトリを切り、インデックスのページコンポーネントをつくります。 
個別記事ページはContentfulから取得したslugで動的にルーティングしたいので、 アンダーバーをつけたページコンポーネント `_slug.vue` をつくります。

```
-| pages/
---| posts/
-----| index.vue // 記事一覧ページ
-----| _slug.vue // 個別記事ページ
---| about.vue
---| index.vue
```

```javascript:.nuxt/router.js
routes: [
{
  path: "/",
  component: _5512af1c,
  name: "index"
},
{
  path: "/posts",
  component: _4aea9340,
  name: "posts"
},
{
  path: "/about",
  component: _355ad457,
  name: "about"
},
{
  path: "/posts/:slug",
  component: _4e57d5d0,
  name: "posts-slug"
}
]
```

> :relaxed: 便利...！！
> https://ja.nuxtjs.org/guide/routing/


各ページコンポーネントで、非同期データを取得、取得データの表示などをなんやかやします。
個別記事ページへのリンクはなんとなく名前付きルートにしました。


```javascript:pages/posts/index.vue
<nuxt-link :to="{ name: 'posts-slug', params: { slug: post.fields.slug }}" class="card-footer-item">
  Read More
</nuxt-link>
```

> https://ja.nuxtjs.org/api/components-nuxt-link
> https://router.vuejs.org/ja/api/router-link.html

## 4. `nuxt generate` で静的ファイルを生成する

何もしないと `nuxt generate` で生成されるのはルート`/` だけです。
そのため、 vue-router で `path: "/posts/:slug"` と動的に定義されているルーティングを、 generate プロパティの routes にも設定します。

```javascript:nuxt.config.js
generate: {
  routes () {
    return client.getEntries({
        'content_type': config.CTF_BLOG_POST_TYPE_ID
      }).then((entries) => {
      return [...entries.items.map(entry => `posts/${entry.fields.slug}`)]
    })
  }
},
```

> :neutral_face: 動的なパラメータのときは Promise を返す関数。
> https://ja.nuxtjs.org/api/configuration-generate


## 5. GitHub Pagesにデプロイ

デプロイ用の scripts を追加しましょう。

```javascript:package.json
"scripts": {
  "dev": "nuxt",
  "build": "nuxt build",
  "start": "nuxt start",
  "generate": "nuxt generate",
  "deploy": "push-dir --dir=dist --branch=gh-pages --cleanup" //これ
},
```

> :slight_smile: GitHub Pages へデプロイするには？ https://ja.nuxtjs.org/faq/github-pages

今回はプロジェクトリポジトリにデプロイするので、アプリケーションのベース URL としてリポジトリ名を設定します。

```javascript:nuxt.config.js
router: {
  base: '/nuxt_blog/'
},
```

> :hushed: この設定をしないと
> こうなってほしいのが → https://hisako135.github.io/nuxt_blog/posts
> こうなってしまう → https://hisako135.github.io/posts

そして以下コマンドをポチーーーー :point_down_tone2: 

```shell
npm run generate
npm run deploy
```

## 完成

* デモ：https://hisako135.github.io/nuxt_blog/
* リポジトリ：https://github.com/hisako135/nuxt_blog


Nuxt.js を初めて使ってみて個人的に「あ、そうなんだ...！」となったことを抜粋しているのでだいぶ端折っていたりあまりエレガントでない部分もありますが、もうちょっとこの子を育てていきたいです2018年。


## 参考にさせていただいたもの
* [Nuxt ユニバーサル Vue.js アプリケーション](https://ja.nuxtjs.org/)
* [SEO、OGP……Vue.js製SPAの「困った」を解決できる「Nuxt.js」が便利だ！](https://www.webprofessional.jp/nuxt-js-universal-vue-js/)
* [vue-router](https://router.vuejs.org/ja/)
* [Integrate Contentful with Nuxt.js](https://www.contentful.com/developers/docs/javascript/tutorials/integrate-contentful-with-vue-and-nuxt/) 
* https://github.com/sitepoint-editors/nuxt-ssr-blog/
* https://github.com/kaydwithers/blog-in-5-minutes-demo/