この記事は最終更新日から 2 年以上が経過しており、内容が古くなっている可能性があります。
nuxt/content モジュールを使えば、カテゴリー・タグの作成、前後の記事リンクの出力など、ブログに必要な一通りのカスタマイズができる。学習コストも比較的低めなので、Nuxt.js 初心者でも取り入れやすい。
nuxt/content モジュールを使って NuxtJS アプリケーションを強化します。content/ディレクトリに書き込むこと で、MongoDB のような API を使って Markdown、JSON、YAML、XML、CSV ファイルを取得します。
MongoDB というのがよく分からないが、外部サービスから API で引っ張ってくるのではなく、ローカル環境のファイルで記事を管理できる。
nuxt/content のインストール
npm install @nuxt/content
// or
yarn add @nuxt/content
{
modules: [
'@nuxt/content'
],
}
コンテンツの作成
content ディレクトリを作成し、その中にコンテンツを作っていく。content 配下のディレクトリ名は任意名で作成できる。
ブログ記事
content/
post/
post-1.md
post-2.md
home.md
カテゴリー・タグ
content/
category/
category-1.md
category-2.md
tag/
tag-1.md
tag-2.md
カテゴリー・タグの分だけファイルを作成する。category-1
tag-1
の部分は、カテゴリー・タグそれぞれのスラッグになる。
フロントマター
ブログ記事
---
title: "タイトル"
published: "2018-02-15"
updated: "2020-05-26"
category: "カテゴリー名"
tag: ["タグ名1", "タグ名2"]
image: "/images/path/filename.png"
---
記事の説明が入ります
フロントマター(---
で囲まれた部分、yaml 形式で記述)で記事のメタデータが管理できる。任意で追加することが可能である。フロントマターでdescription
を定義していない場合は、<!--more-->
(※半角を挟まない)より前の部分がdescription
として抽出される。
カテゴリー・タグ
---
name: コーディング
---
カテゴリー・タグの説明が入ります
<!--more-->
component を作成
component ディレクトリ内にコンテンツ表示用の vue ファイルを作成する。
components/
post/
_slug.vue
index.vue
index.vue
記事一覧
記事一覧を取得
<script>
export default {
(前略)
async asyncData ({ $content }) {
const posts = await $content('post')
.sortBy('published', 'desc')
.fetch()
return { posts }
},
(後略)
}
</script>
$content('post')
…content
内のpost
フォルダーより記事を抽出.sortBy('published', 'desc')
…公開日を降順でソート.fetch()
…データを収集
記事一覧を表示
<template>
<main role="main">
<div class="p-card">
<article
class="p-article-item"
v-for="(post, index) in posts"
:key="index"
>
<nuxt-link :to="'/post/' + post.slug +'/'" class="p-article-item__link">
<figure class="p-article-item__thumb">
<img
:src="post.image"
:alt="post.title + ' サムネイル'"
class="p-article-item__img"
/>
</figure>
<div class="p-article-item__text">
<h2 class="p-article-item__title">{{ post.title }}</h2>
<ul class="meta-container">
<li class="meta meta--published">
<time :datetime="post.published"
>{{ post.published | format-date }}</time
>
</li>
<li class="meta meta--category">{{ post.category }}</li>
</ul>
</div>
</nuxt-link>
</article>
</div>
</main>
</template>
v-for="(post, index) in posts" :key="index"
変数post
に格納された情報をpost
の数だけ繰り返し表示する。post.title
post.image
などで、フロントマターで定義したメタ情報を引用できる。
カテゴリー・タグに属する記事一覧を取得する
<script>
export default {
(前略)
async asyncData ({ $content }) {
.where({ tags: { $contains: tag.name }})
.sortBy('published', 'desc')
.fetch()
return { posts }
},
(後略)
}
</script>
記事全体からカテゴリー・タグ名を含む記事を抽出する。
個別ページ
ページの情報を取得する
<script>
export default {
(略)
async asyncData ({ $content, params }) {
const post = await $content('post', params.slug).fetch()
const [prev, next] = await $content('post')
.only(['title', 'slug'])
.sortBy('published', 'asc')
.surround(params.slug)
.fetch()
const tagList = await $content('tag')
.only(['name', 'slug'])
.where({ name: { $containsAny: post.tags } })
.fetch()
const tags = Object.assign({}, ...tagList.map((s) => ({ [s.name]: s })))
const cate = await $content('category')
.only(['name', 'slug'])
.where({ name: { $containsAny: post.category } })
.limit(1)
.fetch()
const category = cate[0]
return {
post,
prev,
next,
tags,
category,
}
},
}
</script>
$content('post', params.slug)
…post
フォルダよりスラッグと一致する記事を取得.surround(params.slug)
…スラッグの前後の記事を取得category
tag
フォルダより、フロントマターで定義された情報(post.category
post.tag
)と一致するカテゴリー・タグ情報(name
slug
)を取得
ページの情報を表示する
<template>
<main class="l-main">
<article class="p-article">
<header class="p-article__header">
<div class="p-article__thumbWrap">
<figure class="p-article__thumb">
<img :src="post.image" :alt="post.title + ' メイン画像'">
</figure>
</div>
<div class="p-article__main">
<h1 class="p-article__title">{{ post.title }}</h1>
<div class="meta-top">
<ul class="meta-container">
<li class="meta meta--published">
{{ post.published | format-date }}</time>
</li>
<li v-if="post.updated" class="meta meta--updated">
<time datetime="post.updated">{{ post.updated | format-date }}</time>
</li>
</ul>
</div>
</div>
</header>
<div class="p-article__content">
<nuxt-content :document="post" />
</div>
<footer class="p-article__footer">
<!-- メタ情報 -->
<div class="meta-bottom">
<ul class="meta-container">
<li class="meta meta--category">
<nuxt-link :to="`/post/category/${category.slug}/`">{{ category.name }}</nuxt-link>
</li>
<li class="meta meta--tag">
<span v-for="(tag, index) in post.tags" :key="index" class="mr-2"><nuxt-link :to="`/post/tag/${tags[tag].slug}/`">{{ tags[tag].name }}</nuxt-link></span>
</li>
</ul>
</div>
<!-- 前後の記事リンク -->
<ul class="page-nav">
<li v-if="next" class="page-nav__item page-nav__item--next">
<p class="page-nav__text page-nav__text--next"><span class="page-nav__text-inner">次の記事</span></p>
<nuxt-link :to="`/post/${next.slug}/`" class="page-nav__link">
{{ next.title }}
</nuxt-link>
</li>
<li v-else class="page-nav__item page-nav__item--none">
</li>
<li v-if="prev" class="page-nav__item page-nav__item--prev">
<p class="page-nav__text page-nav__text--prev"><span class="page-nav__text-inner">前の記事</span></p>
<nuxt-link :to="`/post/${prev.slug}/`" class="page-nav__link">
{{ prev.title }}
</nuxt-link>
</li>
<li v-else class="page-nav__item page-nav__item--none">
</li>
</ul>
</footer>
</article>
</main>
</template>
<nuxt-content :document="post" />
…ページの body を表示v-if="prev"
v-if="next"
“…prev
next
が存在するときに前後の記事リンクを表示v-else
…存在しない時は空白を表示
nuxt/content で PrismJS を使う
nuxt/content をインストールすると、PrismJSも一緒にインストールされる。prism-theme をインストールして PrismJS のテーマを選ぶ方法もあるが、今回は拡張性のある別の方法にする。
nuxt.config.js の記述
content: {
markdown: {
prism: {
theme: false,
}
}
},
サーバーサイドのハイライトは使わないのでfalse
とする。
prism.js を作成
/plugins/
フォルダ内にprism.js
ファイルを作成する。
import Prism from "prismjs";
// テーマの適用
import "prismjs/themes/prism-tomorrow.css";
// 行番号を表示する(任意)
import "prismjs/plugins/line-numbers/prism-line-numbers";
import "prismjs/plugins/line-numbers/prism-line-numbers.css";
// 言語を表示する(任意)
import "prismjs/plugins/show-language/prism-show-language";
// 表示できる言語を追加する(任意)
import "prismjs/components/prism-json.js";
import "prismjs/components/prism-markdown.min.js";
export default Prism;
Vue ファイルに埋め込み
<script>
import Prism from '~/plugins/prism'
export default {
…
mounted() {
Prism.highlightAll()
},
…
}
</script>
Prism.highlightAll()
で PrismJS のハイライトが適用される。
マークダウンファイル内での記述方法
/*
```の後で言語名を指定
※実際は半角開けない↓
*/
` ` `css
/* prism */
.nuxt-content-highlight {
position: relative;
.filename {
display: inline-block;
border-bottom: none;
padding: .25rem .5rem;
font-size: .8rem;
position: absolute;
right: 1px;
top: 1px;
color: #bbb;
}
}
` ` `
/* ```で閉じる */
```
(バッククオート 3 つ)の後ろで指定したい言語、[]
内でファイル名が記述できる。
/* prism */
.nuxt-content-highlight {
position: relative;
.filename {
display: inline-block;
border-bottom: none;
padding: 0.25rem 0.5rem;
font-size: 0.8rem;
position: absolute;
right: 1px;
top: 1px;
color: #bbb;
}
}
出力されるタグ
<div class="nuxt-content-highlight" data-v-xxxxxxxxx="">
<span data-v-xxxxxxxxx="" class="filename">_base.css</span>
<pre class="line-numbers language-css" data-v-xxxxxxxxx="">
(略)
</pre>
</div>
<pre>…</pre>
内はハイライト用 css が適用されているが、<span data-v-xxxxxxxxx="" class="filename">…</span>
(ファイル名の部分)にはデフォルトのスタイルがない。当ブログではコードブロック右上に表示されるように css スタイルを付けた。