「Nuxt Contentでブログを作ろう」など、
Nuxt公式ドキュメントとかでも紹介されてて気になってたNuxt Content。
ブログ作らないから使う時ないな〜っと思ってたけど、
マークダウンでページを作れるんだったらヘルプページとか簡単にできるのでは?
と思い、いろいろ試してみたときの備忘録。
今開発しているSSSAPIのヘルプや規約系をこれで作ってるけど楽ちん。。(´ω`)
ヘルプページはこんな感じ(´ω`)
@nuxt/contentの中身
@nuxt/contentの内部では、remarkとrehypeを使って、.mdファイルを変換しているよう。
・remarkjs/remark
・rehypejs/rehype
なので、設定はremarkやrehypeのプラグインの設定などを見る必要がある。
また、コードのシンタックスハイライトは、PrismJSを使っている。
・PrismJS
なので、このあたりのCSSファイルも組み込まれる。
インストール
まずは、インストール
$ npm install @nuxt/content
nuxt.configの設定
インストールしたらmodules
に追加。
オプション系はcontent
配下に書く感じ。
{ modules: [ '@nuxt/content' ], content: { // Options } }
記事・コンテンツを用意する
表示したいマークダウンファイルを用意する。
配置場所は、content
ディレクトリ配下。
content/ help/ sample1.md sample2.md index.md
記事の.mdファイルはこんな感じ。
冒頭にフロントマターを追加できて、追加情報を記載できる。
--- title: Introduction description: Learn how to use @nuxt/content. --- @nuxt/contentの使用方法を学びます。 より多くの仕切りを超えたコンテンツの全量。
.md内でVueコンポーネントも使える
.md内でVueコンポーネントも使えるので便利。
Buefyや@nuxt/imageのコンポーネントも使える(´ω`)
・Vueコンポーネント|コンテンツを作成する - Nuxt Content
記事・コンテンツを表示する
記事を表示するのはこんな感じ。
$content().fetch()
で記事を取得<nuxt-content />
で記事を表示
<template> <article> <!-- フロントマターのtitle --> <h1>{{ article.title }}</h1> <!-- markdown部分 --> <nuxt-content :document="article" /> </article> </template> <script lang="ts"> import { Component, Vue, Prop } from "nuxt-property-decorator"; @Component export default class HelpPage extends Vue { private article: any; async asyncData({ $content }) { // content/index.mdを取得 const article = await $content("index").fetch(); return { article }; } } </script>
記事の取得は直接パスを指定しているけど、検索などもできる。
・コンテンツを取得する - Nuxt Content
・コンテンツを表示する - Nuxt Content
カスタマイズや追加設定
GitHubのMarkdownスタイルを適用する
@nuxt/contentにはスタイルがついてないので、
自分で用意しないといけない。
全部用意するのは大変なので、github-markdown-cssを使うようにする。
$ npm install github-markdown-css
インストールしたら、nuxt.configのcssに追加。
{ css: [ '~/assets/css/buefy.scss', // githubのスタイルはbuefyよりも後に指定する 'github-markdown-css' ] }
最後に、スタイルが当たるように.markdown-body
を追加。
<template> <article> <h1>{{ article.title }}</h1> <!-- class="markdown-body"を追加 --> <nuxt-content class="markdown-body" :document="article" /> </article> </template>
・Buefy(Bulma) と github-markdown の共存 | 猫好きが猫以外のことも書く
目次(TOC)を表示する
目次の情報は自動的に生成されて、article.toc
で参照できる。
中身はこんな感じ。
{ "toc": [{ "id": "welcome", "depth": 2, "text": "Welcome!" }] }
これを使って、自分で目次部分を作成する感じ。
<template> <ul> <li v-for="link of article.toc" :key="link.id" :class="{ 'toc2': link.depth === 2, 'toc3': link.depth === 3 }" > <NuxtLink :to="`#${link.id}`">{{ link.text }}</NuxtLink> </li> </ul> </template>
また、生成されるのはh2とh3のみで、構造化されていない。
なので、depth
をみてclassを変更し、
classを使ってインデントさせるなどが必要。
見出しのリンクアイコンを変更する
@nuxt/contentはremark-autolink-headingsというremarkプラグインを使っているので、
見出しに自動でリンクが付いている。
ただ、Buefy(Bulma)を使っていたので、なんか微妙な感じに。。
<!-- 「## 操作方法」の場合(デフォルト)--> <h2 id="操作方法"> <a href="#操作方法" aria-hidden="true" tabindex="-1"> <!-- .icon .icon-link は設定してない。。--> <span class="icon icon-link"></span> </a> 操作方法 </h2>
nuxt.config
で設定すると、リンクの付け方やiconなどを変更できる。
{ content: { markdown: { remarkPlugins: [ [ "remark-autolink-headings", // remark-autolink-headings"の設定 { behavior: "append", content: { type: "element", tagName: "b-icon", // buefyのコンポーネントでもOK properties: { icon: "link-variant", size: "is-small", className: ["ml-1"] } } } ] ] } }, }
設定するとこんな感じに(´ω`)
<!-- 「## 操作方法」の場合(設定変更後)--> <h2 id="操作方法"> 操作方法 <a href="#操作方法" aria-hidden="true" tabindex="-1"> <span class="ml-1"> <i class="mdi mdi-link-variant"></i> </span> </a> </h2>
・markdown.remarkPlugins|設定 - Nuxt Content
・options|remarkjs/remark-autolink-headings
ページ内リンクに移動できるようにする
上記の設定で目次を作れるようになったけど、
ページ内リンクが動かなかったりする。
なので、ページ内リンクには、vue-scrolltoなどを設定する必要がある。
昔の記事はこちら。
#
付きのURLのときに、該当箇所まで移動する
目次にリンクが付いてくれるけど、ハッシュ付きのURLに直接アクセスしても
該当箇所に移動してくれないので、以下のような感じの対応が必要。
- マウント時にハッシュがあるかチェックして、
- ハッシュがあれば
vue-scrollto
で該当箇所まで移動
<script lang="ts"> import { Component, Vue, Prop } from "nuxt-property-decorator"; @Component export default class HelpPage extends Vue { // 略 mounted() { if (!process.browser) return; this.$nextTick(() => { setTimeout(this.moveToHash, 200); }); } private moveToHash() { const hash = this.$route.hash; if (hash && hash.match(/^#.+$/)) { this.$scrollTo(decodeURI(hash)); } } } </script>
はまったところ
Bulmaとgithub-markdownが競合する
Buefyを使ってるけど、bulmaとgithub-markdownのクラス名が競合するらしく、
シンタックスハイライトとかが大変なことになる。。(´・ω・`)
適宜、修正してするか、どちらかをやめるないといけない。。(´・ω・`)
見出しの最初に数字を使うとTOCのリンクが効かない
見出しからidの設定はremark-slugというremarkプラグインがやってくれていてる。
## 見出し <h1 id="見出し">見出し</h1>
ただ、ほぼ自動変換なので、見出しが数字はじまりだと、
不正なIDになってしまい、リンクが有効にならない。。
## 1.見出し <h1 id="1見出し">1.見出し</h1>
remark-slugにオプションなどもないので、見出しの最初に数字などは使えないっぽい。。
$content()
の書き方によって、SPAで動かない時がある
こんな感じでヘルプ記事を用意していて、
content/ help/ sample1.md sample2.md index.md
以下のような、URLでアクセスできるように、
/help/sample1 /help/sample2
パラメタを使って、表示できるようにしていた。
<!-- /pages/help/_slug.vue --> <script lang="ts"> import { Component, Vue, Prop } from "nuxt-property-decorator"; @Component export default class HelpSlugPage extends Vue { private article: any; async asyncData({ params, error, $content }) { const slug = params.slug; if (!slug) return error({ statusCode: 404 }); const article = await $content(`/help/${slug}`).fetch(); if (!article) return error({ statusCode: 404 }); return { article }; } } </script>
開発中は動作するけど、SPA(ssr:false, target:static
)でビルドすると、
.mdファイルが見つからないエラーに。。
$content
の指定の仕方を変えてみると、うまくいくようになった(´ω`)
const article = await $content("help", `${slug}`).fetch();
引数を複数与えることもできます。
$content('article', params.slug)
は/articles/${params.slug}
に変換されます。
ということらしいけど、挙動もちょっと違うのかもしれない。
・$content(path, options?)| コンテンツを取得する - Nuxt Content
SSGではnuxt.config
のgenerate.routes
の設定も
静的サイトを生成する場合、動的ルートがわからないので、
nuxt.cofig
のgenerate.routes
で教えてあげる必要がある。
export default { modules: [, '@nuxt/content' ], generate: { async routes () { const { $content } = require('@nuxt/content') const files = await $content().only(['path']).fetch() return files.map(file => file.path === '/index' ? '/' : file.path) } } }
generate.routes
を設定しておくと、nuxt/sitemapでも有効になるので便利(´ω`)
・ 静的サイト生成|発展的な機能 - Nuxt Content
・routes|The generate Property - NuxtJS
以上!!
PR: SSSAPI
GoogleスプレッドシートをAPI化するサービスを開発してます!
@nuxt/contentをゴリゴリ使ってるので、実際の動作などもみてみてください!
β期間中は最上位プランが無料なので、この機会にぜひぜひお試しください(´ω`)
■SSSAPI
https://sssapi.app