github-stylingというテーマに変更した
このブログはgithub-styleからClone
されたgithub-style-plusをfork
してカスタマイズしたgithub-stylingというテーマを使用している。
名前の通りデザインをGithubに寄せたブログテーマで絶大な人気はなさそうだし、Githubのデザイン変更に追従するのは辛そうだし、オプションも少なそうだっだけど、個人でカスタマイズしてくのであれば問題なそうだったので採用してみた。
結果、個人的に使い慣れた、見慣れたデザインになったのでまあ良かったと思っている。
テーマ変更時にディレクトの変更を余儀なくされ、netlify
のリダイレクト設定が必要になったりと面倒ではあったが、躓くことなくテーマ変更できた。
とはいえ、それなりに時間を食うので、このテーマとは長く付き合いたいと思っている。
ただ前述したようにいくらかカスタマイズしなくちゃいけない部分があり、中でも致命的だったのがローカル検索の遅さだった。
ローカル検索に1分かかる
個人的にGithubの最大使いやすさは検索性能の高さだと思っている。
コード検索とか本当に重宝している。
それに比べてこのテーマの検索は遅すぎた。
ブログは過去のナレッジが詰まっているので頻繁に検索したい、けどそのたびに1分かかるのは辛い。
ということで改善することにした。
実際の改善前が下記動画で、実際には1分20秒かかっている。
ネットワークタブをみると全ファイルを 見に行っていることがわかった。
github-style-plusのローカル検索を解読する
実際に全ファイルを見に行っているのが下記のソースになる
fetch(
`${host.indexOf("localhost") > -1 ? "http://" : "https://"}${host}/index.xml`,
)
.then((resp) => resp.text())
.then(async (res) => {
parser = new DOMParser();
xmlDoc = parser.parseFromString(res, "text/xml");
const linkResult = xmlDoc.getElementsByTagName("link");
const titleResult = xmlDoc.getElementsByTagName("title");
const arr = [];
const matched = [];
await (async function searchLink() {
for (let i = 0; i < linkResult.length; i++) {
await fetch(linkResult[i].textContent).then((resp) =>
resp.text().then((res) => {
const pureText = stripHtml(res);
if (pureText.indexOf(keyword) >= 0) {
matched.push(i);
}
}),
);
}
})();
});
まず、自身のindex.xml
を取得し全URLの件数を取得する。
その件数でfor
を回し、全URLのコンテンツを取り出しキーワードが含まれているかをチェックしている。
で含まれていたらmatched
に格納し、matched
を使用しDom
を生成している。
いま200記事あるので、それを全部見に行くとなると、それはそれなりに時間がかかるに決まっている。
改善する
改善後は下記のようになった。
検索の高速化を調べているとFuse.js
がヒットした。
Powerful, lightweight fuzzy-search library, with zero dependencies.
下記のように実装しなおした。
<script src="https://cdn.jsdelivr.net/npm/fuse.js@7.1.0"></script>
まずはFuse.js
を読み込む。
const fetchIndex = async () => {
const response = await fetch(
`${host.indexOf("localhost") > -1 ? "http://" : "https://"}${host}/index.xml`,
);
const xmlText = await response.text();
const parser = new DOMParser();
const xmlDoc = parser.parseFromString(xmlText, "text/xml");
const items = Array.from(xmlDoc.getElementsByTagName("item"));
searchIndex = items.map((item) => ({
title: item.getElementsByTagName("title")[0].textContent,
link: item.getElementsByTagName("link")[0].textContent,
content: item.getElementsByTagName("description")[0]?.textContent || "",
}));
fuse = new Fuse(searchIndex, {
keys: ["title", "content"],
threshold: 0.3,
});
};
次にfuse
を定義する。
これで特定のキーワードをtitle
とcontent
から検索できるようになる。やっていることは改善前と変わらずに、index.xml
からtitle
とlink
とdescription
を取得している。
threshold
はあいまい検索の閾値らしい。
オプションは他にもあった。
で、下記コードのように検索ワードをsearch
に渡せば、
const results = fuse.search(query).map((res) => res.item);
fetchIndex
で返した
- title
- link
- content
が取得できるので、それでDom
を生成すればいい。
この方法なら初回にindex.xml
を取得するだけで検索が実装できる。
これでブログを頻繁に検索できるようになり、自身のナレッジに手軽にアクセスできるようになった。
そもそも改善前もdescription
を使えばfetch
する必要がなかったかもしれない。
あるいはもしかするとこの方法だとtag
が取得できないので、あえて全URLのHTMLを見に行っていたのかもしれない。