VitePressでのブログ構築
ブログをVitePressで作りました。
VitePressでブログを構築するにあたって、いくつかTipsというかノウハウがあったので共有です。
とはいえ内部の動作まで追っかけきった完璧な記事ではなく、「なんかこうやったら動いた」くらいの温度感なので、その点ご容赦ください。
構成
Github Pages で公開しています。
レポジトリ: https://github.com/takyam-git/blog
特にひねりもなく、以下のドキュメントのとおりに .github/workflows/deploy.yml
を作って Github Pages で公開されるようにしています。
概念
今回は各記事を「Entry」と表現するようにしてます。
「Post」でもよかったんだけどなんとなく。
ディレクトリ構成
なにかのアプリやライブラリのドキュメントサイトとしてVitePressを利用する場合は /doc
などディレクトリを掘って対応するのが良いらしいのですが、ブログ用のレポジトリなので root に展開しています。
./
.vitepress/
theme/
Entry.vue
index.ts
style.css
config.mts
entries.data.ts
entries/
2023/
xx-xx.md
public/
images/
favicon.ico
entries.md
index.md
このへんが主要なファイル郡になってきます。
理解すべきことは以下ですかね。
-
.vitepress/
ディレクトリ内に諸々設定的なものやレイアウト的なものはつっこむ -
***.md
ファイルが自動的にルーティングされて***.html
として公開される -
public/
ディレクトリは公開ディレクトリ- たとえば
public/images
はhttps://example.com/images/
でアクセスできるようになる
- たとえば
ブログとして構築するにあたって必要な機能
「ブログ」としてやるなら以下の機能が必要なので、対応してます。
- 記事の一覧を取得して、いい感じにリスト表示する
- OGPをページに合わせて出力する
これらをどうやってVitePressで実装するのか、というのがこの記事の内容になります。
記事の一覧を取得して、いい感じにリスト表示する
ブログなので「トップページに新着記事一覧出したい」「記事一覧ページ作りたい」みたいなのが真っ先に出てきたんですが、VitePressはWordPressっぽい名前の割に、特にブログ用ってわけでもないのでデフォルトで良い感じのものがあるわけじゃないっぽいです。
基本的な概念として 元々用意されてるコンポーネント郡は Frontmatter とよばれる、markdownファイルの先頭に記述するYAMLのデータをレンダリングすることに特化してます。
で、このFrontmatterの部分はYAMLベタ書きです。
たぶん動的に操作する方法は無さそう?
デフォルトで用意されてる Features はYAMLをベタ書きするしかない
たとえば、トップページにいい感じに新着記事の一覧を出せそうな Features をFrontmatterで以下のように設定できます。
features:
- title: Feature A
details: Lorem ipsum dolor sit amet, consectetur adipiscing elit
- title: Feature B
details: Lorem ipsum dolor sit amet, consectetur adipiscing elit
- title: Feature C
details: Lorem ipsum dolor sit amet, consectetur adipiscing elit
これで3つのカードがトップページに表示されて、固定の要素をいい感じに表示する機能は用意されてます。
自動で新着記事の一覧を出力するには
が、私がやりたいのは自動で新着記事の一覧を出力したいので、このFrontmatterを使ったYAML戦法は使えません。
そこで2つのものから新着記事一覧を実装することになりました。
- 記事一覧を生成する
entries.data.ts
-
entries.data.ts
を元にレンダリングするVueコンポーネント
entries.data.ts
とは
ファイル名はなんでもいいんですが、記事一覧として entries.data.ts
を作っています。
中身は非常にシンプルで、 entries
ディレクトリ以下のMarkdownファイルの一覧を取得して返す、といった感じです。
createContentLoader()
のドキュメントは以下。
こんな感じで「記事一覧のデータ」を生成することで、各MarkdownファイルやVueコンポーネントから記事一覧を取得できるようになります。
entries.data.ts
をもとに新着記事一覧をレンダリングする
entries.data.ts
で記事一覧が取得できるので、適当にレンダリングしてあげます。
<script lang="ts" setup>
const { data as entries } from '.vitepress/entries.data.ts'
const newEntries = [...entries].reverse().slice(0, 6)
</script>
<template>
<ul>
<li v-for="(entry, index) in newEntries" :key="index">
<span v-if="entry.frontmatter.title">{{ entry.frontmatter.title }}</span>
</li>
</ul>
</template>
OGPをページに合わせて出力する
たとえばfaviconなどの固定のタグを <head>
内に出力するのは、 .vitepress/config.mts
に以下のような感じで設定してあげればよいです。
import { defineConfig } from "vitepress";
export default defineConfig({
head: [
["link", { rel: "icon", href: "/favicon.ico" }],
["meta", { property: "og:image", content: "https://example.com/og.png" }],
["meta", { property: "og:site_name", content: "your site name" }],
["meta", { property: "twitter:card", content: "summary" }],
["meta", { property: "twitter:site", content: "@*******" }],
]
});
OGPタグも og:site_name
みたいなサイト共通の部分だけでいいならはこんな感じでいいと思うんですが、記事ページごとに og:title
とか og:url
は出し分けたいのが人情だと思うので対応しました。
transformHead()
を使ってページごとのヘッダーカスタマイズを行う
transformHead()
ってのを使うと、ページごとのヘッダーカスタマイズができます。
import { defineConfig } from "vitepress";
export default defineConfig({
async transformHead(context) {
return [
["meta", { property: "og:title", content: context.pageData.title }],
["meta", { property: "og:url", content: `https://example.com/${context.pageData.filePath
.replace(/^\//, "")
.replace(/\.md$/, ".html")}` }],
];
},
});
第1引数の context
からいくつか情報が取得できるので、それをmetaタグとして設定しています。
- URLを直接
context
から取得する方法はなさそうだったので、適当に変換してあげる - Frontmatter の情報は
context.pageData
に入ってそうだったのでそれを使う
その他ハマったところ
- ESMにする必要があるので
package.json
には"type": "module"
が必要- これないとエラーになるはず
-
window
はクライアントサイドしかアクセスできないので<ClientOnly>
タグで囲んであげる- 今回は投稿エディタ周りで
window.localStorage
やらにアクセスする必要があって、その辺は<ClientOnly>
で囲んだ親階層を用意することで対応しました -
<ClientOnly>
のドキュメントは以下
- 今回は投稿エディタ周りで
さいご
まだ完成したとは言えないですし、VitePress使いこなせてもないんですけどVitePressのデフォのテーマの出来がよいので、何もしなくてもライトテーマ/ダークテーマ対応のシャレオツブログがGithubPagesで公開できます。
Github Pagesで作れるということは、無料でブログが作れるということなので、いいことです。
その他、記事の投稿をできるようにしたり、まだ作りかけの機能がいくつかあるので、そのうちこの記事を拡充する形で追記していこうと思います。
参考
-
https://nshmura.com/posts/migration-to-vitepress/
- めっちゃ参考になりました。ありがとうございました。
Discussion