系列:站点搭建笔记· 第 1 篇
给静态博客选全文搜索:三种方案对比
目录
注意:本文是站点初始化时生成的示例文章。 内容是通用的技术方案对比,不涉及任何真实项目的具体信息。确认排版没问题后可以删掉。
静态博客有个绕不开的取舍:没有服务端,搜索怎么办?把三条常见路线摆在一起,说清楚各自适合什么场景。
先明确约束
选方案前,先把约束写死。本文假设的约束是:
- 纯静态托管,没有常驻进程,也不打算为了搜索开一台服务器
- 文章数量在几百篇量级,不是几万篇
- 希望搜索完全在浏览器里完成,查询词不出本机
- 构建时间不能爆炸,最好还在两分钟以内
这几条一摆,选项其实就不多了。
方案一:前端小索引
构建时把每篇文章的标题、正文切词,生成一个 JSON 索引,前端拉下来自己算匹配。
优点:完全自主,想怎么排序就怎么排序。
缺点:索引体积随文章量线性增长。几百篇全文字索引动辄几 MB,首屏拉这个文件很伤。要做增量加载、要自己实现分词和排序,代码量不小 —— 等于自己造一个搜索引擎。
结论:文章少、要求特殊时可以用;作为通用方案不划算。
方案二:第三方托管搜索
把内容同步到 Algolia、Meilisearch 之类的托管服务,前端调它的 API。
优点:功能最全,拼写纠错、同义词、聚合筛选都有;性能不用自己操心。
缺点:引入外部依赖和密钥;免费额度通常按「搜索请求数」或「记录数」计费,超了要花钱;页面会向第三方发请求,查询词要经过别人的服务器。
结论:内容量大、需要高级检索能力时值得。个人博客用它是杀鸡用牛刀,还要接受外部依赖。
方案三:构建期索引(Pagefind)
构建完成后,扫描生成的 HTML,切成语义块并压缩成二进制索引,前端按需拉取分片。
关键在「分片」:搜索结果只加载命中的那几个片段,而不是整份索引。所以即使文章上千,首次搜索也只拉几 KB。
| 对比项 | 前端小索引 | 第三方托管 | Pagefind |
|---|---|---|---|
| 运行位置 | 浏览器 | 对方服务器 | 浏览器 |
| 索引体积 | 大,随文章线性涨 | 不占本地 | 分片,按需加载 |
| 查询词是否外发 | 否 | 是 | 否 |
| 需要密钥 | 否 | 是 | 否 |
| 成本 | 0 | 有免费额度 | 0 |
| 拼写纠错 / 同义词 | 自己写 | 内置 | 无 |
| 接入成本 | 高 | 中 | 低 |
| 与静态站契合度 | 一般 | 一般 | 高 |
为什么最后选 Pagefind
三条约束它都满足,而且几乎不需要写代码 —— 它是构建后的一个命令,产出一个自包含的目录。
代价是明确的两条:
- 没有高级检索能力。它做的是前缀匹配加权重排序,不做拼写纠错,也不理解同义词。对个人博客来说够用。
- 只对构建产物有效。这一点足够反直觉,值得单独写一篇 —— 见系列下一篇。
三种方案的一句话总结
下面这块是用 JSX 写的 —— 这个文件是 .mdx 而不是 .md,所以正文里能直接用组件和表达式。这就是 MDX 相对于纯 Markdown 的增量能力。
一句话结论
- 前端小索引
- 第三方托管
- Pagefind
这块就是 MDX 相对纯 Markdown 多出来的能力:正文里可以直接写 JSX、export const 数据,
甚至 import 组件(不过文章最好别依赖源码里的模块,那会让内容不再独立)。
一个容易被忽略的细节
默认情况下 Pagefind 会把整页文字都索引进去,包括导航栏和页脚。结果是搜什么都能匹配到每个页面,因为「首页 / 文章 / 分类 / 标签」这些词在每个页面都出现过。
解决办法是在正文容器上加一个属性,明确告诉它「只索引这里」:
<article data-pagefind-body> <!-- 只有这里面的文字会进索引 --></article>顺带一提:一旦页面上出现了这个属性,那些没有加这个属性的页面会被整体跳过。所以如果发现某类页面搜不到,先检查是不是漏加了这个属性。
「站点搭建笔记」全部 2 篇
- 给静态博客选全文搜索:三种方案对比(当前)
- 内容集合:让 frontmatter 写错就构建失败