Nuxt.jsとcontentfulとNetlifyで作るWebサイト

この記事は、TechTrain Advent Calendar 2019の15日目の記事です。

こんにちは、@kohey18 です:bow::bow:

普段はBandという会社の代表しながら、様々なクライアント(医療系/EC系/CtoC系etc)の「開発支援」や「組織マネージメント支援」を行っています(たまに「デザイン支援」もする)
副業したい！業務委託先探している！みたいなエンジニアは連絡くれたらお茶でもしましょー！！

**自分の会社のコーポレートサイトもまともに作っていないのに**、
クライアントの `コーポレートサイト` や `サービスサイト(≒LP)` の設計/開発/デザイン/コーディング/運用に入る機会がたまたまあり、「あ、この構成いいかも:relaxed:」と思ったので、改めてこの機会にまとめようかなと

## こんな人は参考になるかも？

- 今からサービスを紹介するページを作りたい！運用したい！改善サイクルを早く回したい！:muscle:
- 自社のコーポレートサイト作らないといけない:sob: お問い合わせフォームも付けてねって言われたよ:joy:
- 9割静的なページ + 1割動的なページのサイトを作らないと!ホスティング先は？デプロイどうする？動的に差し込みたいデータどうやって取ってくる？みたいな人:fire:

## あるあるな要件

- :boy: 「お問い合わせフォームつけてね、メールにも飛ばしたいし、Slackにも飛ばしたいよね」
- :boy: 「プレスリリース等のNews一覧ページ/詳細ページも作りたい」
- :boy: 「Newsページの詳細ページはブログ書くみたいなCMSほしい、あ、見出しも付けたい！画像もアップロードしたい！タグも付けたい」
- :boy: 「いや、、、とはいえ、WordPress導入はしたくない、、、、CMSも作りたくない、運用保守したくない、、！」
- :girl: 「エンジニアの手を使わずに反映できる仕組みがほしいよね」

## 僕はこうやって作った

