VitePress 内置了轻量级、开箱即用的本地全文搜索功能(基于 MiniSearch),但对于纯中文文档站点而言,直接开启往往会遇到中文无法检索、某些页面被跳过等问题。本文记录从功能接入、中文分词配置到避坑排查的完整实践。
一、开启内置本地搜索
在 .vitepress/config.mjs(或 .ts)的 themeConfig 中添加 search 配置:
javascript
export default defineConfig({
themeConfig: {
// 启用本地全文搜索
search: {
provider: "local",
options: {
locales: {
root: {
translations: {
button: {
buttonText: "搜索文档",
buttonAriaLabel: "搜索文档",
},
modal: {
noResultsText: "无法找到相关结果",
resetButtonTitle: "清除查询条件",
footer: {
selectText: "选择",
navigateText: "切换",
closeText: "关闭",
},
},
},
},
},
},
},
},
});二、配置 CJK 中文分词支持
VitePress 默认的 MiniSearch 采用英文空格分词规则。中文词语之间没有空格,如果不做特殊配置,整个长句子会被当成单个词,导致输入子词(如“伽利略”)时无法匹配。
1. 注入中文分词规则
通过在 options.miniSearch 中同时配置 options.tokenize(构建索引端)与 searchOptions.processTerm(用户查询端),实现字符与词汇级的中文切分:
javascript
search: {
provider: "local",
options: {
miniSearch: {
options: {
tokenize: (text) =>
text
.split(/[\s\-]+|(?=[A-Z])|(?<=[a-z])(?=[A-Z])|(?<=[\u4e00-\u9fa5])|(?=[\u4e00-\u9fa5])/u)
.map((t) => t.trim().toLowerCase())
.filter(Boolean),
},
searchOptions: {
combineWith: "AND",
fuzzy: false,
processTerm: (term) =>
term
.split(/[\s\-]+|(?=[A-Z])|(?<=[a-z])(?=[A-Z])|(?<=[\u4e00-\u9fa5])|(?=[\u4e00-\u9fa5])/u)
.map((t) => t.trim().toLowerCase())
.filter(Boolean),
},
},
locales: {
// 汉化弹窗文本...
}
}
}为什么 searchOptions 也要配置 processTerm?
tokenize 负责将文章内容切片存入索引,而 processTerm 负责在用户输入搜索词时执行同样的切分。两端切分规则一致,才能实现精准召回。
三、踩坑排查:为什么特定文章搜不到?
如果某篇文章在页面上明明有文字,但全局搜索完全搜不到,通常是以下原因导致的:
1. 文章缺少 Markdown 标题标签
VitePress 在构建本地搜索索引时,会依赖 HTML 标题标签(<h1> ~ <h6>)来将整篇文章按章节切片(Section):
- 如果某篇 Markdown 没有任何一级标题(
#)或二级标题(##),搜索构建器在提取分块时会因为找不到标题而直接跳过整篇文章,导致正文全部漏录! - 解决办法:确保每篇 Markdown 至少包含一个一级标题(
#)或二级标题(##)。
2. 缓存未更新
在调整了搜索分词配置后,开发服务器或浏览器可能缓存了旧的 @localSearchIndex 索引:
- 重新运行
npm run dev; - 在浏览器按
Ctrl + F5强制刷新清除前端缓存。