2023年はもっといい年にしたいですね。
Table of Contents
あけましておめでとうございます〜
あけましておめでとうございます。本年もよろしくおねがいします。
本年こそは何かを成し遂げたい私ですが、2017年からこまごま続けているこのブログの技術スタックについてもだいぶ固まってきた気がするので新年早々まとめ記事を書いていこうかと思います。
はじめに
このブログのソースコード、記事のMarkdownなどはすべてGitHubで管理してます。
そこに載っているReadmeをみればある程度やっていることがわかるようにはなっているのですが、せっかくなので、どうしてその技術スタックなのかなどの深堀りもできたらなぁーと思ってます。
また、対象の読者は主に個人開発で無料でできるだけそれっぽいサイトが作りたいという人です。無料にこだわってます。めっちゃこだわってます。高度なことはお金を払ってしましょう...。
思い切ってすべてまとめてみようと思ったら、異常なくらい記事が長くなりそうなので、手始めにブログ本体に関することを中心にまとめます。全3回くらいを予定してます。
基本的な技術(サマリー)
本ブログは2019年からGatsby.jsを使ったSSG(Static Site Generator)で構築・運用されています。それ以前はWordPressでした。WordPressは個人ブログの定番ですね。
当時フロントエンドの技術がさっぱりわからず、いよいよ危機感を感じたところから勉強のためブログでも作ってみるか〜というノリでGatsbyで作り始めたと思います。
ブログ記事はMarkdownで管理し、GitHubへのPushおよびPull Requestのイベント契機でGitHub ActionsがGatsby CLIを使ってBuild、静的サイトを作成する仕組みです。これもいくつかの技術変遷がありました。
ホスティングはNetlifyを使ってます。これは当初から変えてません。浮気しようと思ったこともあるんですけどね。使い続けている理由などは後述します。
基本的な技術スタックを聞く限り、「あ〜良くあるSSGを使った個人ブログね〜」という印象と思いますが、結構紆余曲折してます。
Why Gatsby?
SSGでサイトを作るなら例えばNext.jsだったり、Hugo、最近だとAstroなんかも流行ってますし、Vue.jsのフレームワークになれているならVuePress、Denoで作りたければlumeなんかの選択肢も上がってきます。
そのなかでなぜGatsbyがこのブログで採用され続けているかというと、このサイトを作り込み過ぎて他のフレームワークへの移行コストがドエライことになっているからです。Reactのコンポーネントもたくさん作ってしまいました。いわゆるサンクコストというやつです。
こと、SSGの分野でGatsbyが何となく最近他のフレームワークに押されているのを感じますが、理由としてなんとなくGraphQLの存在を常に意識させ続けられる開発体験がちょっと辛くなる瞬間、特にReactのコンポーネント書いていると裏にいるGraphQLとべったり依存しているナ〜というあの感覚が嫌いで使わなくなっていく人が多い印象があります。
ただ、それらは何年も運用しているともはや慣れっこになってしまいました。
GraphQL最高! GraphQL最高!オマエもGraphQL最高と叫びなさい‼︎
というのは冗談で、データソースをある意味GraphQLが媒介してくれるので、ある程度データソースに依存しないサイトを作ることはできるのは事実あります。
別にNext.jsでもgetStaticPropsの処理次第でMarkdown読み込みの依存度を減らしてあげるような作り方は全然できると思いますが、何も考えずに作れるのがGatsbyのいいところではないでしょうか?
唐突ですが、未来の悪魔のお腹の中、臭そう...。カンガルーのお腹の袋は臭いらしいですよ。
プラグインがだいたいなんとかしてくれる
これです。まさにこれです。コミュニティーが成熟し、ちょっとしたやりたいことなら自分で実装しなくてよく、だいたいプラグインが用意されてます。
だいたいのプラグインはnpm installしてgatsby-config.jsに定義してあげるだけです。楽ちん。
今、使っているプラグインを列挙すると次のものがありました。結構使ってますね。
- gatsby-plugin-preact
- gatsby-plugin-typegen
- 後述します。GatsbyをTypeScript化するときに、GraphQLの型を自動生成します。
- 内部的にはGraphQL Code Generatorを使って.graphqlファイルからtypes.tsを作る感じです。
- gatsby-plugin-exclude
- Storybook編でお話します。Storybook対応の際に一部のxxx.stories.tsxをbuildの際に読み込んでしまう問題があったため、対象から除外する目的で導入してます。
- gatsby-plugin-react-helmet
- 超定番プラグイン。各Page, TemplateごとにHeadタグを作りたいときに便利です。ブログサイトだとSEOを意識させられるので必須ですね。
- gatsby-plugin-react-helmet-canonical-urls
- react-helmetでcanonicalをつけてくれるプラグイン。SEO対策。
- gatsby-plugin-sass
- 後述します。このブログのStyleはSass(SCSS)で管理されています。
- gatsby-plugin-minify-classnames
- 後述します。SassをCSS modulesで使うと長くなりがちなclass nameをproduction build時に短くしてサイズをちっちゃくしてくれます。
- gatsby-plugin-purgeCSS
- 後述します。CSSフレームワークとして使っているBootstrapが重たいので少しでも小さくするために使ってます。
- gatsby-remark-copy-linked-files
- このブログではあんまり使ってませんが、ファイルを公開するときによしなにpublicからのリンクを作ってくれます。
- gatsby-plugin-webpack-bundle-analyser-v2
- Webpackのバンドルサイズを確認します。後述しますが、React時代はめちゃくちゃでかかった...。Preactになってちょっと小さくなった。
- gatsby-source-filesystem
- ファイルとして保存されているMarkdownをGraphQLとして読み込み、記事化するために必要なデータソースプラグインです。Markdownをデータソースにしている場合、ほぼ必須のプラグインだと思います。
- gatsby-plugin-robots-txt
- SEO対策です。後述するPreview Deployの場合、そちらをGoogleにIndexされないように制御もしてます。詳しくはCIの回で話します。
- gatsby-plugin-feed
- RSSを生成してくれます。
- gatsby-plugin-algolia
- gatsby-transformer-remark
- MarkdownのRemarkをやってくれるプラグインです。
- gatsby-remark-numbered-footnotes
- あまり使えてませんが脚注を入れるのに使ってます。
- gatsby-remark-prismjs-title
- Prismjsでシンタックスハイライトしたコードブロックにタイトルを付けるときに使います。
- gatsby-remark-prismjs
- Prismjsを使ってコードブロックをシンタックスハイライトします。技術ブログには必須ですね。
- gatsby-remark-autolink-headers
- 記事内のh1,h2とかにアンカーをつけてくれる。技術記事は引用が多そうなのでつけています。
- gatsby-remark-external-links
- 外部リンクを別タブで開かせるためにつけてます。 noopener noreferrer 忘れずに。
- gatsby-remark-check-links
- サイト内リンク切れを防止するプラグインです。今までこちらの動作に引っかかったbuildが一度もないので、使っているかと言えばつかっていないかもです。
- gatsby-plugin-layout
- いわゆるWFのLayout(共通的な側を作る)機能です。このブログでは、全ページで表示されるNavbarの実装とページ遷移時のトランジションの実装で使ってます。
- gatsby-plugin-sitemap
- サイトマップを作ります。SEO対策ですね。
- gatsby-plugin-nprogress
- ページ遷移時にprefetchされていないpage-data.jsonがあった場合、ロードが発生してしまうので、そこで出すグルグルを出してます。出てくる場面が少ないほうがよいですね。
- gatsby-plugin-optimize-svgs
- SVGの最適化をしてくれるプラグインです。だいたい50%くらい削れているそうです。
- gatsby-plugin-manifest
- PWAのマニフェストファイルを作ってくれます。モバイルからのアクセスも結構あったりしますのでPWAは重要です。きっと...。
- gatsby-plugin-minify
- いわゆるminify系のプラグイン。ビルドされた成果物をとことん読みにくくしてくれる代わりにサイズを小さくしてくれます。1%くらい小さくなっているっぽいです。
- gatsby-plugin-offline
- Service Worker対応をかんたんに実装してくれます。gatsby-plugin-manifestと合わせればモバイルの場合、アプリっぽい実装が実現できます。(このブログでその必要があるのか疑問ですが...)
- gatsby-plugin-netlify
- Netlifyが解釈できる_headersファイルを生成しています。主にCache Controlを定義しており、できるだけCacheを見てもらう感じで攻めます。
GatsbyのGatsby Node.js APIを制すれば結構自由度が高い
Gatsbyでは、gatsby-node.jsで動的ページを作成する処理をGatsby Node.js APIを使って定義します。
しかも、これが結構優秀でただMarkdownを読み込んでHTMLをPropsでTemplateにそのまま渡すだけではなく、独自にcontextを生成し、Propsで渡すことができます。
一例ですが、このブログのcreatePagesでは、lazy-load(lozad)のdata-src置換と文字数カウントをcreatePages時、つまりgatsby build時に実施するようにしてます。
なるべく、Reactが処理するコード量を小さくしてあげ、必要な情報をcontext経由で渡してあげることで、ページ遷移時のレンダリングコストを小さくできると信じてます。(逆に転送量が上がる気もするので微妙かもしれません。)
もはや自己満足の世界です。可読性は大きく損ねるしバグの温床なのであまりおすすめはしません。Reactで処理させたり、Markdownで直接定義したほうが100倍わかりやすいです。はい。
でも夢の広がるGatsby Node.js APIs!
import path from "path";
import { GatsbyNode } from "gatsby";
import createPaginatedPages from "gatsby-paginate";
export const createPages: GatsbyNode["createPages"] = async ({
graphql,
actions: { createPage },
}) => {
const repHtml = html.replace(
/<img[\s|\S]src=/g,
'<img class="lozad" src="data:image/gif;base64,R0lGODlhAQABAGAAACH5BAEKAP8ALAAAAAABAAEAAAgEAP8FBAA7" data-src='
);
const words = repHtml
.replace(/<code[\s, \S]*?<\/code>/g, "")
.replace(/<("[^"]*"|'[^']*'|[^'">])*>/g, "")
.replace(/\s+/g, "")
.replace(/#x.*;/, "")
.replace(/&/, "").length;
const minutes = Math.ceil(words / 400);
const component = templateKey || "blog-post";
createPage({
path: $path,
component: path.resolve(`src/templates/${String(component)}.tsx`),
// additional data can be passed via context
context: {
id,
index,
repHtml,
words,
minutes,
},
});
});
};
GatsbyのTypeScript化
GatsbyのTypeScript化は2021年の年末くらいに実施しました。
TypeScript化の前に依存ライブラリの都合上Gatsby自体のバージョンアップ(v2=>v4)を先にしないといけない状況で、結構苦労した記憶があり本当はちゃんと記事を書きたかったのですが、もうすべてを忘れてしまいました。
今ではTypeScriptの型を存分に享受してます!
特にGatsbyのTypeScript化で苦労するところはMaybe型の扱いだと思います。
基本的にgatsby-plugin-typegenがGraphQLのスキーマーから型を自動生成してくれるのですが、その際の型がほとんどMaybe型といういわゆるundefinedが返る可能性を示すものとなります。
型生成としては正しいですが、これがReactだと扱いにくく、JavaScriptのときには一切書かなかった論理和||
によるundefinedの回避やOptional chainingをひたすら書かないといけません。
// こんな感じのMaybe祭り
type MarkdownRemarkFrontmatter = {
readonly title: Maybe<Scalars['String']>;
readonly slug: Maybe<Scalars['String']>;
readonly date: Maybe<Scalars['Date']>;
readonly description: Maybe<Scalars['String']>;
readonly tags: Maybe<ReadonlyArray<Maybe<Scalars['String']>>>;
readonly headerImage: Maybe<Scalars['String']>;
readonly templateKey: Maybe<Scalars['String']>;
readonly id: Maybe<Scalars['String']>;
};
// なので使うときはこんな感じ
<SEO title={frontmatter?.title}
url={shareURL}
siteTitleAlt="tubone BOYAKI"
isPost
tag={frontmatter?.tags ? frontmatter.tags[0] || "" : ""}
description={excerpt || ""}
image={frontmatter?.headerImage || "https://i.imgur.com/4r1DViT.png"}
/>
Gatsbyを途中でTypeScript化しようと思っている人はOptional chainingに慣れておきましょう!まじで...。
Preactの導入
ブログをGatsbyで作りはじめて色々詰め込みすぎたため1年位たったあたりでやたら重たいブログになってしまったという苦い経験があり、なんとなくReact→Preactへの切り替えを実施した記憶があります。
こちらはプラグインの指示通り入れればすんなり動いた感じで特に苦労しませんでした。
おそらくですが、複雑な遅延処理やstateの管理、useEffectのような副作用を使わないサイトだったのでpreact/compatを使わないで実装しきれたからだと思います。
Preactの効果は結構あって、gatsby-plugin-webpack-bundle-analyser-v2でバンドルサイズを比較して1MB以上の削減効果があった(らしい)です。
らしい、というのはその件を一切ブログに書いておらず、唯一Twitterに投稿していたのですが、当時、過去つぶやきを掘り返されることによる炎上が界隈で問題になっていたので、酔ったときになんとなく投稿するつぶやきが未来に炎上することを防ぐ対策の一環で作ったtw-del-backupという自家製ツールが、 ちょうど当時のPreact切り替えのつぶやきを綺麗サッパリ消してしまっていました。
ただ、tw-del-backupは削除したつぶやきを暗号化してバックアップしているので、そちらを確認すると1MB以上削減できたということです。
tw-del-backupは定期的にTweetのバックアップと削除を行なう便利ツールですが、つぶやきが消えることで、添付している画像も合わせて消してしまう仕様になっており、これはまずいですね。
画像も暗号化して保存するように作り直そうかしら...。
また、詳しくはCI/CD編でお話しようかと思うのですが、現在のProductionバンドルサイズはhttps://tubone24.github.io/blog/ba/にホスティングするようにしているのでいつでも確認可能です。
Parsedで1.35MB、GZip圧縮で343KBなのでやや重いですが、まぁ妥当な数字ではないでしょうか?(内半分はGitalkというコメント管理のライブラリです。)
CSSフレームワークとしてのBootstrapとSass with CSS Modules
このブログのスタイルはCSSフレームワークBootstrapがベースとなってます。
Why? という感じかもしれませんが、このブログのテンプレートつまり Gaysby new
コマンドを最初に実行した際にできるテンプレートがBootstrap v3ベースだったことと、当時Bootstrap以外のCSSフレームワークはMaterial UIしか知らず、冒険するのもなぁ〜 という感じでBootstrapをそのまま使うことにしました。
実はこの判断が結果大失敗で、前述したTypeScript化の際にGatsbyのバージョンをv2からv4に上げる対応で、Bootstrap v3で使うjQueryとBootstrapのCSSのloadタイミングが狂ってしまい、うまく動かなくなってしまい実にツライことになりました。
なので、先にBootstrapがjQueryを使わなくなるBootstrap v5へのバージョンアップを済ませてから各ライブラリのバージョンアップを行ない、最後にTypeScript化する、という大工事を強いられたのです。
Bootstrap v5にアップデートするのではなく、別のCSSフレームワーク、例えばTailwind CSSなどへの切り替えも検討していたのですが、モバイル対応のため採用していたGridシステムの置き換えが想像以上に大変だったことと、今のブログデザインを大きく変えたくなく、Bootstrapのほうが逆に古臭くて好きなのでいいかなと思ってBootstrapを使い続けることにしてます。
daisy UIとか、tailwind-gridとかいろんな技術を触ってみましたがいずれしっくりこなかった、というより技術力がなくて使いこなせませんでした。 今年こそは技術力を上げていきたいですね。
Bootstrapの置き換えなどは今年も最後だからBlogのBootstrap v3を何とかするに詳細がありますので詳しくはそちらをご覧ください。
細かいスタイルの調整にはSass(SCSS)を使ってます。これもGaysby new
コマンドを実行した際にできるテンプレートで採用されていたこととCSSライクな記述なら初心者でもとっつき安いかなという安易な考えで採用しました。
最初の頃はSassがGlobal CSSに当たる構造だったので、BEMっぽくクラス名をつけてましたが、誰のレビューも通さない個人開発ではそんなルールはいずれ崩壊し、改修不可能なところまで汚染しまくっていたのでBootstrap v5化が終わったタイミングでSassについては、思い切ってCSS Modulesに移行しました。
会社ではstyled-componentsを使ってますが、CSSに弱い私にはCSSはCSSで分かれたファイルの方がIDEでの補完がききやすく個人的にはSassに満足してます。
Tailwindへの憧れはPurgeCSSで解決しろ!
とはいえ、流行っているTailwindへの憧れを捨てきれずにいた私は、どうしてBootstrapではなくTailwindを使いたいのかを改めて自分に問いかけました。
色々考えた結果、欲しい機能はPurge CSSくらいしか思いつかなかったので、BootstrapでもPurge CSSできるようにgatsby-plugin-purgeCSSを使いました。
ちょっとかっこ悪いですが、GlobalのSCSSにBootstrapのCSSを@importしたうえで、Build時のPost CSS処理で巨大なCSSを作り、Purgeさせてます。
gatsby-plugin-purgecss:
Previous CSS Size: 237.61 KB
New CSS Size: 57.13 KB (-75.96%)
Removed ~180.48 KB of CSS
という結果がBuild時でるので、76% くらいは削れているっぽいです。優秀。
さらに、SCSSで定義しているCSS Modulesもできるだけ小さくするべく、こちらはgatsby-plugin-minify-classnamesを使ってます。
SCSS時点では、
.container {
display: flex;
}
.footer {
padding: 1rem;
}
といったスタイルがCSSになると、
/* index.module.css */
.b_b {
display: flex;
}
.b_c {
padding: 1rem;
}
といった感じでクラス名がちっちゃくなります。これでどれくらいサイズが小さくなっているのかは不明ですが、多分自己満足の世界だと思います。
Gatsby嫌なところ
ここまでGatsbyのいいところを紹介してきましたが数年運用していてツライと感じる点もご紹介しておきます。
これはいいところの相反ですが、やはりGraphQLを否応がなく意識させられます。それもGatsbyのプラグインシステムが作ったスキーマーを正しく理解することが求められます。
言い方かもしれませんが、GraphQLの知識は求められるのにGraphQLが使いこなせるようにはならない感覚に近いです。
なので、GraphQLを勉強したい!という理由一点張りで採用することはあまりおすすめできないなというのが私の所管です。
あと、プラグインが便利すぎて使いまくっているのですが、結構動作原理がわかりにくいです。
プラグイン同士の競合もめっちゃします。そのデバッグは骨が折れるので、ちゃんとコードが書ける人はスクラッチしたほうが無駄な時間を過ごさなそうです。
一例ですが、gatsby-plugin-typegenとgatsby-source-filesystemみたいに、コード自動生成系のプラグインとデータソースのプラグインはお互いの処理をトリガーに処理が走る可能性があるので、処理がループしがちです。
こういったところを解きほぐしていくのはプラグインでサクサク実装したい思想から外れてしまう動きなのかもしれませんね..。
その他の要素技術
ブログ本体に関するその他の要素技術についてもサクッとまとめていきます。
Blog内検索
ブログ開設当初から、ブログ内で記事検索ができたらいいなぁーとずっと思ってました。というより、技術ブログのブログ内検索って結構使いませんか?
サイト検索ならGoogleの検索窓でブログ内検索に site: キーワードつけてあげれば実装できそうですが、いったんGoogleに遷移する動きもちょっとダサいですし、公開直後の記事が反映されないのもツライです。
なので、独自に検索できる仕組みを用意することにしました。
gatsby-plugin-algoliaを使えば比較的かんたんに検索窓を実装できます。
あらかじめAlgoliaという全文検索エンジンにIndexを作っておけばBuild時にGraphQLのQueryから抽出されたdataをIndexしてくれます。
このブログではタイトル、タグの他本文も少し加えるようにして検索性向上を図ってます。
検索窓(Searchbox)の動きはこのブログのコンポーネントを管理しているStorybookから確認できます。
画像のホスティング
このブログの特徴でもありますが、画像のホスティングはimgurを使ってます。
他の画像ホスティングサービスも検討しましたが無料は正義なのと、URLのsuffixをいじると画像サイズが変えられるのが魅力で採用しております。
GatsbyにはSharpを使って画像最適化する仕組みがありますが、画像枚数が多くなると著しくBuild時間がかかるので、こちらの採用は見送りました。
コメント機能
ブログといえば、コメント機能ですね。 もちろんコメント機能は入れております。
このブログではGitalkをコメント機能で採用してます。
GitHubのIssueがデータソースになる仕組みで、記事ごとにIssueが作られ、コメントはIssueへのコメントの形で反映されます。非常に良くできたプロダクトだと思います。
ただ、この数年コメントが来てません...。
Fontawesome + Icomoon
ブログで使うアイコンはFontawesomeを使ってますが、Fontawesomeのアセットが大きかったので、Icomoonを使って利用する最小限だけFontファイルに再パッケージする形にしてます。
Icomoonを使うことで任意のIconfontを作ることができるので、Fontawesomeだと全部入りすぎてちょっと...というとき、結構使えるかもしれません。
ただし、独自に設定したFontであるので、下記のように文字コードとの対応をCSSで設定する必要があります。
@font-face {
font-family: icomoon;
font-style: normal;
font-weight: normal;
src: url("/fonts/icomoon.eot?s0mo8f");
src:
url("/fonts/icomoon.eot?s0mo8f#iefix") format("embedded-opentype"),
url("/fonts/icomoon.woff2?s0mo8f") format("woff2"),
url("/fonts/icomoon.woff?s0mo8f") format("woff"),
url("/fonts/icomoon.ttf?s0mo8f") format("truetype"),
url("/fonts/icomoon.svg?s0mo8f#icomoon") format("svg");
font-display: block;
}
[class^="icon-"],
[class*=" icon-"] {
font-family: icomoon !important;
font-style: normal;
font-weight: normal;
font-variant: normal;
line-height: 1;
text-transform: none;
speak: never;
/* Better Font Rendering =========== */
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-mail::before {
content: "\e900";
}
RSS / Sitemap
ブログだとRSSとSitemapも作らないといけません。SEO上でも重要ですね。
それぞれgatsby-plugin-feedとgatsby-plugin-sitemapというGatsbyプラグインで実現してます。
特に変わったことはせず、Build時にXMLを生成しているだけです。
a11y対応
実はa11yにちゃんと対応しているこのブログです。
会社のフロントエンドツヨツヨエンジニアさんと去年勉強会する機会があり、a11y対応についての共有をいただきました。
それまでは「ほ~ん」くらいの興味しかなかったa11yですが、知れば知るほどHTMLというマークアップ言語の正しいあり方を示されてるようでそこからこのブログのa11y対応が始まりました。
今ではトップページLighthouseで100点を取ることができるくらいに改善できました。勉強会してもらい、ありがとうございます!!!
ちなみにa11yってなんて読むんですか?「アリー?」「エーイレブンワイ?」
Sentry
一応監視系もしっかりしたほうがええな、ということでSentryを導入してます。
ほとんどがChunkLoadErrorですが、たまにライブラリーアップデート起因のエラーもでるのですごく重宝してます。
あと、パフォーマンスメトリックも取っているのでパフォーマンス改善への意欲づくりに貢献してます!意欲だけあって実行しないですが。
Netlify
このブログのホスティング先はNetlifyを採用しております。
理由はこのブログを作ろうと思ったとき無料で使える良さそうなホスティングがNetlifyとFirebaseホスティングくらいしかなかったのでGitHubと連携すればBuildまでやってくれるNetlifyを採用しました。
Firebaseホスティングに比べると帯域制限が少ないのが魅力ではありましたが、やはり上がる課題として日本からのTATが遅すぎる(最寄りのCDNエッジはシンガポールとのこと)点です。
正直課題は有りまくりです。Vercelとか最近だとCloudflare Pagesとか別のホスティングも検討したいですが、CI/CD含めて作り込んでしまったのでサンクコストになって移行できてません。
その代わりにバンドルサイズを下げたりして、TATが遅くてもなんとかなるようにしてやってます。
BuildはGitHub Actionsで
実は選定根拠にもなったNetlifyのBuild機能は現在使っておらず、BuildはGitHub Actionsで実施して、Build済みのバンドルファイル一式をNetlify CLIでDeployする形式を採用してます。
このあたりの話はNetlifyのビルド時間をGitHub Actionsで0時間にして月末のヒヤヒヤから解放されよう!に詳しく書いてあります。
マジで細かく記事をコミットしたいような書き方してると無料枠簡単に突破します。
まとめ
という感じで結構いろんなことをやっているブログですが、パフォーマンスはどんな感じでしょうか?
GTmetrixで計測するとスコアはA。
Performance metricもおおむね良さそうです。
ただ、Lighthouse(Page Insight)で測ってみるとデスクトップはまずまずですが、モバイルの結果がすこぶる悪いです。
MobileのスコアはFirst Contentful Paint (3G) でもある程度スピードを要求されるため、スコアを上げるには、JavaScriptの節約がモット必要なようです。
おまけ
このブログでは、Google Analytics v4を使ってアクセス解析も実施してます。
GAはGoogle Search Consoleと連携するとSearch Console Insightが使えるので、
せっかくなのでこのブログの稼ぎ頭記事も見てみましょう。流入検索ワードを見てみると、k6、StyleGAN、Vue Particles、ラズパイ電子ペーパーあたりが上位っぽいです。
今年こそバズる記事を量産したい...。