ロゴテキスト ロゴ

    Nuxt ContentをNuxt.jsのプロジェクトに導入する(TypeScriptにも対応)

    Nuxt ContentをNuxt.jsのプロジェクトに導入する(TypeScriptにも対応)

    Nuxt.jsを使いつつ、ブログのような静的なページを作るのに適したNuxt Content

    そのNuxt ContentをNuxt.jsのプロジェクトに導入する方法を記載します

    検証した環境

    1 yarn 1.22.4
    2 nuxt 2.14.12
    3 @nuxt/content 1.13.1

    前提

    create nuxt-appを実行した際は以下のようにしました。

    TypeScriptを選びつつ、その他は必要最低限にしました。

    create-nuxt-app v3.5.2
    ✨  Generating Nuxt.js project in add-nuxt-content-sample
    ? Project name: add-nuxt-content-sample
    ? Programming language: TypeScript
    ? Package manager: Yarn
    ? UI framework: None
    ? Nuxt.js modules: (Press <space> to select, <a> to toggle all, <i> to invert selection)
    ? Linting tools: Prettier
    ? Testing framework: None
    ? Rendering mode: Universal (SSR / SSG)
    ? Deployment target: Server (Node.js hosting)
    ? Development tools: (Press <space> to select, <a> to toggle all, <i> to invert selection)
    ? Continuous integration: None
    ? Version control system: Git


    $ yarn run devを実行した時は以下のようになります

    プロジェクトを作った初期の状態

    Nuxt Contentのパッケージを追加

    Nuxt Contentのパッケージを追加します



    yarnの場合

    $ yarn add @nuxt/content

    npmの場合

    $ npm install @nuxt/content



    パッケージが導入出来ている事を確認。

    プロジェクトフォルダでnpm lsを使って@nuxt/content@<バージョン番号>が表示されればOK!

    $ npm ls --depth=0 | grep nuxt
    2:├── @nuxt/content@1.13.1  //この行が表示される
    3:├── @nuxt/types@2.14.12
    ・・・

    configファイルの編集

    Nuxt Contentを使う事をnuxt.config.jsに記載

    nuxt.config.js
    export default {
      modules: ['@nuxt/content'],
    }

    TypeScriptの場合

    TypeScriptの場合、tsconfig.jsonに追記を行う必要があります。

    公式に書いてある通り、 @nuxt/typesもしくは@nuxt/vue-appの後ろに書く必要があります

    tsconfig.json
    {
      "compilerOptions": {
        ・・・
      },
      "types": [
        "@types/node",
        "@nuxt/types",
        "@nuxt/content"  //@nuxt/typesの後に追記
      ]
    }

    「なぜ?」というのが公式に書いてあってパッと見「??」てなるんですけど

    nuxt の動作方法のため、コンテキストの $content プロパティは TypeScriptのdeclaration merging機能 を通して、nuxt Context インターフェースにマージする必要があります。型に @nuxt/content を追加すると、パッケージから型をインポートし、TypeScript が Context インターフェースに追加されたものを認識するようになります。

    インストール - Nuxt Content より

    要は $context@nuxt/typesContextインタフェースに追加して使えるようにするよ、という事を言っています。

    <script lang="ts">
    import { Context } from '@nuxt/types'
     
    export default {
      async asyncData({ $content, params }: Context) {
        const articles = await $content('articles').fetch()
    ・・・

    実際に記事を取得する部分では上記のように書きます。




    @nuxt/typesの後ろに@nuxt/contentを書かないと{ $content, params }: Contextの部分で Property '$content' does not exist on type 'Context'. と怒られます。

    Property '$content' does not exist on type 'Context'.

    サンプル用の記事を追加

    記事を書くためのフォルダを作成します。



    Nuxt Contentではデフォルトでプロジェクト直下の「content」フォルダ配下に記事のファイルを作成するルールになっています

    content/
      articles/
        sample-01.md
        sample-02.md

    articlesというフォルダ名は何でも問題なく、例えばblogsnewsといったフォルダ名もつけられます。




    マークダウンが正しく表示出来るか試したいので「sample-01.md」は以下のようにしました。

    sample-01.md
    ---
    title: sample-01のタイトル
    description: Learn how to use @nuxt/content.
    ---
     
    # h1 Heading
    ## h2 Heading
    ### h3 Heading
    #### h4 Heading
    ##### h5 Heading
    ###### h6 Heading
     
    ## Emphasis
     
    **This is bold text**
     
    __This is bold text__
     
    *This is italic text*
     
    _This is italic text_
     
    ~~Strikethrough~~
     
    ## Blockquotes
     
     
    > Blockquotes can also be nested...
    >> ...by using additional greater-than signs right next to each other...
    > > > ...or with spaces between arrows.
     
    ## Lists
     
    Unordered
     
    + Create a list by starting a line with '+', '-', or '*'
    + Sub-lists are made by indenting 2 spaces:
      - Marker character change forces new list start:
        * Ac tristique libero volutpat at
        + Facilisis in pretium nisl aliquet
        - Nulla volutpat aliquam velit
    + Very easy!
     
    Ordered
     
    1. Lorem ipsum dolor sit amet
    2. Consectetur adipiscing elit
    3. Integer molestie lorem at massa
     
     
    1. You can use sequential numbers...
    1. ...or keep all the numbers as '1.'
     
    Start numbering with offset:
     
    57. foo
    1. bar


    マークダウンの頭の部分

    ---
    title: Introduction
    description: Learn how to use @nuxt/content.
    ---

    「Front Matter」(フロントマター) と言って、静的ジェネレーターでよく使われているイメージのもので、 Front Matter | Jekyll • シンプルで、ブログのような、静的サイト


    カテゴリやタグなんかを設定する事も出来ます。




    Nuxt Contentの場合、.md 内でVueのコンポーネントが使えるため、公式のようなこんな便利な使い方も!

    何これすごいんですけど!!

    記事一覧と記事詳細ページを作る

    フォルダ階層は以下のように

    pages/
      articles/
        _slug.vue  //記事詳細
      index.vue  //記事一覧(トップページ

    「articles/」というフォルダ名はURLになります。




    ↑の場合https://hogehoge.com/articles/sample-01というURLにアクセスすると

    「sample-01.md」の内容が表示されます。


    「pages/blogs/_slug.vue」とすればhttps://hogehoge.com/blogs/sample-01に。




    とっても簡素ですがまずコード


    記事一覧

    pages/index.vue
    <template>
      <ul>
        <li v-for="article in articles" :key="article.slug">
          <nuxt-link :to="`/articles/${article.slug}`">
            {{ article.title }}
          </nuxt-link>
          <p>{{ article.updatedAt }}</p>
        </li>
      </ul>
    </template>
     
    <script lang="ts">
    import { Context } from '@nuxt/types'
     
    export default {
      async asyncData({ $content, params }: Context) {
        const articles = await $content('articles').fetch()
        return { articles }
      }
    }
    </script>

    記事詳細

    pages/articles/_slug.vue
    <template>
      <main>
        <h1>{{ article.title }}</h1>
        <nuxt-content :document="article" />
      </main>
    </template>
     
    <script lang="ts">
    import { Context } from '@nuxt/types'
     
    export default {
      async asyncData({ $content, params }: Context) {
        const article = await $content('articles', params.slug).fetch()
        return { article }
      }
    }
    </script>

    無事表示が出来ました!

    コードについて

    以下のコードで全ての記事が取得出来ます

    async asyncData({ $content, params }: Context) {
      // 記事を全て取得する
      const articles = await $content('articles').fetch()
      // 記事をdataに設定する
      return { articles }
    }

    $content('articles').fetch()$content('<content/<フォルダ名>') になります。

    例えば以下の場合は $content('blogs').fetch() になります

    content/
      blogs/
        sample-01.md
        sample-02.md

    return { articles }はNuxtのasyncData()の書き方のため詳細は省きますが、

    最終的にVueのdataに格納しています。




    記事詳細の取得は以下

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

    $content は第2引数文字列を渡す事で以下のように変換されます

    • $content('articles', 'sample-01')/articles/sample-01

    この事については Nuxt Content の公式ドキュメントに書かれています。

    引数を複数与えることもできます。 $content('article', params.slug)/articles/${params.slug} に変換されます。

    コンテンツを取得する - Nuxt Contentより



    params.slugはNuxtの書き方になるので詳細は省きますが、ファイル名を_slug.vueとしているからです。

    例えばファイル名を_file.vueとした場合、params.fileになります。






    NuxtのプロジェクトにNuxt Contentを追加するサンプルコードGitHubに上げてあります

    https://github.com/yuki-takara/add-nuxt-content-sample

    プロフィールの背景画像 プロフィール画像
    Yuki Takara
    都内でフリーランスのエンジニアをやってます。フロントとアプリ開発メインに幅広くやってます。