こんにちはjunです。前回の記事は概念と基本的なコンテンツの作成・取得・表示について解説しました。今回の記事では
について解説していきます。それでは早速いきましょう。
Nuxt.jsはpages/
というディレクトリ配下にファイルを作ることで、vue-routerの設定が自動で行われURLが生成されます。私のブログでは/articles/{sulg}
というURLで対象の記事を呼び出します。そのURLが有効になるにはpages/
ディレクトリの構築をする必要があります。
一方、Nuxt Contentで任意の記事データを取得してみるとpath:
というプロパティがあります。それはcontent/
ディレクトリをルートとしたパスになっています。以下のような構成の場合、
├── content
│ ├── articles
│ │ ├── aaaaaa.md
aaaaaa.md
のデータのパスは/articles/aaaaaa
となります。つまりhttps://example.com/articles/aaaaaa
というルートに対して、aaaaaa.md
が呼び出されるように設定します。
ひとまず以下のようにpages/
配下に作成してください。
├── pages
│ ├── articles
│ ├── _slug.vue
_slug.vue
といものを作ることで公式にある通り、/articles/{sulg}
のルートに対してparams.sulg
で{sulg}
の値を取得できるようになります。
それでは_sulg.vue
で以下のように記述します。
<template>
<article>
<nuxt-content :document="content" />
</article>
</template>
<scirpt>
export default {
async asyncData({ $content, params,redirect }) {
const content = await $content('articles').where({path:"/articles/"+params.slug}).fetch();
if(content.length > 0){
return {
content:content[0],
}
}else{
redirect('/articles')
}
}
}
</script>
asyncData()
中で$content()
をフェッチします。そしてwhere()
で"/articles/"+params.slug
に一致するコンテンツを引っ張るようにします。
where
クエリを使用すると配列で結果が返るので、あれば一致した結果、なければ一覧ページにリダイレクトするようにします。以上でNuxtにおけるpagesディレクトリの設定は完了です。
asyncData()
はSSRの時のpage/
配下のファイルで使用できます。サーバーサイドで処理される箇所であり、そこで$contentをfetchします。静的書き出しを行うと、asyncData()
内の処理は書き出し中に実行されます。
公式:Data Fetching
ブログ記事は単に文章だけでなく、太字、リンク、画像、見出し、目次が大体必要になります。文章の修飾はマークダウンの記述を行えば問題ありません。マークダウン記法は今回は解説しません。こちらの記事がお世話になりました。
マークダウンで画像を表示する場合は基本的に以下のように記述します。
![image alt text](/image/sample.jpg)
Nuxtの場合は画像をasset
あたりに入れておき、require('~/asset/sample.jpg')
という感じで依存性を解決できます。しかしマークダウンでは以下のパターンで画像パスの解決ができません。
// パターン1
![image alt text](~/asset/sample.jpg)
// パターン2
![image alt text](/asset/sample.jpg)
// パターン3
![image alt text](require(~/asset/sample.jpg))
画像のレンダリングは"@nuxt/content": "^1.14.0"
時点で特段に対応されておらず、ドキュメントにも書かれていませんでした。Githubのissueでも報告されているように議論となっています。
一応解決策としてはマークダウンファイルそのものにVueコンポーネントを書いてしまうことです。最初に画像レンダリング用のコンポーネントを作成します。
<template>
<img :src="imgSrc()" :alt="alt"/>
</template>
<script>
export default {
props: {
src: {
type: String,
required: true
},
alt: {
type: String,
},
},
methods: {
imgSrc() {
try {
return require(`~/assets/image/${this.src}`)
} catch (error) {
return null
}
}
},
}
</script>
上記のコンポーネントを マークダウン に記述します。
以下のようにします。
<imageRender src="sample.jpg"/>
上記の記述はNuxt Contentがåいい感じにvueコンポーネントとして扱ってくれます。パスの解決はコンポーネント内のrequire()
が行います。あまりかっこいい方法ではありませんが、一応これで画像をレンダリングできます。
Nuxt Contentはマークダウン内のHTMLをHTMLとして扱ってくれますので、カスタムな要素を記述できます。
<div class="alert alert-warning">
このアラートもこのようにHTMLをマークダウンに書いています!
</div>
Nuxt Contentは一応開発者向けなのか、簡単にコードブロックに対してシンタックスハイライトを有効にすることができます。prismJSを使用するのでまずはインストールします。
npm install prism-themes
そしてnuxt.config.js
で以下のようにテーマのCSSを設定します。
content: {
markdown: {
prism: {
theme: 'prism-themes/themes/prism-material-oceanic.css'
}
}
},
以上でコードブロック内にてシンタックスハイライトが有効になります。
目次の作成、もとい見出しのデータの取得は簡単です。const content = $content().fetch()
で取得した content.toc
で見出しのデータが取得できます。
toc:Array[8]
0:Object
depth:2
id:"サイトのルーティングを考えてpagesの構成を設定する"
text:"サイトのルーティングを考えてpagesの構成を設定する"
1:Object
私のブログの左サイドにある目次も、上記のようなオブジェクトを利用して作っています。
以上がブログに必要であろう要素をNuxt Contentで記述しました。私の一般記事のtemplateは以下ようになっていますので、是非参考にしてください。
<template>
<div class="p-main-container">
<div class="p-main-wrapper">
//目次のコンポーネント
<toc :toc="content.toc"/>
//メインのエリア
<div id="l-center-area">
//サムネイル
<img v-if="thumbnail" class="c-article-thumbnail" :src="thumbnail" :alt="content.title">
//タグとカテゴリーのバッチ
<div class="p-article-badge p-badge-container">
<nuxt-link class="c-tag-badge u-blue" v-for="(c,index) in content.category" :key="'category-'+index" :to="'/category/'+c">
<span>{{$store.getters['getCategoryTextBySlug'](c)}}</span>
</nuxt-link>
<nuxt-link class="c-tag-badge" v-for="(t,index) in content.tag" :key="'tag-'+index" :to="'/tag/'+t">
<span>{{$store.getters['getTagTextBySlug'](t)}}</span>
</nuxt-link>
</div>
<h1 class="c-article-header">{{ content.title }}</h1>
// 更新一時など
<div class="p-articler-date">
<span class="c-date"><fa-icon :icon="['fa', 'history']"/>{{ updateAt }}</span>
<span class="c-date"><fa-icon :icon="['far', 'clock']"/>{{ createdAt }}</span>
</div>
// マークダウンのレンダリング箇所
<nuxt-content :document="content" />
</div>
//サイドメニュー
<sidemenu/>
</div>
</div>
</template>
それではpagesファイル、マークダウンも作成したのでとりあえずある分だけ静的書き出ししてみましょう。公式の説明がありますがnuxt.config.js
でgenerate
オプションを設定必要があります。では以下のように設定します。
generate: {
async routes () {
const { $content } = require('@nuxt/content')
const files = await $content({ deep: true }).only(['path']).fetch()
return files.map(file => file.path === '/index' ? '/' : file.path);
}
},
これは何をやっているかというと、$content({ deep: true })
を使用してcontent配下にあるマークダウン一式とそのパスを全て取得して、Nuxtに生成すべきルートを伝えています。なぜこれを行う必要があるのかという理由ですが、Nuxt.jsはpages/
配下の構成を元にして必要なページを生成します。しかしどんなルート名になるかわからない_sulg.vue
というファイル(動的ルート)がある場合は、とりうるルートをgenerate
オプション内で明示的に指定する必要があります。
Nuxt.js自身はcontents配下の構成とパスがどうなっているのかわからないので、Nuxt Contentから取得します。
nuxt.config.js
でSSGができる設定にしたら
npm run generate
を叩くことで静的書き出しが行われます。書き出し後にはdist/
というビルドファイルが作成されます。
npm run start
でひとまずローカル環境でdist/
をドキュメントルートとしてみることができます。以下のような構成で作っていた場合、
├── content
│ ├── articles
│ │ ├── sample.md
│
├── pages
│ ├── articles
│ ├── _slug.vue
http://localhost:3000/articles/sample
にアクセスすると内容が見れると思います。curlでhttp://localhost:3000/articles/sample
でアクセスしてきちんと静的HTMLが書き出されているかを確認してみましょう。
スタイルとかの問題はあるかもしれませんが、ひとまずpages
・content
ディレクトリ、nuxt.config.js
の設定を行えばブログ的な構成とCMSとしての機能が実装できました。次回は記事の一覧ページとページング処理について解説していきます。
コメント
コメント読み込み中..