見出し画像

ポートフォリオサイトを作ろう!【第3回Nuxt.js にContentfulの記事を表示する編】

※この記事について
現在Nuxt+Netlify+Contentfulでポートフォリオサイトを構築しており、その連載記事となります。
連載記事はマガジンにまとめています。こちらをご覧ください。

前回はContentfulの設定を行いました。今回はContentfulで作成したコンテンツ記事を、Nuxt.jsで表示するところまでやりたいと思います。

それでは始めます。


Contentfulパッケージをダウンロード

ターミナルより、Nuxtのディレクトリへ移動し下記実行します。これでパッケージがダウンロードできます。

yarn add contentful


APIKeyの取得

まず、環境ごとに環境変数を設置するために dotenv を導入します。
下記を実行します。

yarn add dotenv


次に、Contentfulの管理画面に移動します。
ここから、「Settings」→ 「API keys」をクリックします。

APIの画面が表示されるので、画面右の「Add API Keys」をクリックします。

KEYの追加画面が表示されるので、Nameに名前を記入します。
また、「Space ID」と「Content Delivery API - access token」をコピーし、画面右上の「Save」をクリックします。

.envファイルの作成

次に、プロジェクトのディレクトリ直下に「.env」ファイルを作成します。「.env」ファイルには、先ほどコピーした「Space ID」と「Content Delivery API - access token」を記入します。

CTF_SPACE_ID=【コピーしたSpace ID】
CTF_CDA_ACCESS_TOKEN=【コピーしたContent Delivery API - access token】
CTF_PAGE_TYPE_ID="page"


Nuxt.js側での接続

次にNuxt.js側での接続設定をします。

プロジェクトルート直下にある「nuxt.config.js」ファイルを下記のように修正します。

const {getConfigForKeys} = require('./lib/config.js')
const ctfConfig = getConfigForKeys([
 'CTF_PAGE_TYPE_ID',
 'CTF_SPACE_ID',
 'CTF_CDA_ACCESS_TOKEN'
])
const {createClient} = require('./plugins/contentful')
const cdaClient = createClient(ctfConfig)
const config = {
 head: {
   title: 'nuxt_page',
   meta: [
     { charset: 'utf-8' },
     { name: 'viewport', content: 'width=device-width, initial-scale=1' },
     { hid: 'description', name: 'description', content: 'Nuxt.js project' }
   ],
   link: [ { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' } ]
 },
 loading: { color: '#3B8070' },
 build: {
   extend (config, { isDev, isClient }) {
     if (isDev && isClient) {
       config.module.rules.push({
         enforce: 'pre',
         test: /\.(js|vue)$/,
         loader: 'eslint-loader',
         exclude: /(node_modules)/
       })
     }
   },
 },
 plugins: [ { src: '~plugins/contentful' } ],
 generate: {
   routes () {
     return cdaClient.getEntries({
       'content_type': ctfConfig.CTF_PAGE_TYPE_ID
     }).then(entries => {
       return [
         ...entries.items.map(entry => `/page/${entry.fields.path}`)
       ]
     })
   }
 },
 env: {
   CTF_SPACE_ID: ctfConfig.CTF_SPACE_ID,
   CTF_CDA_ACCESS_TOKEN: ctfConfig.CTF_CDA_ACCESS_TOKEN,
   CTF_PAGE_TYPE_ID: ctfConfig.CTF_PAGE_TYPE_ID
 }
}
module.exports = config

次に、環境変数を読み込むためファイルを作成します。プロジェクトディレクトリ直下に「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_PAGE_TYPE_ID: process.env.CTF_PAGE_TYPE_ID,
     CTF_SPACE_ID: process.env.CTF_SPACE_ID,
     CTF_CDA_ACCESS_TOKEN: process.env.CTF_CDA_ACCESS_TOKEN
   }
   return getValidConfig(configEnv, keys)
 }
}

次に、Contentfulにアクセスするためのファイルを作成します。
プロジェクトディレクトリ直下の「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.jsとContentfulの連携準備完了です。


コンテンツ記事の取得

それでは次に、コンテンツ記事を取得する設定を行います。

今回は

・「/items」に作成したコンテンツの一覧を表示
・「/items/[ID]」に作成したコンテンツの詳細を表示

という風に設定します。

まずは、プロジェクトディレクトリ直下の「pages」ディレクトリの下に「items」というディレクトリを作成し、その中に「index.vue」というファイルを作成します。

pages
- items
  - index.vue

「pages/items/index.vue」には下記のように記載します。

<template>
  <section class="index">
    <card v-for="post in posts"
      v-bind:key="post.fields.path"
      :title="post.fields.title"
      :path="post.fields.path"
      :image="post.fields.image"
    />
  </section>
</template>

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

const client = createClient()
export default {
  transition: 'slide-left',
  components: {
    Card
  },
  async asyncData ({ env, params }) {
    return await client.getEntries({
      'content_type': env.CTF_PAGE_TYPE_ID,
    }).then(entries => {
      return {
        posts: entries.items
      }
    })
    .catch(console.error)
  }
}
</script>

<style scoped>
.index {
  display: flex;
  flex-wrap: wrap;
}
</style>

次に、「components」ディレクトリ内に「card.vue」というファイルを作成します。
「components/card.vue」 には以下のように記載します。

<template>
 <article class="card">
   <nuxt-link v-bind:to="{ name: 'items-path', params: { path: path }}" class="wrapper">
     <img class="card_image" v-bind:src="image.fields.file.url"/>
     <h1 class="card_title">{{ title }}</h1>
   </nuxt-link>
 </article>
</template>
<script>
export default {
 props: ['title', 'path', 'image']
}
</script>

最後に、「items/_path.vue」を作成します。

<template>
 <section class="path">
   <h1 class="path_title">{{ post.fields.title }}</h1>
   <img class="path_image" v-bind:src="post.fields.image.fields.file.url"/>
   <vue-markdown>{{post.fields.body}}</vue-markdown>
 </section>
</template>
<script>
import VueMarkdown from 'vue-markdown'
import {createClient} from '~/plugins/contentful.js'
const client = createClient()
export default {
 transition: 'slide-left',
 components: {
   VueMarkdown
 },
 async asyncData ({ env, params }) {
   return await client.getEntries({
     'content_type': env.CTF_PAGE_TYPE_ID,
     'fields.path': params.path,
     order: '-sys.createdAt'
   }).then(entries => {
     return {
       post: entries.items[0],
     }
   })
   .catch(console.error)
 }
}
</script>

以上で対応完了です。

ブラウザで、「/items」にアクセスすると下記のような画面が表示されます。(今回は試しに3記事投稿してみました。)

クリックすると、詳細ページへ遷移します。遷移先のURLは、「items/【path】」(pathには記事投稿時にpathフィールドに記入した値が入る)となります。

以上で『Nuxt.js にContentfulの記事を表示する編』を終了します。次回はどんなデザインにするかの設計ができたら良いなと思います。


おまけ

今回contentfulと連携を行いました。ブログを自分のサイトで持ちたい場合は利用すると便利かと思います。
設定していて思ったのですが、私の場合ブログはnoteで書くので、今回はcontentful利用するまでもないかもしれません。
ブログ書かないでポートフォリオサイトのみあれば大丈夫という方は、今回の設定飛ばしていただいても大丈夫かと思います。


--- 🖥---

この記事は、私がポートフォリオサイトを作成するまでを追った連載形式です。連載の他の記事は下記マガジンをご覧ください。


参考

下記の記事を参考にさせていただきました。本当にありがとうございます。


読んでいただきありがとうございます。