VuePress から Nuxt3 に移行しました

移行のモチベーション

  • なんとなく React が分かってきて仕事で使いたいと思った
  • 一方 Vue は趣味で使い続けたい
  • Nuxt3 使ってみたい
  • VuePress だとデザインとかマークアップの修正が行いづらい

みたいな考えがあったのですがそこまで作りたいものも今のところないのでとりあえず放置気味になっていたこのサイトを改修しようと思いました。
一応 VuePress のままでも問題はなかったが、デザインも直したい箇所がたくさんあったので Nuxt3 への移行を決めました。

移行作業

移行作業では下記を目標として作業しました。

  • ダークモードとテーマ切替えの実装
  • デザインをシンプルにまとめる

インストール・環境設定

基本的にはNuxt3のサイトに書いてあるとおり進めていった。
Beta だった時期に試した時はnuxiが呼び出せなかったりバグがあったがそういったバグとかも治っていてnpx nuxi init nuxt-appすんなりインストールできた。
結構普段はcreate-**-appとか使わずにマニュアルで環境を作ってしまうことが多いけどコマンド一つで環境が整うのめっちゃ楽でいいですね。

Nuxt は環境をセットアップしたあとにインストールコマンドが必要なのでnpm iしておく。

log
Nuxt CLI v3.0.0-rc.4

  > Local:    http://localhost:3000/
  > Network:  http://192.168.10.18:3000/

爆速で localhost が立ち上がってびっくりするけどリンクを開くとロード画面が開いて若干待たされる。
それでも開発環境の立ち上がりは十分早いのでストレスは無い。

※画像はv3.0.0-rc.4時点でのロード画面v3.0.0-rc.5だとまた違うロード画面になっていた。

こういう細かいところもちゃんと作り込んでいるので Vue 関連のプロジェクトは好きだなと思う。

Vue3 の壁

とりあえずで進めたはいいが前に Vue を書いたのが 2.x の頃なので書き方を忘れていたり新しい API が分からなかったりで結構苦労した。
Composition API っていうの使っておけばいいんでしょ?というレベル感でとりあえず進めていく。

レイアウトの作成

とりあえずレイアウトから作っていこうと思ってレイアウトの編集から始めた。
基本的にはlayout > default.vueに基本レイアウトを作成して、それ以外のレイアウトが必要な場合はlayoutディレクトリで別の名前を作っていく感じらしい。
今回のリニューアルの際にダークモードも自分で実装しようと思ったのでレイアウト側でそれを定義する。

基本的なやり方は前に記事にしたものを見返しながら設定していった。

  • CSS 側で配色を定義
  • それを変更するためのロジックを用意
  • 別途 meta を変更するためのロジックを用意

という感じなのでそれを進めていく。
記事を書いている時点でのレイアウトファイル最新版

一応それっぽくダークモードとの切り替えができているが端末のテーマがダークモードだった場合にライトモードからダークモードへの切り替わりでちらつきが発生してしまうので後々直しておこうと思う。

※後にNuxt ColorModeというパッケージを入れて修正しています。

以前のサイトからコンポーネントを移植

以前のサイトもそこまでコンテンツは無いので移植するコンポーネントも多くないのでサクッと終わった。
Zenn の記事データを取ってくるコンポーネントJSON ファイルを元に制作物一覧を生成するコンポーネントぐらいなのでそこまでトラブルも起きずに Vue2 から 3 への変更ができた。

Zenn の記事データを取ってくるコンポーネントについて以前はaxiosを使っていたが別にfetchでいいじゃんということでそこだけは書き換えをおこなったけどほとんど昔のコードとロジックは同じ。

記事データの移行

記事データを移行するにあたって VuePress のように自動でパースはしてくれなさそうなので、Markdown パーサを入れようと思っていたが
どうやらNuxt Contentというものがあるのを発見した。Nuxt のcontentディレクトリについての説明はこっち

サクッと読んだ感じcontentディレクトリに.mdファイルを入れてうまいことやると記事(マークアップ)を生成してくれそうなのでこれを使うことにする。

パッケージのインストール

別途@nuxt/contentのパッケージが必要そうなのでインストールする必要がある。

sh
npm install --save-dev @nuxt/content

でインストールしてnuxt.config.tsに下記の項目を追加する。

