🙌

GatsbyからNuxtJSに移行してみた

2021/08/12に公開

flutter-study.dev

「Flutterで始めるアプリ開発」というWebサイトを公開している。
もともとはGatsbyでSSGしていたが、Vueを使う必要が出てきたので、勉強がてらNuxtJSへと移行してみた。

https://www.flutter-study.dev/

Markdownで記事を書く

Gatsbyの時は@rocketseat/gatsby-theme-docsを使っていて、Markdownで各記事を書いていた。

https://github.com/Rocketseat/gatsby-themes/tree/main/@rocketseat/gatsby-theme-docs

NuxtJSでもMarkdownで書いた記事はそのまま再利用したいので、@nuxt/contentを使うことにした。

https://content.nuxtjs.org/

@nuxt/content

インストール

導入は簡単でパッケージをインストールしてnuxt.config.jsでモジュールを追加すればOK。

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

Markdown

Markdownファイルはcontentディレクトリ内に適宜配置していく。
配置したディレクトリ・ファイル名がそのままURLとなり、Gatsbyの時と同じなのでURLを変換する必要はなかった。

各記事のタイトルや説明など、メタ情報的なものはYAMLで先頭に記述できる。
Gatsbyの時も同じ要領で書かれていたので再利用できた。

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

記事の順番

Gatsbyの時は、サイドバーに記事の一覧を表示していて、その情報を元に前後の記事を判定していた。

@nuxt/contentでは各記事にメタ情報的なものを記述しておくことで、記事の順番を管理することが出来る。
具体的には、次のようにslugという情報をpage-01,page-02,...といった連番で各記事に記述すると、その順番で記事を取得することが出来る。

---
title: Introduction
description: Learn how to use @nuxt/content.
slug: page-01
---
const pages = await this.$content('pages').sortBy('slug').fetch()

また、前後の記事を簡単に取得することも出来る。
各記事のslugpage-01,page-02,page-03と記述してあれば、page-02から前後のpage-01page03の記事が取得できる。

const [prev, next] = await this.$content('pages').surround('page-02').fetch()

Static Site Generation

nuxt generate

全て静的ファイルとしてFirebase Hostingにアップロードしている。

NuxtJSでSSGするのは非常に簡単で、nuxt.config.jstarget: trueと設定し、nuxt generateコマンドを実行するだけで良い。

Composition API

しかし、Composition APIを使いつつSSGする時に地味に困った点がある。

もしかしたら解決方法があるのかもしれないが、useFetch()を使っている箇所がNuxtLinkでページ遷移した際にpayloadがうまく読み込まれなかった。

export default defineComponent({
  setup () {
    const context = useContext()
    const route = useRoute()
    const doc = ref<IContentDocument>()
    
    useFetch(async () => {
      const $content = context.content
      const { page } = route.value.params
      doc.value = await $content(page).fetch()
    })
    return { doc }
  }
})
</script>

しかたなく、Composition APIを使うのをやめて、asyncDataで各記事のデータ取得処理を記述することとした。

export default Vue.extend({
  async asyncData ({ $content, params }): Promise<Data> {
    const { page } = params
    const doc = await $content(page).fetch()
    return { doc }
  }
})

そもそも、NuxtLinkを使わずにaタグを使えば良いところではあるが、@nuxt/contentの内部処理的に、相対パスであればNuxtLinkに変換する仕様となっていた。
なので、@nuxt/content自体の実装を変更するのは手間だったので、ひとまずComposition APIを使わない方向に。。。

    /**
     * Replace a tag with nuxt-link if relative
     */
    if (node.tagName === 'a' && (node.properties.href || '').startsWith('/')) {
      node.tagName = 'nuxt-link'
      node.properties.to = node.properties.href
      delete node.properties.href
    }

https://github.com/nuxt/content/blob/0b6a7be6f6287cde880b0d37c88e6461efa167ff/packages/content/parsers/markdown/compilers/json.js#L16-L20

TypeScript

create-nuxt-appを使ってNuxtJSプロジェクトを作成する時に、TypeScriptを選択できるので簡単にTypeScriptで書き始めることが出来る。

https://github.com/nuxt/create-nuxt-app

しかし、NuxtというかVue側の問題であるが、TypeScriptとの相性があまりよろしくない印象をうける。
テンプレート部分は一切型定義の恩恵が受けられないのはもちろんだが、NuxtJSのasyncDataで返す値の型は明示的に記述しないと認識されなかったりする。

最後に

NuxtJSは簡単に使うことができ、ドキュメントも充実している。
また、NuxtJS用のモジュールが充実していて、多くのケースで爆速でアウトプットできる環境が整っている。
いわゆるWebサイトであったり、個人で作る小規模なWebアプリであれば、簡単かつ爆速で作れるNuxtJSが良さそうな感じがする。

また、TypeScriptとの相性が悪い点はNuxtJS/Vueの辛いところである。
TypeScriptとの相性が悪いということは、エディタ上でもソースコードを追いにくいということになる場合が多いことになると思う。
なので、業務としてWebアプリを作る場合は個人的には避けたほうが良さそうな印象ではある。

次は、Next.jsでも使ってみる。

FlexboxとGrid Layoutを使ったCSSレイアウト入門

FlexboxとGrid Layoutを使ったCSSレイアウトに関して書籍にまとめました。

作って学ぶWebサイト制作

Webサイト制作の入門サイトを作っています。

https://web-study.dev/

Discussion