Skip to content

Instantly share code, notes, and snippets.

@utamori
Last active October 26, 2022 02:33
Show Gist options
  • Save utamori/2b6cd7436026ff6d14f58282032b5aa8 to your computer and use it in GitHub Desktop.
Save utamori/2b6cd7436026ff6d14f58282032b5aa8 to your computer and use it in GitHub Desktop.
title description imgUrl date authors tags
Create a Blog with Nuxt Content 和訳
The Content module is a git files based headless CMS that provides powerful features when it comes to write blogs, documentation sites or just adding content to any regular website. In this post we will go through most of the benefits of this module and discover how we can create a blog with it.
blog/creating-blog-with-nuxt-content/main.png
2020-07-02
Nuxt
Content
Markdown

content module はgitファイルベースのヘッドレスCMSで、ブログやドキュメントサイトを書いたり、普通のウェブサイトにコンテンツを追加したりするときに強力な機能を提供します。この記事では、このmoduleの利点を見ていき、どのようにしてブログを作成できるのかを学んでいきます。

View demo / Source code

Getting started

Installation

contentモジュールを使い始めるには、まずnpmかyarnを使ってモジュールをインストールする必要があります。

npm install @nuxt/content
// or
yarn add @nuxt/content

そして、nuxt.configファイル内のmodulesプロパティに追加します。

nuxt.config.js

export default {
  modules: [
    '@nuxt/content'
  ]
}

新しいプロジェクトを create nuxt-app で作成した場合、contentモジュールを選んで追加できます。

Let's create our markdown page

contentモジュールは content/ ディレクトリのファイルを読み込むことで動作します。

mkdir content

プロジェクトを create nuxt-app で作成した場合、content/ディレクトリはすでに作成されています。

ブログの記事を追加できるarticles/ ディレクトリを作成してみましょう。

mkdir content/articles

contentモジュールは、markdown、csv、yaml、json、json5、またはxmlを解析できます。Markdownファイルを使って最初の記事を作ってみましょう。

最初の記事を作成してみましょう。

touch content/articles/my-first-blog-post.md

これでブログ記事のタイトルとテキストを追加できるようになりました。

# はじめてのブログ

contentモジュールを使用した私の最初のブログ記事へようこそ

markdownでは、#を使用して<h1>のタイトルを作成します。 それとブログタイトルとの間にスペースを残してください。markdownでの記述の詳細については、基本構文ガイドを参照してください。

Displaying your content

ページ内にコンテンツを表示するには、ページの前にアンダースコア(_)を付けて、動的ルーティングを使用します。ブログフォルダ内に_slug.vueという名前のページコンポーネントを作成することで、vueルーターが提供するparams.slug変数を使って各記事の名前を取得できます。

touch pages/blog/_slug.vue

ページがレンダリングされる前に記事の内容を取得するために、ページコンポーネントでasyncDataを使うことができます。変数$contentを使うことでcontextからコンテンツにアクセスできます。動的なページを取得したいので、params.slugでどの記事を取得するかを知る必要があります。これは contextを通して利用可能です。

pages/blog/_slug.vue

<script>
export default {
  async asyncData ({ $content, params }) {
    // ここで記事を取得します
  }
}
</script>

関数 asyncData の内部では article という名前の変数を作成し、await に続いて $content を使ってコンテンツを取得します。フェッチしたい内容を $content に渡す必要があります。これは私たちの場合、記事のフォルダと、URLのパラメータから取得したslugです。そして、フェッチメソッドを最後までチェインして、フェッチの結果を含む記事を返します。

pages/blog/_slug.vue

<script>
export default {
  async asyncData ({ $content, params }) {
    const article = await $content('articles', params.slug).fetch()

    return { article }
  }
}
</script>

コンテンツを表示するために、documentpropに返された変数を渡しつつ、 <nuxt-content /> コンポーネントを使用しています。この例では、よりセマンティックなHTMLとするために、HTMLのarticleタグでラップしていますが、お好みであればdivや他のHTMLタグを使用できます。

pages/blog/_slug.vue

<template>
  <article>
    <nuxt-content :document="article" />
  </article>
</template>

これで、開発サーバーを実行して、ルート http://localhost:3000/blog/my-first-blog-post に行くことができ、マークダウンファイルからコンテンツを見ることができます。

content from markdown

Default Injected variables