tsnuxt.config.ts
import { defineNuxtConfig } from 'nuxt'// https://v3.nuxtjs.org/api/configuration/nuxt.configexport default defineNuxtConfig({  modules: [    '@nuxt/content'  ],  content: {    // https://content.nuxtjs.org/api/configuration/  },  app: {    head: {      meta: [        { name: 'color-scheme', content: 'light dark' },      ],      link: [        { rel: 'stylesheet', href: '/assets/css/main.css' },      ],    }  }})

とりあえず構成としてはこのようにしたいので設定をしていく。

txt
pages
┗ post
  ┣ index.vue   // ブログ一覧
  ┗ [...slug].vue  // 個別記事

/content/ディレクトリもこんな感じでパスをあわせておくと良さそう。

txt
Content
┗ post
  ┗ // ...各記事データ

記事のコンテンツは[...slug].vueというファイルを作ればルーティングしてくれるらしい。
内部は下記のように<ContentDoc></ContentDoc>というコンポーネントを入れればマークアップを展開してくれる。

vue[...slug].vue
<template>  <div>    <ContentDoc></ContentDoc>  </div></template>

特にどのコンテンツをインポートするとか、API への接続とかの処理を記載しなくてもディレクトリを合わせて[...slug].vueを作るだけで記事を表示してくれるのでサクッとドキュメントページを作りたい人にも良さそう。

記事ページのカスタマイズ

デフォルトの見た目でもまあいいが一部見た目を変更したりしたいのでちょっと手を加えていく。
最初は記事ページ用の css を用意してそれを記事ページだけで読み込ませようかと思ったけど、一応ちゃんとしたやり方があったのでそっちでやっていく。

公式ドキュメントだと、/components/content/ディレクトリにcomponent sourcesを見ながら同じ名前のコンポーネントを作成すればいけそうなので試してみる。

  • Checkout the original component sources.
  • Use the exact same props.
  • Name it the same in your components/content/ directory.
  • Make it yours 🚀.
  1. 変更したいコンポーネント(タグ)を決める
  2. Prose**.vueファイルをcomponent/content/に作成
  3. component sourcesと同じprposを利用してファイルを作成する

といった感じで進めれば編集可能。

作ったコンポーネント

ハイライトの追加

ハイライトの導入はnuxt.config.ts > content > highlightからテーマを追加することで有効化できる。
しかし、下記のようなエラーが出てしまったので一旦ハイライトの指定を別のものに変えて対応した。

log
[nuxt] [request error] No grammar provided for <source.diff>
  at Registry.e._collectDependenciesForDep (/G:/dev-local/d6/node_modules/shiki-es/dist/shiki.mjs:12:24999)
  at Registry.<anonymous> (/G:/dev-local/d6/node_modules/shiki-es/dist/shiki.mjs:12:25495)
  at /G:/dev-local/d6/node_modules/shiki-es/dist/shiki.mjs:12:23016
  at Object.next (/G:/dev-local/d6/node_modules/shiki-es/dist/shiki.mjs:12:23125)
  at s (/G:/dev-local/d6/node_modules/shiki-es/dist/shiki.mjs:12:21828)

Shiki のLanguageを見てみるとdiffには対応しているようなのできちんと対応すれば表示されそう。

ダークモード時に自動でテーマを変えてほしいなー、と思っていながらドキュメントを見ると複数のハイライトテーマに対応しているようなので設定してみた。
しかしうまく設定できず、型定義を見ると公式ドキュメントのような設定はできないっぽいので一旦はコードブロック内部の背景色を固定して対応した。

tsmodule.d.ts
highlight: false | {    /**      * Default theme that will be used for highlighting code blocks.      */    theme?: Theme;    /**      * Preloaded languages that will be available for highlighting code blocks.      */    preload?: Lang[];};

Theme型はテーマ名として指定できる文字列の union 型

ダークモードの追加

最初はダークモードの実装を自分で行っていたが、Nuxt ColorModeというモジュールがあるのでそちらを利用する。
基本的な使い方としてはNuxt Contentと同じようにモジュールをインストールして、nuxt.config.tsにモジュールを追加する。

