用 Pagefind 给静态博客加全文搜索
📅 创建 ·☕ 约 1 分钟·✍ 335 字

为什么不用客户端搜索库
一开始我想过把文章标题和摘要塞进一个 JSON,前端自己过滤。问题是:
- 文章一多,那个 JSON 就有几百 KB,首屏白等;
- 只能搜标题和摘要,搜不到正文;
- 中文分词要自己搞。
Pagefind 怎么做的
它在构建之后扫描 dist/ 里的 HTML,生成一堆分片索引文件:
npm run build # astro build && pagefind --site dist
产物长这样:
dist/pagefind/
├─ pagefind.js # 运行时模块
├─ pagefind-entry.json # 索引清单
└─ fragment/*.pf # 分片索引
浏览器只在需要时下载命中的那几个分片,不是整个索引。
关键:别让它进首屏
我把搜索做成了「懒到骨子里」:
input.addEventListener('focus', function () { load(); });
用户点进输入框的那一刻,才开始 import('/pagefind/pagefind.js')。
在那之前,首屏 JS 里跟搜索有关的字节数是 0。
自己渲染结果
Pagefind 自带的 UI 组件有几十 KB,而且样式不好覆盖。它的 JS API 其实很短:
const pf = await import('/pagefind/pagefind.js');
await pf.options({ excerptLength: 26 });
const search = await pf.search('芙宁娜');
const results = await Promise.all(
search.results.slice(0, 8).map((r) => r.data())
);
// results[i] → { url, excerpt, meta: { title, date } }
excerpt 里已经带好了 <mark> 高亮标签,直接插进 DOM 就行。
踩到的坑
- 本地开发搜不到东西 —— Pagefind 只认构建产物,
astro dev下没有索引。要先npm run build。 - 动态 import 会被打包器处理 —— 用
is:inline的内联脚本,路径原样保留。 - 高亮颜色 ——
<mark>默认是刺眼的黄色,记得用主题变量接管。
结论:静态站做搜索,Pagefind 基本是目前的最优解。
