✌️

Vue 3 + Hugo でSPAかつSEOにつよいブログサイトを作る

2021/03/08に公開

先日、個人のブログサイトをリニューアルしました。
https://hira.page/

その際にブログを作る上でのCMS選定をかなり色々悩んだのと、Vue3を使ってみての知見をまとめます。ソースコードはGitHubにあります。一応、フロントエンドエンジニア向けの内容です。

実現したかったこと

更新の楽さ

記事はMarkdownで書きたいです。また、記事の中に画像を⌘Vでペーストしたいですね。

AMP対応

SEO・読み込み速度向上のために、ブログ記事はAMP形式で公開したいです。

SNSのOGP対応 (SeverSideRendering/StaticSiteGnerate)

Googleのクローラが賢くなっているので、SEOの目的だけでSSRする必要性は低くなっているらしいです。ただ、TwitterなどのSNSで記事画像をOGPプレビューするためには、SSRやSSGの対応が必要になります。

他のサイトに投稿した記事も自サイトのブログ一覧に表示したい

これけっこうエンジニアの悩みじゃないかと思います。いろんなサイトに記事を書いているとすべての記事を一覧で見せられる場所がないんですよね...

私の場合はZennの他にnoteQiitaに記事を投稿しているので、その記事も一覧で出したいです。

ブログCMSの選択肢

ブログ作るならとにかくWordpress!みたいな時代もありましたが、最近はメンテナンス性やセキュリティ的に避けられることも多いです。

私が今回候補にしたのは以下です。

Studio + note

はやりのNoCodeツールStudioはけっこう良さそうです。コードをかかなくてもおしゃなサイトが作れますし、ホスティング・CMS機能もあります。またnoteと連携させて自分が投稿したnote記事を一覧表示させたりもできるようですね。

私はデザイナーなので、これ使っちゃう選択肢もありでした。ただ、AMP対応や細かいカスタムがしたかったので見送りました。

Contentful

HeadlessCMSとよばれる分野のCMSサービスです。Webサービス上でMarkdownなどを使って記事が書けて、自分の静的サイトでAPIをコールして記事を取得します。類似サービスにPrismicや国産のMicroCMSがあります。

Nuxtといっしょに使われることが多いですね。いわゆるJAMstackと呼ばれて人気な構成です。最後まで残った候補ですが、最終的にカスタム性が高いHugoを選びました。

Gatsby

Facebook公式のStatic Site Generatorですね。記事をMarkdownで書いてHTMLをビルドできます。調べていると、なんだかいろいろプラグインを使いまくらなきゃいけなそうで辟易してやめました。Reactなれてる方は良いと思います。

Vue 3について

Vueのロゴ
最近やっと3がリリースされましたね。いろいろ変更点が多いので、面倒でも公式サイトをひととおり読むことはおすすめします。とくにvue-routerも記法がめちゃくちゃ変わっているので、使っている人はそちらも公式の移行ガイドを読みましょう。

私はSSRやSSGをしないのであればNuxtを使いたくない(参考)ので、公式のvue-cliを使ってサイトの骨子を作っています。

Composition API

変数やメソッドを目的ごとにブロックとしてまとめて書きやすくなります。ただ、正直小規模なWebサイトでCompositionAPIを使うと、かえってコードが読みづらく感じたので使ってません。

ちなみにTypeScriptを使おうとすると、Vue.Extend記法が削除されていたり、class-style-componenも非推奨になっており、CompositonAPIを使って書くことをじんわり強いられます。(参考)

Hugoについて

Hugoのロゴ
Go Lang 製のStaticSiteGeneratorです。類似サービスにHexo,Jekeyll,Gatsby,あと一応Nuxtなどがあります。

Markdownで記事を書き、静的なHTMLファイルをCLIでビルドします。Vueを使わなくても、Hugoだけでも十分ブログサイトを構築できます。ライターがエンジニアならHugoはかなりおすすめできます。

AMP HTMLにも変換できる

ビルドされるHTMLは自由にカスタマイズできるので、AMP形式のHTMLも当然作成できます。別ファイルで書いたSCSSを埋め込んだりすることもかんたんです。制約が多いので面倒ですが、公式ガイドラインにそって作りましょう。

私のHugoテンプレートは以下です。
https://github.com/psephopaiktes/hira.page/blob/main/hugo-amp-contents/layouts/blog/single.html

Vue 3 + Hugo の構成

ここが一応本題です。サイトは以下のようなページ構成です。

パス 概要
/ トップページ
/me アバウトページ
/contact お問い合わせページ
/blog ブログ一覧ページ
/blog/{ID} ブログ詳細ページ

記事詳細は静的なHTMLにして、他のページはSPAにする

上記ページ一覧でいうと、ブログ詳細ページのみHTMLとして生成し、ほかはVueのSPAそのままで使います。Markdown形式でブログ記事を書き、それをAMPのHTMLにHugoでビルドします。
HTMLの生成イメージ
同時にブログの一覧情報をもつRSS.xmlが生成されます。Vueのブログ一覧ページでは、そのRSSをJavascriptで読み込んで記事一覧を表示します。