tsNuxt.config.ts
export default defineNuxtConfig({  modules: [    '@nuxt/content',    '@nuxtjs/color-mode',  ],  colorMode: {    dataValue: 'theme',  },  // ...});

colorModeの中に項目を追加することで設定を変更可能。
このモジュールはサイトのカラーモードの状態管理(設定状況をキャッシュしてくれるっぽい?)のみを行うので切り替えるテーマは自分で作成する必要がある。
モジュールはテーマに応じてhtml要素のクラス名と data 属性の値を変更してくれるのでそれに応じて CSS 変数を差し替える方法で対応するのが一番楽だと思う。

cssmain.css
:root {  --c-static-dark: #333;  --c-static-darker: #222;  --c-static-darkest: #111;  --c-static-light: #ddd;  --c-static-lighter: #eee;  --c-static-lightest: #fff;  --c-static-base-dark: #1f214e;  --c-static-base-light: #c6c8ec;  --c-static-secondary: #70909e;  --c-static-important: #db3131;}html:not([class*="dark-mode"]) {  color-scheme: light;  --c-dark: var(--c-static-dark);  --c-darker: var(--c-static-darker);  --c-darkest: var(--c-static-darkest);  --c-light: var(--c-static-light);  --c-lighter: var(--c-static-lighter);  --c-lightest: var(--c-static-lightest);  --c-base-dark: var(--c-static-base-dark);  --c-base-light: var(--c-static-base-light);  --c-secondary: var(--c-static-secondary);  --c-important: var(--c-static-important);}html.dark-mode {  color-scheme: dark;  --c-dark: var(--c-static-light);  --c-darker: var(--c-static-lighter);  --c-darkest: var(--c-static-lightest);  --c-light: var(--c-static-dark);  --c-lighter: var(--c-static-darker);  --c-lightest: var(--c-static-darkest);  --c-base-dark: var(--c-static-base-light);  --c-base-light: var(--c-static-base-dark);  --c-secondary: var(--c-static-secondary);  --c-important: var(--c-static-important);}

ちなみにこのサイトの設定は上記のような感じ(最新版
ダークモード・ライトモードで色が変わるように設定しているが、静的な値も欲しい場合もあるので:rootで一旦静的な値を定義しておいてそれをそれぞれ代入していく感じで書いています。
多分このやり方がダークモードに対応しているデザインシステムとかでも一般的だと思う(※要検証)

機能としてはそこまで大きなモジュールではないがきちんとprefers-color-mode の仕様を考慮していたり※、設定値の記憶など細かい箇所もきちんと作られているのでこのモジュールで機能が不足することはなさそう。
また、ページ個別でカラーモードを強制できる機能もあるので段階的にダークモードの導入をしていく方法なども用意されている。

prefers-color-modeには'light''dark'以外の値も入ってくる可能性があるので想定していない値が入ってきた場合のフォールバック値が必要

コンポーネント側は下記のようにuseColorModeからインスタンスを作成して<ColorModeInstance>.preferenceの値を変更することでカラーモードの変更が可能。
現在の値は<ColorModeInstance>.valueで参照できる。

vueColorMode.vue
<script lang="ts" setup>import { useColorMode } from '#imports';const colorMode = useColorMode();const toggleDarkMode = () => {  colorMode.preference = colorMode.value === 'dark' ? 'light' : 'dark';  console.log(`current theme ${colorMode.value}`);}</script>

Netlify でのデプロイ

VuePress 時代から Netlify を使っていたのでそのまま Netlify にデプロイすることにした。
Nuxt3 のドキュメントにもNetlify に関しての記述があります。

ここにも書いてある通り Netlify へのデプロイではほとんど設定する項目はありません。
既にアカウントを作成している人は Add new site からリポジトリを連携して、ビルドコマンドとパブリッシュディレクトリを追加するだけで自動的にサイトが公開されます。

特にビルド設定を変更していない場合は、build settings の項目内の Build command にnpm run buildを入れ Publish directory をdistに設定するだけで公開される。
本当にやることが無いのでデプロイについて書くことはそこまでないです。

Nitro のドキュメントにはZero-Config Providersとしてazurevercelも記載されている。
Node.js ServerのページにPM2での使い方も記載されているのでセルフホストするときなどはこっちを見ればよさそう。

最後に

というわけで VuePress から Nuxt3 への移行は完了しました。
Netlify がデフォルトで Nuxt のデプロイに対応していたり、Nuxt Content が用意されていたりしてかなりスムーズに移行ができました。
VuePress と違いレイアウトを作成する必要がありますが、リポジトリの作成からデプロイまでそこまで時間がかからないので拡張性を考えると Nuxt+Nuxt Content でのドキュメント管理の可能性を感じました。

まとめ

  • Nuxt Content が優秀
  • Nuxt3 + Netlify の組み合わせは楽

今後やりたいこと

  • 各ページへ ogp の設定
  • 記事ページに URL を入れたら ogp を展開してくれるようにしたい
post at 記事一覧へ戻る