LoginSignup
7
2

More than 3 years have passed since last update.

Nuxt.js+Contentful+Netlifyでブログサイトを作るために環境構築する

Last updated at Posted at 2020-12-14

はじめまして、新卒1年目フロントエンドエンジニアのフジタです。
今回はNuxt.jsで個人的にブログサイトを作成しているのでそれについてまとめていこうと思います。

Nuxt.jsとは

SSR(サーバーサイドレンダリング)に対応したVue.jsのフレームワークです。僕はSSRって聞くとなんだかワクワクします。

まだまだ勉強中なので詳しく知りたい方は公式ドキュメントをご覧ください。

SSR(サーバーサイドレンダリング)とは

SPA(シングルページアプリケーション)などでブラウザで行われていたJSの実行とHTML生成をサーバー側で行う技術です。
動作の流れとしては以下です。

  1. クライアント側からHTTPリクエストが送られる(差分のみ)
  2. サーバーが更新されたHTMLを生成してクライアント側に返す
  3. ブラウザ側でHTMLを表示

差分のみのHTTPリクエストが送信されるのはSPAも同じです。SPAとの違いはHTMLの生成をブラウザ側で行わずにサーバー側で行う点にあります。
メリットとしてSEOの向上、ユーザーの通信環境に左右されにくいことが挙げられます。

検索エンジンのクローラが完全に描画されたページを直接解析するため、SEO が向上します。
(中略)
特にインターネットの遅さや遅いデバイスでは、コンテンツの再生時間が短縮されます。サーバで描画されたマークアップは、すべての JavaScript がダウンロードされて表示されるまで待つ必要がないので、ユーザは完全に描画されたページをすぐに見ることができます。

引用:どうしてSSRなのか? - Vue SSR ガイド

デメリットはサーバー側の負担が大きくなることやNode.jsが実行できるサーバーを用意する必要があることが挙げられます。

  • より複雑なセットアップと開発の要件を構築します。静的ファイルサーバに展開できる完全静的 SPA とは異なり、サーバで描画されたアプリケーションでは Node.js サーバを実行できる環境が必要になります。

  • サーバ側の負荷が増えます。 Node.js の完全なアプリケーションを描画することは、静的ファイルを提供するだけでなく、CPU を多用することになるので、トラフィックが多いことが見込まれる場合は、対応するサーバの負荷に備え、キャッシュの対策を賢明に行なってください。

引用:どうしてSSRなのか? - Vue SSR ガイド

Nuxt.js以外に使うもの

  • Netlify
    • 静的サイトをホスティングすることができるWebサービス。超便利
  • Contentful
    • ヘッドレスCMS(ビューのないCMS)
    • ブログ記事のコンテンツ管理に使用します

ヘッドレスCMSをふわっと説明

通常のCMS(WordPressとか)はWebページ(フロントエンド)と管理(バックエンド)が1つに統合されています。ヘッドレスCMSはその2つを分けようという構造から生まれたCMSです。

つべこべ言わずに環境構築していく

create-nuxt-app を使う

すぐ始めたいのでcreate-nuxt-appを使用します。
※コマンド入力した後に色々選択します。(今回は省略)

$ npm -v
6.9.0

$ npm init nuxt-app sample-blog

起動できた

依存関係をインストール後、プロジェクトのフォルダにいきnpm run devをすると起動します。今回は試しにVuetifyを使ってみようと思い選択したので起動画面がいつもと違いました。びっくりした。
スクリーンショット 2020-12-13 21.10.08.png

仮でコンテンツの実装をする

一旦仮で記事コンテンツの実装をします

components/card.vue
<template>
  <v-card class="card mb-5">
    <nuxt-link
      :to="{ name: 'blog-slug'}"
      class="wrapper"
    >
      <v-card-title>記事タイトル</v-card-title>
    <v-card-text>
      <v-row
        align="center"
        class="mx-0"
      >
      </v-row>
      <div>記事の概要</div>
    </v-card-text>
    </nuxt-link>
  </v-card>
</template>

<style lang="scss" scoped>
.wrapper {
  text-decoration: none;
}
</style>
pages/index.vue
<template>
  <v-container class="index">
    <card
      v-for="i in 5"
      :key="i"
    />
  </v-container>
</template>

<script>
import Card from '~/components/card.vue'

export default {
  components: {
    Card
  }
}
</script>
pages/blog/_slug.vue
<template>
  <v-container class="slug">
    <h1 class="slug_title">
      タイトル
    </h1>
    <p class="slug_date">2020/12/14</p>
    <div>
      記事の内容
    </div>
  </v-container>
</template>

nuxt.config.jsでデフォルトでテーマが設定されているので背景が黒いですが、白だと同化してしまうのでそのままにしておきます。

スクリーンショット 2020-12-13 22.49.28.png

Vuetifyのおかげですでにそれっぽく見えますね^^

contentfulの設定をする

ログインする

こちらからログインできます。

コンテンツを作成する

  1. 上部のContent modelからAdd content typeで作成
  2. modalが出てくるので入力して作成

スクリーンショット 2020-12-13 23.08.13.png

スクリーンショット 2020-12-13 23.05.52.png

modelを使用してコンテンツを追加する

上部のContentからAdd [type名]でコンテンツを何個か作成します。

スクリーンショット 2020-12-13 23.11.57.png

スクリーンショット 2020-12-13 23.20.17.png

Contentfulをnuxtプロジェクトに追加する

$ npm add --dev contentful

以下、設定ファイルです。

lib/config.js
require('dotenv').config()