[![Screenshot from Gyazo](https://gyazo.com/e38c9f8f3326b1b43ef622fbe6b6ce23/raw)](https://gyazo.com/e38c9f8f3326b1b43ef622fbe6b6ce23)

それぞれの良さをざっとまとめますね

## Nuxt.js

[![Screenshot from Gyazo](https://gyazo.com/502acd192eef13769269719a9809eb0f/raw)](https://gyazo.com/502acd192eef13769269719a9809eb0f)


普段めちゃめちゃ `React.js x TypeScript` でアプリケーション作るんですけど、こういう静的なテンプレートがほとんどのサイトは `<template></template>` で書ける方がいいかなと思って、Nuxt.jsにしました:bow:

この辺通りに進めれば、ベースのプロジェクトはさくっとできるので、最高:muscle:

ref: https://ja.nuxtjs.org/guide/installation

ディレクトリ構成は https://ja.nuxtjs.org/guide/directory-structure この辺を参考にしてほしいです:pray:
あるあるでいうとこんな感じの構成

```
pages
├── about.vue // /about ページ
├── index.vue // TOPページ
├── news
│   ├── _id.vue // News詳細ページ
│   └── index.vue // News一覧ページ
layouts
└── default.vue // Layout構成
components
├── Footer.vue
├── Header.vue
```

- `layouts/default.vue` (あっ、pugで書いています:pray:)

```vue
<template lang="pug">
.layouts
  .layouts__header
    HeaderComponent
  .layouts__main
    nuxt
  .layouts__footer
    FooterComponent
</template>
<script>
import HeaderComponent from "../components/Header.vue";
import FooterComponent from "../components/Footer.vue";

export default {
  components: {
    HeaderComponent,
    FooterComponent
  }
};
</script>
```

今のところ、マークアップできるぐらいのデザイナーが迷わずにcommitできているのを見ると、Nuxt.js選んで良かったかなーとか思ったりしました:sob: :sob: 

## contentful

https://www.contentful.com/

Headless CMSと言われるもの。
なにができるかめちゃ簡単に説明すると「Newsを投稿するCMSが用意されていて、欲しい型でJSONが返ってくる」

[![Screenshot from Gyazo](https://gyazo.com/b7b64f2def1098442de4cc07397ec003/raw)](https://gyazo.com/b7b64f2def1098442de4cc07397ec003)

### contentfulでNewsページのモデル設計をする

あるあるな構成:bow:

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/15816/68c19a72-a6b8-bce5-e477-dd6ca2b88230.png)

この辺の構成は要件詰めながら、エンジニアがここまで作ってあげると良さそう:dog:

### CMSどんな感じ？

タイトルを設定したり、記事内容を書いたり、、、！！

[![Screenshot from Gyazo](https://gyazo.com/98cdd1a3f540c3a8eb0f3f436941196a/raw)](https://gyazo.com/98cdd1a3f540c3a8eb0f3f436941196a)

公開時間を設定したり、サムネイルの画像をアップロードしたり、Instagramから取ってきたりetc

[![Screenshot from Gyazo](https://gyazo.com/50b8d475836c6092559f2b042fedcb7b/raw)](https://gyazo.com/50b8d475836c6092559f2b042fedcb7b)

### そもそもcontentfulにどういうフォーマットあるの？

[![Screenshot from Gyazo](https://gyazo.com/31299229106a5b6c66797f20e66ca6d5/raw)](https://gyazo.com/31299229106a5b6c66797f20e66ca6d5)

この辺までは自由に扱えます！！

### Nuxt.js側でどうやってFetchしてくるの？

News一覧を取ってくるところだと、こんな感じで書いています！
contentfulのNewsデータをFetchしてくるところも、limitやorderのパラメーターがあるので柔軟にいけます！

```vue
<template lang="pug">
</template>
<script>
import { createClient } from "~/plugins/contentful.js"; // contentfulのplugin設定をする
const client = createClient();
export default {
  components: {},
  data() {
    return {
      currentPosts: []
    };
  },
  asyncData({ params }) {
    return client
      .getEntries({
        content_type: "news", // News一覧を
        order: "-fields.publishedAt", // 最新順で
        limit: 4 // 4件取ってくる
      })
      .then(entries => {
        return {
          currentPosts: entries.items
        };
      })
      .catch(error => {});
  },
  methods: {
  }
};
</script>
<style lang="stylus"></style>
```

動的にデータを入れてCMSもあって、API化させたい！みたいなときにcontentful使ってみるのはいかがでしょうか？

### Netlify

(Netlifyってロゴは先頭小文字だし、サイト内は先頭大文字だし、正しい表記どっち？！？！？ってなりませんか、皆様)

[![Screenshot from Gyazo](https://gyazo.com/e499b1ef6ef14b21f50aca0a2006932c/raw)](https://gyazo.com/e499b1ef6ef14b21f50aca0a2006932c)

なにができるかというと

- hosting
- build
- deploy

がベースで、
PullRequestがmasterにマージされれば、 `build` -> `deploy` まで走る仕組みとかはマジで改めて最高です:fire:

とはいえ、僕的には

- **deploy preview**
  - Pull Requestごとにプレビューリンクが発行されるからマージ前に皆がチェックできる
- **Netlify Form**
  - `<form>` を特に難しいことしなくてもメールに届くし、Slackにも届くし、各種webhookの対応できる

上記2つが「チームで開発する視点」だと非常に有効でした！！

### deploy preview

[![Screenshot from Gyazo](https://gyazo.com/6148396df20211ee7979af8b78f44fb6/raw)](https://gyazo.com/6148396df20211ee7979af8b78f44fb6)

(すでにマージされた事例で申し訳ないのですが)
ココ！ってなっているところをクリックすると、preview用のリンクが発行され、マージ前に皆で動作確認できた上でマージする運用がしやすくて、これもnetlifyにGitHubの設定の連携をするだけでうまく行ったのですごく良かったです:sob:

### Netlify Form

一般的なformの構成に

```html
<form>
  <!-- 入力項目 -->
  <input type="submit" />
</form>
```

これに

```html
<form netlify>
  <!-- 入力項目 -->
  <input type="submit" />
</form>
```

`netlify` を `<form>` に入れるだけでform内のsubmit情報が全てNetlify側に吸い上げられる:fire: :fire:

Netlify側でformの入力項目が吸い上げられたら、あとはNetlify側でどこに飛ばすか(メールやSlack)を最後設定するだけで、
メールに飛んだり、Slackに飛んだりする:bow:

お問い合わせフォームをSlackとメールに飛ばす設定例

[![Screenshot from Gyazo](https://gyazo.com/ee200b310e07e9654df78706b4c24fdb/raw)](https://gyazo.com/ee200b310e07e9654df78706b4c24fdb)

以下実用例コード(pugです):bow:

```vue
form(
  name="contact" // Netlify formのデータのグルーピングに命名必須 
  @submit="checkForm" // 送信前チェック
  method="POST"
  action="/contact/accepted" // 送信完了後にどこのページに飛ぶか
  netlify // Netlify formを利用するにはこれを入れるだけ
)
```

## チームで開発する上において

- 最初の仕組み(contentfulをFetchするところやNetlify form)だけ作れば**エンジニアの手を借りずに(とはいえレビューとかはあるよ)、デザイナーが率先してサイトを更新** したり、 **Pull Requestがマージされればそのまま本番反映される仕組み** はエンジニア手離れできている and 改善サイクルが圧倒的に速いなと思っていてめちゃめちゃいいなと今のところチームで運用していても思う:fire:

## 最後に

色々開発/運用しているしているフェーズだが本構成でサイト作るのは良さそうだなという所感だったのでまとめてみました。
httpsの証明書あたりもNetlifyに寄せていたりしますが、本当は社内の証明書全てAWSのACMで管理しているのもあると、そちらに寄せたいなーとか思ったりなのですが、その辺の知見もまだない感じです！:fire:

nuxt.config.jsの設定周りの知見も少しずつ増えてきたので、手を動かしながらベストプラクティス探していこうと思います！