nuxt/contentモジュールを使用すると、テンプレート内でアクセスしたり表示できるinjected variablesにアクセスできます。 ドキュメントのデフォルトinjected variablesを見てみましょう。

  • body: bodyテキスト
  • dir: ディレクトリ
  • extension: ファイル拡張子 (今回の例では.md)
  • path: ファイルパス
  • slug: ファイルslug
  • toc: 目次のための配列
  • createdAt: ファイル作成日
  • updatedAt: ファイル更新日

先ほど作成した変数articleを使うことで、これらの変数にアクセスできます。articleは、私たちがアクセスできるこれらのさまざまな変数をすべて含んだオブジェクトです。<pre>タグを使って出力することでそれらを調べてみましょう。

pages/blog/_slug.vue

<pre> {{ article }} </pre>

このページでは、空の配列である変数プロパティを持つオブジェクトと、h1タグとpタグ、その他の情報を持つbody変数があるとわかります。 下にスクロールしていくと、アクセスできる他のすべての変数があるとわかります。

"dir": "/articles",
"path": "/articles/my-first-blog-post",
"extension": ".md",
"slug": "my-first-blog-post",
"createdAt": "2020-06-22T10:58:51.640Z",
"updatedAt": "2020-06-22T10:59:27.863Z"

これは、変数articleに続いて私たちが使用したいものを使用することで、これらの変数にアクセスできることを意味します。たとえば、article.updatedAtは私たちに投稿が最後に更新された日付を与えます。

pages/blog/_slug.vue

<p>記事の最終更新日: {{ article.updatedAt }}</p>

ご覧のように、date型そのままの出力は人間に優しいものではありません。日付を取得し、年、月、日のオプションを指定して新しい日付を返すメソッドを作成することで、これをフォーマットできます。

pages/blog/_slug.vue

methods: {
    formatDate(date) {
      const options = { year: 'numeric', month: 'long', day: 'numeric' }
      return new Date(date).toLocaleDateString('en', options)
    }
 }

そして、テンプレートの中でformatDateメソッドを使用して、コンテンツから取得した日付を渡すと、きれいにフォーマットされた日付を返してくれます。

pages/blog/_slug.vue

<p>記事の最終更新日: {{ formatDate(article.updatedAt) }}</p>

Custom Injected variables

YAMLフロントマターのブロックをmarkdownファイルに追加することで、カスタムしたinjected variablesを追加することもできます。それはファイルの最初にあり、---の間に設定された有効なYAML形式でなければなりません。これは記事のタイトル、説明、画像などのSEO用変数を追加するのに便利です。

content/articles/my-first-blog-post.md

---
title: はじめてのブログ記事
description: ブログ作成のための@nuxt/contentの使い方を学ぶ
img: first-blog-post.jpg
alt: my first blog post
---

これで、title、description、img、alt変数を設定できたので、articleオブジェクト変数を使用してアクセスできます。

pages/blog/_slug.vue

<template>
  <article>
		<h1>{{ article.title }}</h1>
		<p>{{ article.description }}</p>
		<img :src="article.img" :alt="article.alt" />
		<p>記事の最終更新日: {{ formatDate(article.updatedAt) }}</p>

    <nuxt-content :document="article" />
  </article>
</template>

Styling our markdown content

このページを検査するとmarkdownの内部に書かれたすべてのものがnuxt-contentクラスを持つdivの内部にラップされていることがわかります。これは、nuxt-contentクラスでラップすることにより、markdownファイルから来るすべての要素に簡単にスタイルを追加できることを意味します。

pages/blog/_slug.vue

<style>
.nuxt-content h2 {
  font-weight: bold;
  font-size: 28px;
}
.nuxt-content h3 {
  font-weight: bold;
  font-size: 22px;
}
.nuxt-content p {
  margin-bottom: 20px;
}
</style>

YAMLフロントマターから来る他のすべてのタグは、TailwindCSSを使用するか、スタイルタグにCSSを追加することで、通常通りのスタイルできます。

⚠️ スコープされたスタイルはnuxt-contentでは動作しませんので、スタイルタグで追加する場合は、スコープされたスタイルを使用しない方が良いです。ここでスタイルを追加するか、cssフォルダー内のグローバルスタイルとして追加できます。

マークダウンタグは正しいタグに変換され、2つの <h1> タグがあることを意味します。今、私たちのマークダウンファイルから1つを削除しなければなりません。