function getValidConfig(configEnv, keys) {
  let { config, missingKeys } = keys.reduce(
    (acc, key) => {
      if (!configEnv[key]) {
        acc.missingKeys.push(key)
      } else {
        acc.config[key] = configEnv[key]
      }
      return acc
    },
    { config: {}, missingKeys: [] }
  )

  if (missingKeys.length) {
    throw new Error(`Contentful key is missing : ${missingKeys.join(', ')}`)
  }
  return config
}

module.exports = {
  getConfigForKeys(keys) {
    const configEnv = {
      CTF_BLOG_POST_TYPE_ID: process.env.CTF_BLOG_POST_TYPE_ID,
      CTF_SPACE_ID: process.env.CTF_SPACE_ID,
      CTF_CDA_ACCESS_TOKEN: process.env.CTF_CDA_ACCESS_TOKEN
    }
    return getValidConfig(configEnv, keys)
  }
}
plugins/contentful.js
const contentful = require('contentful')
const defaultConfig = {
  CTF_SPACE_ID: process.env.CTF_SPACE_ID,
  CTF_CDA_ACCESS_TOKEN: process.env.CTF_CDA_ACCESS_TOKEN
}

module.exports = {
  createClient(config = defaultConfig) {
    return contentful.createClient({
      space: config.CTF_SPACE_ID,
      accessToken: config.CTF_CDA_ACCESS_TOKEN
    })
  }
}
nuxt.config.js
import colors from 'vuetify/es5/util/colors'

const pkg = require('./package')
const { getConfigForKeys } = require('./lib/config.js')
const ctfConfig = getConfigForKeys([
  'CTF_BLOG_POST_TYPE_ID',
  'CTF_SPACE_ID',
  'CTF_CDA_ACCESS_TOKEN'
])

const { createClient } = require('./plugins/contentful')
const cdaClient = createClient(ctfConfig)

export default {
 // 省略
  generate: {
    routes() {
      return cdaClient
        .getEntries(ctfConfig.CTF_BLOG_POST_TYPE_ID)
        .then(entries => {
          return [...entries.items.map(entry => `/blog/${entry.fields.slug}`)]
        })
    }
  },
  env: {
    CTF_SPACE_ID: ctfConfig.CTF_SPACE_ID,
    CTF_CDA_ACCESS_TOKEN: ctfConfig.CTF_CDA_ACCESS_TOKEN,
    CTF_BLOG_POST_TYPE_ID: ctfConfig.CTF_BLOG_POST_TYPE_ID
  }
// .env
CTF_SPACE_ID=<Contentfulで記載されているspace_id>
CTF_CDA_ACCESS_TOKEN=<Contentfulで記載されているアクセストークン>
CTF_BLOG_POST_TYPE_ID=<contentfulで記載されているポストタイプ>

Contentfulに対応したコンテンツの内容に書き換える

components/card.vue
<template>
  <v-card class="card mb-5">
    <nuxt-link
      :to="{ name: 'blog-slug', params: {
        sys: id
      }}"
      class="wrapper"
    >
      <v-card-title>{{ title }}</v-card-title>
    <v-card-text>
      <v-row
        align="center"
        class="mx-0"
      >
      </v-row>
      <div>{{ date }}</div>
    </v-card-text>
    </nuxt-link>
  </v-card>
</template>

<script>
export default {
  props: {
    title: {
      type: String,
      default: ''
    },
    id: {
      type: String,
      default: ''
    },
    date: {
      type: String,
      default: ''
    }
  }
}
</script>

<style lang="scss" scoped>
.wrapper {
  text-decoration: none;
}
</style>
pages/index.vue
<template>
  <v-container class="index">
    <card
      v-for="(post,i ) in posts"
      :key="i"
      :title="post.fields.title"
      :id="post.sys.id"
      :date="post.sys.updatedAt"
    />
  </v-container>
</template>

<script>
import Card from '~/components/card.vue'
import { createClient } from '~/plugins/contentful.js'

const client = createClient()
export default {
  transition: 'slide-left',
  components: {
    Card
  },
  asyncData({ env, params }) {
    return client
      .getEntries(env.CTF_BLOG_POST_TYPE_ID)
      .then(entries => {
        return {
          posts: entries.items
        }
      })
      .catch(console.error)
  }
}
</script>
pages/blog/_slug.vue
<template>
  <v-container class="slug">
    <h1 class="slug_title">
      {{ article.fields.title }}
    </h1>
    <p class="slug_date">{{ article.sys.updatedAt }}</p>
    <div>
      {{ article.fields.body.content[0].content[0].value }}
    </div>
  </v-container>
</template>

<script>
import { createClient } from '~/plugins/contentful.js'

const client = createClient()
export default {
  props: {
    id: {
      type: String,
      default: ''
    }
  },
  transition: 'slide-right',
  async asyncData({ env, params }) {
    return await client
      .getEntry(params.sys)
      .then(entrie => {
        return {
          article: entrie
        }
      })
      .catch(console.error)
  }
}
</script>

Contentfulのコンテンツを持ってこれた

スクリーンショット 2020-12-13 23.51.32.png

スクリーンショット 2020-12-13 23.51.39.png

Netlifyの設定をする

デプロイ

  1. GitHubでログインしておきます。
  2. New site from Gitを押す
  3. Gitのプロバイダーを選択する(今回はGitHub)
  4. Configure the Netlify app on GitHub.を押してGitHubでNetlifyと連携するリポジトリを追加する
  5. Build commandPublish directory(今回はdist)を設定してデプロイする

ContentfulとNetlifyのWebhook連携

先ほどの.envのキーを入力します。

スクリーンショット 2020-12-14 0.21.54.png

完成!

スクリーンショット 2020-12-14 0.39.06.png

最後に

以上、環境構築編でした。この後にマークダウンで記述するための設定やコーディングなどが控えているのですが、力尽きてしまったので今回はここまでになります。

次回 気が向いたらマークダウン編です。

7
2
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
7
2