note, Qiita, Zennへのリンクを投稿できる

当初はQiita,noteのAPIで、自分のアカウント記事一覧を取得する方法を考えたのですが、画像やブログの記事タグをサイト独自に設定したいと思いました。そこで、各サービスのURLへリダイレクトするHTMLを生成することにしています。Hugoのmarkdown記事を書くときに、以下のように設定することで、先述したRSS.xmlに記事として表示されます。

---
id: "201904_ui-tools-compare"
title: "いちばん詳しい Sketch ・ XD ・ Figma ・ Studio の比較"
date: 2019-04-22T08:45:17+09:00
update: ""
draft: false
tags: ["sketch", "figma", "xd", "app・service"]
description: ""
redirect: "https://note.com/psephopaiktes/n/ne911c869a9d8"
---

上記のredirect項目が設定されているMarkdownは、HTMLをビルドする際にmeta refreshが設定され、リダイレクトされます。2度手間ではありますが、表示する記事も選べるため、これが一番効率良い運用でした。

更新しやすくするため、ShellScriptをnpmに設定しておく

以下のnpmコマンドが使えるようにShellScriptを用意したので、記事更新もらくらくです。逆にHUGOのネイティブコマンドだけで更新作業をしようとすると、若干めんどいです。

コマンド 概要
$ npm run new 入力したIDで新規ブログ記事を生成する
$ npm run draft 下書き中のブログを一覧で表示し、選択するとその記事ファイルを開いてローカルサーバーをたてる。

詳しくはソースコードの以下のファイルをご確認ください。

Markdown中に画像をペーストする

VS CODEのプラグインを使うことで、画像をかんたんに記事中にペーストできます。Markdownファイル上で⌘⌥Vすると、クリップボードの画像をそのmdファイルと同じ場所に保存し、以下を記述してくれます。ファイルの保存場所や名前は当然カスタム可能です。

![](screenshot-2020-03-02.png)

Hugoで画像を自動縮小する

Image Processingという機能で、Hugoをビルドする際に画像の解像度をリサイズできます。あまり考えずにスクショをどんどん記事に貼りたいので、とても助かります。Jpegの圧縮率や回転なども指定できます。

ただ、それほど圧縮率はかしこくないので、将来的にはGoogleの画像圧縮アプリ「Squossh」のCLIを組み込みたいなあと考えてます。

その他の技術・環境

Formcarry

Formcarryのロゴ
JAMStackで地味に困るのがお問い合わせフォームの実装ですね。今回採用したFormcarryは静的なWebサイトに、めちゃくちゃかんたんにお問い合わせフォームを実装できるWebサービスです。

事前にWebサイトに登録して設定しておけば、あとは以下のようにHTMLを用意するだけで終わりです。

<form action="https://formcarry.com/s/{{ENDPOINT}}" method="POST">
    <input type="email" name="email" id="email">
    <input type="submit" value=“Subscribe”>
</form>

{{ENDPOINT}} の部分はできれば秘匿したいので、後述する環境変数を使ってソースコードで見られないようにします。

ホスティングにNetlifyを使っているならNetlify Formを使えばいいと思います。あと、Google Domainを使っている場合はメールエイリアスがかんたんに作れるので、それを公開しちゃうのも有りだと思います。

Vercel

Vercelのロゴ
定番のホスティングサービスですね。nodeの実行ができるので、GitHubのmainブランチにプッシュしたコードが自動で本番にビルドされるようにしています。

環境変数も設定できるので、APIキーの秘匿などももちろん可能です。Vue.jsでは process.env.VUE_APP_... の形式で環境変数を使えるので、ローカルの開発では.env.localファイルなどを用意してそちらを設定します。これもVue3で微妙に形式がかわったので注意が必要です。

Vue CLI の公式ガイドを読めばそんなに苦労せず使えると思います。

ダークモード対応

ダークモード・ライトモードが切り替えられるようになっています
prefers-color-schemeメディアクエリを使用して、ユーザーのデバイス設定にあわせてページのダーク/ライトモードを変更しています。(参考)

今回はFigmaでWebデザインする段階からライトモード用、ダークモード用の色変数をそれぞれ用意して、最終的な実装でもそれをSCSS変数として使う方法をとりました。

こちらもそのうちデザインからCSSまでのワークフローを別の記事にしようと思います。

SVG Sprite

読み込みリクエスト数を減らすため、SVG画像はすべてひとつにまとめて、スプライト画像として使っています。src/assets/svg-sprite に置いたSVGアイコンが、 $ npm run server するたびにsvg-spriteパッケージによってSVG Spriteに変換されます。

Vueの基底コンポーネントとして登録しておくことで、HTMLの中では

<SVG symbol="logo" alt="ロゴ画像" />

とかんたんに呼び出して使えます。CSSで色やサイズも変えられるのでめっちゃ便利です。こちらもけっこう工夫したので、別で記事にしようかと思ってます。


以上です。ブログ更新者がエンジニアであればけっこうおすすめの構成です。

長文読んでくれてありがとうございます。

Discussion