Adding an icon to our headings anchor

<h2>タグの中には<a>タグとhrefを持つ<a>タグがあり、その中には iconicon-link クラスを持つ span タグがあることに注目してください。これはページのそのセクションへのリンクに便利です。現在、見出しのリンクは空で、かつ非表示になっています。スタイルを追加してみましょう。iconクラスを使って、アイコンの背景画像としてsvgを追加できます。まず、SVGをアセットフォルダーに追加する必要があります。この例では、svgフォルダーに追加したSteve Schoger's Hero Icons.のアイコンを使用しています。

pages/blog/_slug.vue

.icon.icon-link {
  background-image: url('~assets/svg/icon-hashtag.svg');
  display: inline-block;
  width: 20px;
  height: 20px;
  background-size: 20px 20px;
}

Add a table of contents

自動生成されるtoc変数は、ブログ記事に目次を追加することを可能にします。ブログ記事に見出しを追加してみましょう。

## This is a heading
This is some more info

## This is another heading
This is some more info

これで、これらの新しい見出しが toc配列の中に、id、深さ、テキストを伴って表示されるようになりました。深さの値は見出しタグの値を参照するので、<h2> の値は2、<h3> の値は3となります。

content/articles/my-first-blog-post.md

## This is a heading
This is some more info

### This is a sub heading
This is some more info

### This is another sub heading
This is some more info

## This is another heading
This is some more info

tocの IDとテキストにアクセスできるようになったので、これらをループさせてそれぞれを出力し、<NuxtLink> コンポーネントを使ってリンク先のセクションのIDにリンクさせることができます。

pages/blog/_slug.vue

<nav>
    <ul>
      <li v-for="link of article.toc" :key="link.id">
         <NuxtLink :to="`#${link.id}`">{{ link.text }}</NuxtLink>
      </li>
    </ul>
</nav>

これでToCリンクが機能し、クリックするとドキュメントの正しい部分に移動します。contentモジュールは自動的に各見出しにidとリンクを追加します。開発ツールでマークダウンファイルの見出しの1つを検査すると、<h2>タグにidがあるとわかります。これは tocで見つかったのと同じidで、基本的には tocが正しい見出しにリンクする方法です。

ダイナミッククラスを使って見出しの深さに基づいて見出しクラスをスタイルすることで、これをさらに改善できます。リンクの深さが2の場合はy軸にパディングを追加し、深さが3の場合は左のマージンと下のパディングを追加します。ここではTailwindCSSクラスを使用していますが、カスタムクラス名やスタイルを自由に使用してください。

pages/blog/_slug.vue

:class="{
     'py-2': link.depth === 2,
     'ml-2 pb-2': link.depth === 3
}"

Use HTML into your markdown files

ときどき、マークダウンファイルにHTMLを追加したくなることがあります。いくつかのクラスを持つdivを追加して、背景色が青、テキストは白、いくつかのパディングと底辺にマージンを持つようにしてみましょう。

content/articles/my-first-blog-post.md

<div class="bg-blue-500 text-white p-4 mb-4">
  This is HTML inside markdown that has a class of note
</div>

Adding a Vue component

また、マークダウンファイル内にVueコンポーネントを追加することもできます。つまり、infoやアラートボックスのようなコンポーネントを再利用する場合、必要なスタイルのコンポーネントを作成し、スロットとしてテキストを渡すことができます。

nuxt.configファイルでプロパティ componentstrue に設定することで、アプリケーションにコンポーネントを追加できます。(v2.13以降)

nuxt.config.js

export default {
	components: true
}

コンポーネントの自動インポートは、componentsフォルダ内にglobalフォルダを追加してグローバルに登録しない限り、<nuxt-content>では機能しません。

mkdir components/global

このフォルダ内にInfoBoxコンポーネントを作ってみましょう。

components/global/InfoBox.vue

<template>
  <div class="bg-blue-500 text-white p-4 mb-4">
    <p><slot name="info-box">default</slot></p>
  </div>
</template>

Then in our markdown these components will be available without having to import them. これでmarkdownファイル内で、import文を書く必要もなくコンポーネントを利用できます。

content/articles/my-first-blog-post.md

<info-box>
  <template #info-box>
    This is a vue component inside markdown using slots
  </template>
</info-box>

