跳到主要内容
Ricky's Blog

系列:站点搭建笔记· 第 1 篇

给静态博客选全文搜索:三种方案对比

静态站没有服务端,搜索就得想别的办法。把常见的三条路摆在一起比:前端小索引、第三方托管、构建期索引。

约 4 分钟项目复盘
目录

注意:本文是站点初始化时生成的示例文章。 内容是通用的技术方案对比,不涉及任何真实项目的具体信息。确认排版没问题后可以删掉。

静态博客有个绕不开的取舍:没有服务端,搜索怎么办?把三条常见路线摆在一起,说清楚各自适合什么场景。

先明确约束

选方案前,先把约束写死。本文假设的约束是:

  • 纯静态托管,没有常驻进程,也不打算为了搜索开一台服务器
  • 文章数量在几百篇量级,不是几万篇
  • 希望搜索完全在浏览器里完成,查询词不出本机
  • 构建时间不能爆炸,最好还在两分钟以内

这几条一摆,选项其实就不多了。

方案一:前端小索引

构建时把每篇文章的标题、正文切词,生成一个 JSON 索引,前端拉下来自己算匹配。

优点:完全自主,想怎么排序就怎么排序。

缺点:索引体积随文章量线性增长。几百篇全文字索引动辄几 MB,首屏拉这个文件很伤。要做增量加载、要自己实现分词和排序,代码量不小 —— 等于自己造一个搜索引擎。

结论:文章少、要求特殊时可以用;作为通用方案不划算。

方案二:第三方托管搜索

把内容同步到 Algolia、Meilisearch 之类的托管服务,前端调它的 API。

优点:功能最全,拼写纠错、同义词、聚合筛选都有;性能不用自己操心。

缺点:引入外部依赖和密钥;免费额度通常按「搜索请求数」或「记录数」计费,超了要花钱;页面会向第三方发请求,查询词要经过别人的服务器。

结论:内容量大、需要高级检索能力时值得。个人博客用它是杀鸡用牛刀,还要接受外部依赖。

方案三:构建期索引(Pagefind)

构建完成后,扫描生成的 HTML,切成语义块并压缩成二进制索引,前端按需拉取分片。

关键在「分片」:搜索结果只加载命中的那几个片段,而不是整份索引。所以即使文章上千,首次搜索也只拉几 KB。

对比项 前端小索引 第三方托管 Pagefind
运行位置 浏览器 对方服务器 浏览器
索引体积 大,随文章线性涨 不占本地 分片,按需加载
查询词是否外发
需要密钥
成本 0 有免费额度 0
拼写纠错 / 同义词 自己写 内置
接入成本
与静态站契合度 一般 一般

为什么最后选 Pagefind

三条约束它都满足,而且几乎不需要写代码 —— 它是构建后的一个命令,产出一个自包含的目录。

代价是明确的两条:

  1. 没有高级检索能力。它做的是前缀匹配加权重排序,不做拼写纠错,也不理解同义词。对个人博客来说够用。
  2. 只对构建产物有效。这一点足够反直觉,值得单独写一篇 —— 见系列下一篇。

三种方案的一句话总结

下面这块是用 JSX 写的 —— 这个文件是 .mdx 而不是 .md,所以正文里能直接用组件和表达式。这就是 MDX 相对于纯 Markdown 的增量能力。

一句话结论

  • 前端小索引索引体积随文章量增长,得自己造搜索引擎
  • 第三方托管引入外部依赖与密钥,查询词要出境
  • Pagefind没有拼写纠错,且只对构建产物有效

这块就是 MDX 相对纯 Markdown 多出来的能力:正文里可以直接写 JSX、export const 数据, 甚至 import 组件(不过文章最好别依赖源码里的模块,那会让内容不再独立)。

一个容易被忽略的细节

默认情况下 Pagefind 会把整页文字都索引进去,包括导航栏和页脚。结果是搜什么都能匹配到每个页面,因为「首页 / 文章 / 分类 / 标签」这些词在每个页面都出现过。

解决办法是在正文容器上加一个属性,明确告诉它「只索引这里」:

<article data-pagefind-body>
<!-- 只有这里面的文字会进索引 -->
</article>

顺带一提:一旦页面上出现了这个属性,那些没有加这个属性的页面会被整体跳过。所以如果发现某类页面搜不到,先检查是不是漏加了这个属性。

「站点搭建笔记」全部 2 篇

  1. 1.给静态博客选全文搜索:三种方案对比(当前)
  2. 2.内容集合:让 frontmatter 写错就构建失败