グローバルコンポーネントはアプリケーション全体で利用できるようになるので、globalフォルダーにコンポーネントを追加する際には注意が必要です。 これは、使用される場合にのみ追加されるcomponentsフォルダーにコンポーネントを追加する場合とは動作が異なります。

Adding an Author component with props

YAMLプロパティの他の利点は、propsを通してコンポーネントで利用できるようにすることです。たとえば、著者についてのコンポーネントを持つことができ、ゲストブロガーがいる場合、著者欄を変更できます。 マークダウンファイルの中で、著者の名前、バイオ、画像を含む新しいauthorオブジェクトをフロントマッターに追加できます。

content/articles/my-first-blog-post.md

---
author: 
	name: Benjamin
	bio: All about Benjamin
	image: https://images.unsplash.com/.....
---

これでAuthorコンポーネントを作成できるようになりました。

touch components/global/Author.vue

ここでは、著者の画像、著者のタイトル、名前と著者のバイオの動的なdivを作成します。

components/global/Author.vue

<template>
  <div>
      <img :src="author.img" />
      <div>
        <h4>Author</h4>
        <p>{{ author.name }}</p>
        <p>{{ author.bio }}</p>
      </div>
  </div>
</template>

スタイルはこれらの例から削除されています、スタイルを自分で追加するか、デモコードからスタイルをコピーしてください。

そして、スクリプトタグの中に、オブジェクトであるauthorのpropsを追加し、また、設定を必須に指定できます。

components/global/Author.vue

<script>
export default {
  props: {
    author: {
      type: Object,
      required: true
    }
  }
}
</script>

コンポーネントを使用するには、マークダウンに追加してpropsを渡す必要があります。

content/articles/my-first-blog-post.md

<author :author="author" />

ここにコンポーネントを置くということは、記事ごとにそれを繰り返さなければならないということです。この場合、slugページに直接追加する方が良いでしょう。私たちは著者のpropを article.author に変更する必要があります。

pages/blog/_slug.vue

<template>
  <article>
		<h1>{{ article.title }}</h1>
		<p>{{ article.description }}</p>
		<img :src="article.img" :alt="article.alt" />
		<p>Article last updated: {{ formatDate(article.updatedAt) }}</p>

    <nuxt-content :document="article" />

		<author :author="article.author" />
  </article>
</template>

このコンポーネントをglobalフォルダからcomponentsフォルダに直接移動でき、テンプレートで使用しているようにslugページに自動インポートされます。

Adding a code block to your post

contentモジュールでは、prismJSを自動的に含めることでコードブロックのスタイルを設定できます。つまり、正しいマークダウン構文を使用してコードブロックを書くことができ、コードブロックは言語に応じてスタイリングされて表示されます。

content/articles/my-first-blog-post.md

```js
export default {
  nuxt: "is the best"
}
<p>code styling is easy</p>
}

また、コードブロックの言語の後に角括弧内でファイル名を追加することで、コードブロックのファイル名を追加することもできます。

```markdown
```js[my-first-blog-post.md]
export default {
  nuxt: "is the best"
}

ファイル名は、ファイル名クラスを持つspanに変換されます。この例では tailwind クラスを使用していますが、お好みであれば通常の CSS を使用しても構いません。

`assets/css/tailwind.css`

```css
.nuxt-content-highlight {
  @apply relative;
}
.nuxt-content-highlight .filename {
  @apply absolute right-0 text-gray-600 font-light z-10 mr-2 mt-1 text-sm;
}

prism-themesのように、異なるテーマを使用することができます。それをインストールしてから、nuxt.configファイルのcontentオプションにお好みのテーマを追加することができます。

npm install prism-themes
// or
yarn add prism-themes

そして、nuxt.configファイルのコンテンツオプションで、prismを使ってマークダウンオブジェクトを追加し、使用したいテーマを追加できます。

nuxt.config.js

content: {
  markdown: {
    prism: {
      theme: 'prism-themes/themes/prism-material-oceanic.css'
    }
  }
}

Creating a previous and next component

これでかなり完成したブログ記事が完成しましたが、ユーザーが簡単に別の記事へ移動できるようになれば素晴らしいことだと思いませんか?まず、記事を複製して3つの記事を作成しましょう。次に、前の記事と次の記事を移動するのための新しいコンポーネントを作成しましょう。

touch components/PrevNext.vue

このコンポーネントでは NuxtLink コンポーネントの中で v-if を使用して、以前のブログ記事があるかどうかを確認し、ある場合はその記事へのリンクを追加します。変数prevnextを使って記事のタイトルを出力できます。つまり、次の記事と前の記事を表示するために画像と説明文を含むカードを作成できます。この例ではタイトルだけを表示します。前の記事がない場合は、スタイルを整えるのに便利な空のスパンを表示します。次の記事へのリンクも同じようにします。

components/PrevNext.vue

<template>
  <div class="flex justify-between">
    <NuxtLink
      v-if="prev"
			:to="{ name: 'blog-slug', params: { slug: prev.slug } }"
      class="text-primary font-bold hover:underline"
    >
      {{ prev.title }}
    </NuxtLink>
    <span v-else>&nbsp;</span>
    <NuxtLink
      v-if="next"
      :to="{ name: 'blog-slug', params: { slug: next.slug } }"
      class="font-bold hover:underline"
    >
      {{ next.title }}
    </NuxtLink>
    <span v-else>&nbsp;</span>
  </div>
</template>

このコンポーネントでは、prevnext というpropsを渡して、ブログ記事のページでそれらを利用できるようにしています。

components/PrevNext.vue

<script>
export default {
  props: {
    prev: {
      type: Object,
      default: () => null
    },
    next: {
      type: Object,
      default: () => null
    }
  }
}
</script>

前の記事と次の記事を asyncData に追加することで、前の記事と次の記事を取得することができます。前の記事と次の記事を取得するために、prevnext という名前の const の配列を作成し、記事フォルダの内容をawaitします。今回はタイトルとslugだけが必要なので、only() をチェインしてタイトルとスラッグを渡すことができます。

sortBy() メソッドを使って、データを createdAt 日付の昇順でソートすることができます。次に surround() メソッドを使用し、パラメータからスラッグを渡します。

そして、記事で行ったのと同じように前の記事と次の記事を返します。

pages/blog/_slug.vue

async asyncData({ $content, params }) {
    const article = await $content('articles', params.slug).fetch()

    const [prev, next] = await $content('articles')
      .only(['title', 'slug'])
      .sortBy('createdAt', 'asc')
      .surround(params.slug)
      .fetch()

    return {
      article,
      prev,
      next
    }
  },

これで、slugページに <prev-next>コンポーネントを追加できます。

pages/blog/_slug.vue

<template>
  <article>
		<h1>{{ article.title }}</h1>
		<p>{{ article.description }}</p>
		<img :src="article.img" :alt="article.alt" />
		<p>Article last updated: {{ formatDate(article.updatedAt) }}</p>

    <nuxt-content :document="article" />

		<author :author="article.author" />

		<prev-next :prev="prev" :next="next" />
  </article>
</template>

nuxt.configファイルで components: trueを設定しているので、このコンポーネントを使用するためにインポートする必要はありません。

Working with the API

データをクエリするとき、ContentモジュールはAPIへのアクセスを提供してくれるので、何が返されているかを直接問い合わせることができます。開発者モードでは、以下のURLでAPIにアクセスできます。http://localhost:3000/_content/。私たちの例では、記事はarticlesと呼ばれるフォルダにあるので、このURLは空になります。

slugの名前を入れれば個別の記事が見れるようになっていますhttp://localhost:3000/_content/articles/my-first-blog-post

JSON Viewer AwesomeのようなChromeの拡張機能を使うと、結果をよりよく見ることができます。

これで、URLで結果を直接クエリし、結果をJSONとして表示できます。これを使用して、すべてのブログ投稿のリストを含むブログインデックスページを作成できます。APIを使用して、利用可能なものを確認できます。私たちとブログのインデックスページでは、title、description、img、slug、authorのみを返します。それがどのようなものか見てみましょう。

http://localhost:3000/_content/articles?only=title&only=description&only=img&only=slug&only=author

List all the blog posts

ブログインデックスページを作成して、ブログ投稿を一覧表示できるようになりました。すでにインデックスページが作成されているので、このページ内のすべてのデモコードを削除する必要があります。

$contentparamsasyncData関数のコンテキストに渡すと、定数articlesを宣言して、articlesの引数を$contentに渡し、返されるコンテンツをawaitします。 articlesはparamsからのslugです。 APIからテストしたため、 only()を使用してタイトル、説明、img、slug、authorを取得できます。これにより、必要なものが正確に得られます。 sortBy()を使用して、createdAtを指定し日付の昇順で並べ替えてから、 fetch()で記事を返すことができます。

pages/blog/index.vue

<script>
export default {
  async asyncData({ $content, params }) {
    const articles = await $content('articles', params.slug)
      .only(['title', 'description', 'img', 'slug', 'author'])
      .sortBy('createdAt', 'asc')
      .fetch()

    return {
      articles
    }
  }
}
</script>

これで、他のデータプロパティと同じように記事を利用できるようになったため、 v-forを使用してテンプレートで使用し、すべての記事をループして記事のタイトルと著者名、更新日、説明、そして画像を出力できます。<NuxtLink>コンポーネントを使用して記事のslugにリンクします。

pages/index.vue

<template>
  <div>
    <h1>Blog Posts</h1>
    <ul>
      <li v-for="article of articles" :key="article.slug">
        <NuxtLink :to="{ name: 'blog-slug', params: { slug: article.slug } }">
          <img :src="article.img" />
          <div>
            <h2>{{ article.title }}</h2>
            <p>by {{ article.author.name }}</p>
            <p>{{ article.description }}</p>
          </div>
        </NuxtLink>
      </li>
    </ul>
  </div>
</template>

Using the where query to create an Author page

contentモジュールでは、whereクエリを使って結果をフィルタリングすることもできます。著者の詳細とその著者によるすべての投稿を表示する著者ページを持つことができます。

touch pages/blog/author/_author.vue

前回と同じように、データを取得するためにasyncDataを使用しますが、今回はwhere()メソッドを追加します。ここでは、paramsで指定した著者名と同じ著者名の投稿を取得したいと思います。

例:

http://localhost:3000/_content/articles?author.name=Maria

作成者にオブジェクトを使用しているので、nuxt.configファイルのcontentプロパティにオプションとしてnestedPropertiesを追加して、クエリを実行したいものを渡します(ドット表記のクエリの場合のみ)。

nuxt.config.js

export default {
	content: {
	  nestedProperties: [
	    'author.name'
	  ]
	}
}

ご覧のように、私たちはすべてのデータを取得していますが、著者であるMariaのデータのみを取得しています。もし大文字を使わずにmariaを使っていたら、何も返ってきません。そこで、$regexを使って大文字のままにすることができます。

そして、このページに表示したいすべての詳細を取得します。先ほどの例では only() メソッドを使って必要なものを返しましたが、かなり多くのコンテンツを必要とするので、代わりに without() メソッドを使って、返したくないもの、つまり投稿の本文を渡すことができます。

pages/blog/author/_author.vue

<script>
export default {
  async asyncData({ $content, params }) {
    const articles = await $content('articles', params.slug)
      .where({
        'author.name': {
          $regex: [params.author, 'i']
        }
      })
      .without('body')
      .sortBy('createdAt', 'asc')
      .fetch()

    return {
      articles
    }
  }
}
</script>

配列を使って without() メソッドにbody以外のものを渡すことができます。

without(['body', 'title'])

その後、これらのデータを使用して、著者名とバイオだけでなく、各投稿を示す素敵な著者ページを作成できます。

pages/blog/author/_author.vue

<template>
  <div>
    <h1>Author: {{ articles[0].author.name }}</h1>
    <p>Bio: {{ articles[0].author.bio }}</p>
    <h3>Here are a list of articles by {{ articles[0].author.name }}:</h3>
    <ul>
      <li v-for="article in articles" :key="article.slug">
        <NuxtLink :to="{ name: 'blog-slug', params: { slug: article.slug } }">
          <img :src="article.img" :alt="article.alt" />
          <div>
            <h2>{{ article.title }}</h2>
            <p>{{ article.description }}</p>
            <p>{{ formatDate(article.updatedAt) }}</p>
          </div>
        </NuxtLink>
      </li>
    </ul>
  </div>
</template>

この例ではすべてのスタイルが削除されていることに注意してください。あなた自身でページのスタイルを設定するか、デモコードからスタイルをコピーできます。

日付をフォーマットするために、先ほど作成したメソッドを追加できます。

pages/blog/author/_author.vue

methods: {
    formatDate(date) {
      const options = { year: 'numeric', month: 'long', day: 'numeric' }
      return new Date(date).toLocaleDateString('en', options)
    }
  }

そして、もちろんブログ記事から新しい著者ページへのリンクもしておきましょう。

components/Author.vue

<NuxtLink :to="`/blog/author/${author.name}`">
    <img :src="author.img" />
      <div>
        <h4>Author</h4>
        <p>{{ author.name }}</p>
        <p>{{ author.bio }}</p>
      </div>
</NuxtLink>

Add a search field

Nuxt/contentモジュールでは、search()メソッドを使って記事を検索できます。

まず、検索コンポーネントを作成してみましょう。

touch components/AppSearchInput.vue

次に、空の文字列として始まるsearchQueryを返すdataプロパティと、空のArticles配列を追加します。 次に、Vueのwatchメソッドを使用して、searchQueryの引数を渡すsearchQuery関数を監視します。 searchQueryがない場合、articles配列は空であり、returnを呼び出すだけです。 そうでない場合は、記事を取得して、 $ contentが記事を渡すのを待ちます。 これで、 limit()メソッドを使用して返される結果の数を制限できます。次に、 search()メソッドを使用してsearchQueryを引数として渡し、 fetch()メソッドを最後までチェーンします 。

components/AppSearchInput.vue

<script>
export default {
  data() {
    return {
      searchQuery: '',
      articles: []
    }
  },
  watch: {
    async searchQuery(searchQuery) {
      if (!searchQuery) {
        this.articles = []
        return
      }
      this.articles = await this.$content('articles')
        .limit(6)
        .search(searchQuery)
        .fetch()
    }
  }
}
</script>

次に、テンプレートに入力を追加し、v-modelを使ってそれをSearchQueryのデータプロパティに接続する必要があります。次に、記事がある場合は v-for を使って記事をリストアップし、<NuxtLink> コンポーネントを使って記事にリンクします。

components/AppSearchInput.vue

<template>
  <div>
    <input
      v-model="searchQuery"
      type="search"
      autocomplete="off"
      placeholder="Search Articles"
    />
    <ul v-if="articles.length">
      <li v-for="article of articles" :key="article.slug">
        <NuxtLink :to="{ name: 'blog-slug', params: { slug: article.slug } }">
          {{ article.title }}
        </NuxtLink>
      </li>
    </ul>
  </div>
</template>

これで、コンポーネントをページの任意の場所に追加して使用できるようになりました。

pages/_slug.vue

<AppSearchInput />

このページの改善されたスタイリングについては、デモコードを参照してください。また、検索コンポーネントを含む追加されたヘッダーコンポーネントと同様に、著者とインデックスページに表示されます。

Live editing our content

私たちのブログは本当に素晴らしく、ページ上のコンテンツを修正する必要がある場合は、ライブエディット機能によってブラウザ上で直接修正できます。あなたがしなければならないことは、開発モードでページをダブルクリックするだけです。すぐにライブエディット画面が開きます。ここでは、あなたのテキストとフロントマターのいずれかを変更できます。グローバルコンポーネントフォルダーにあるコンポーネントを追加できます。クリックするだけでブラウザ上で変更をライブで見ることができ、エディターやコンソールでファイルが変更されて保存されたことを確認できます。

Generating our content

新しいブログをデプロイしたい場合は、nuxt buildnuxt export コマンドを実行します。nuxt build コマンドはアプリをビルドし、すべてのwebpackアセットを追加して.jsバンドルを作成します。次にnuxt exportコマンドを実行すると、html、css、js、画像を静的アセットとしてエクスポートします。クローラーがすべてのリンクをクロールして動的なルートを生成してくれるので、ルートプロパティを追加したり、新しいページを取得するために何かをする必要がないことに気づくでしょう。

次に nuxt serve コマンドを使うことで、デプロイ前にブラウザで確認できるように、本番用の静的サイトを提供できます。

コンテンツをビルドから分離することで、新しいマークダウンページを追加できます。nuxt build を実行する必要があるのは、.vueページやコンポーネント、またはコンテンツフォルダーにないものを変更する場合だけです。(コンテンツを変更するたびにnuxt buildする必要はない)

Conclusion

contentを使った作業はとても楽しく、あなたができることはたくさんあります。あなたの作品をDiscordチャンネルのshowcaseで紹介することを忘れないでください。まだ登録していませんか?これからもNuxt.jsの新しいコンテンツや機能をどんどんリリースしていきますので、今がチャンスです! sign upお楽しみに。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment