搜索服务
为你的 VitePress 站点选择合适的搜索方案,提供出色的内容查找体验。
方案对比
| 特性 | 本地搜索 | Algolia | DocSearch | FlexSearch | Pagefind |
|---|---|---|---|---|---|
| 类型 | 内置 | 云服务 | 云服务 | 第三方插件 | 第三方工具 |
| 免费 | ✅ | ⚠️ 付费 | ✅ 开源免费 | ✅ | ✅ |
| 索引方式 | 构建 | 云端爬虫 | 云端爬虫 | 构建 | 构建后索引 |
| 中文支持 | ✅ | ✅ | ✅ | ✅ | ✅ |
| 模糊搜索 | ⚠️ 有限 | ✅ 优秀 | ✅ 优秀 | ✅ 优秀 | ✅ 良好 |
| 配置难度 | 简单 | 中等 | 简单 | 中等 | 简单 |
| 索引大小 | 中等 | 云端 | 云端 | 较大 | 极小 |
| 离线可用 | ✅ | ❌ | ❌ | ✅ | ✅ |
选择建议
| 场景 | 推荐 | 原因 |
|---|---|---|
| 小型文档(< 100 页) | 本地搜索 | 简单、免费、开箱即用 |
| 开源项目 | DocSearch | 免费、专业 |
| 企业文档 | Algolia | 功能强大、稳定 |
| 大型站点(> 500 页) | FlexSearch | 性能好、可定制 |
| 极致轻量 | Pagefind | 索引小、加载快 |
| 隐私优先 | 本地搜索 / Pagefind | 无需第三方服务 |
本地搜索
VitePress 内置的本地搜索方案,基于 MiniSearch,开箱即用。
基本配置
ts
// .vitepress/config.mts
export default defineConfig({
themeConfig: {
search: {
provider: 'local'
}
}
})中文搜索优化
默认分词对中文支持有限,可以通过自定义 tokenize 函数优化:
ts
export default defineConfig({
themeConfig: {
search: {
provider: 'local',
options: {
miniSearch: {
searchOptions: {
// 模糊搜索阈值:0 = 精确,1 = 匹配所有
fuzzy: 0.2,
// 前缀搜索:输入部分字符即可匹配
prefix: true,
// 搜索结果权重:标题 > 小标题 > 正文
boost: { title: 4, text: 2, titles: 1 }
},
options: {
// 中文分词:按标点符号和空格分割
tokenize: (text: string) =>
text.split(/[\s\u3000\u3001\u3002\uff0c\uff1b\uff1a\uff01\uff1f\u201c\u201d\u2018\u2019\uff08\uff09]+/)
}
},
// 显示详细视图(包含上下文片段)
detailedView: true
}
}
}
})排除页面
将某些页面从搜索索引中排除:
ts
export default defineConfig({
themeConfig: {
search: {
provider: 'local',
options: {
// 排除特定路径的页面
exclude: (id: string) => {
return id.includes('private/') ||
id.includes('draft/') ||
id === 'changelog.md'
}
}
}
}
})自定义搜索翻译
ts
export default defineConfig({
themeConfig: {
search: {
provider: 'local',
options: {
translations: {
button: {
buttonText: '搜索文档',
buttonAriaLabel: '搜索文档'
},
modal: {
displayDetails: '显示详细',
resetButtonTitle: '清除查询条件',
backButtonTitle: '返回',
noResultsText: '无法找到相关结果',
footer: {
selectText: '选择',
navigateText: '切换',
closeText: '关闭',
navigateUpText: '上移',
navigateDownText: '下移',
selectEnterText: '打开'
}
}
}
}
}
}
})本地搜索限制
| 限制 | 说明 |
|---|---|
| 索引大小 | 页面数超过 1000 时搜索索引可能较大,影响加载速度 |
| 模糊搜索 | MiniSearch 的模糊搜索能力有限,不如 Algolia |
| 分词精度 | 自定义分词只能按规则切分,无法理解语义 |
| 实时更新 | 内容更新需要重新构建才能生效 |
Algolia / DocSearch
企业级搜索服务,Algolia 为开源项目提供免费的 DocSearch 程序。
快速配置
ts
// .vitepress/config.mts
export default defineConfig({
themeConfig: {
search: {
provider: 'algolia',
options: {
appId: 'YOUR_APP_ID',
apiKey: 'YOUR_SEARCH_API_KEY',
indexName: 'YOUR_INDEX_NAME',
// 可选:设置搜索语言
locales: {
zh: {
placeholder: '搜索文档',
translations: {
button: { buttonText: '搜索文档' },
modal: {
searchBox: {
resetButtonTitle: '清除查询条件',
resetButtonAriaLabel: '清除查询条件',
cancelButtonText: '取消',
cancelButtonAriaLabel: '取消'
},
startScreen: {
recentSearchesTitle: '搜索历史',
noRecentSearchesText: '没有搜索历史',
saveRecentSearchButtonTitle: '保存搜索',
removeRecentSearchButtonTitle: '移除搜索',
favoriteSearchesTitle: '收藏',
removeFavoriteSearchButtonTitle: '移除收藏'
},
errorScreen: {
titleText: '无法获取结果',
helpText: '请检查网络连接'
},
footer: {
selectText: '选择',
navigateText: '切换',
closeText: '关闭',
searchByText: '搜索提供'
},
noResultsScreen: {
noResultsText: '无法找到相关结果',
suggestedQueryText: '试试搜索',
reportMissingResultsText: '认为应该有结果?',
reportMissingResultsLinkText: '点击反馈'
}
}
}
}
}
}
}
}
})DocSearch 申请
- 访问 DocSearch 申请页面 提交申请
- 项目需满足条件:
- 开源项目,文档公开可访问
- 文档内容为技术相关
- 你是项目维护者
- 审核通过后(1-2 周)收到包含
appId、apiKey、indexName的邮件 - 将配置添加到 VitePress 配置文件
自定义爬虫配置
DocSearch 通过爬虫抓取网站内容,你可以在配置中指定抓取规则:
json
{
"index_name": "your-site",
"start_urls": ["https://your-site.com/"],
"sitemap_urls": ["https://your-site.com/sitemap.xml"],
"stop_urls": ["/changelog", "/privacy"],
"selectors": {
"lvl0": {
"selector": ".VPDoc h1",
"global": true,
"default_value": "Documentation"
},
"lvl1": ".VPDoc h2",
"lvl2": ".VPDoc h3",
"lvl3": ".VPDoc h4",
"lvl4": ".VPDoc h5",
"text": ".VPDoc p, .VPDoc li"
},
"selectors_exclude": [".vp-code-group", ".line-numbers"],
"custom_settings": {
"attributesForFaceting": ["language", "version", "type"],
"attributesToSnippet": ["text:20"]
}
}Algolia 高级功能
| 功能 | 说明 | 配置方式 |
|---|---|---|
| 分面搜索 | 按分类、版本筛选 | attributesForFaceting |
| 同义词 | 搜索"安装"也匹配"setup" | Algolia Dashboard 配置 |
| 排序策略 | 按相关度/人气/时间排序 | 配置多个排序索引 |
| 高亮 | 搜索结果中高亮匹配词 | 自动支持 |
| 点击分析 | 追踪用户点击的搜索结果 | 配置 clickAnalytics: true |
Algolia 的更完整配置和自定义 UI 开发,请参考 Algolia 搜索集成指南。
FlexSearch 增强
基于 FlexSearch 的高性能本地搜索,适合大型站点。
安装
bash
npm install vitepress-plugin-flexsearch -D基本配置
ts
// .vitepress/config.mts
import { withFlexSearch } from 'vitepress-plugin-flexsearch'
export default withFlexSearch({
themeConfig: {
search: { provider: 'local' }
},
flexSearch: {
// 编码器:控制分词和匹配策略
encode: 'balance',
// 分词策略:forward = 前向匹配
tokenize: 'forward',
// 搜索精度:1-9,越高越精确但越慢
resolution: 9,
// 缓存大小(条目数)
cache: 100
}
})编码器选择
| 编码器 | 说明 | 适用场景 |
|---|---|---|
default | 标准分词 | 英文内容 |
simple | 简单分词,速度最快 | 大型站点 |
balance | 平衡精度与速度 | 通用推荐 |
advanced | 高级分词,精度最高 | 需要精确匹配 |
extra | 极致精度,速度最慢 | 搜索质量优先 |
中文搜索优化
ts
export default withFlexSearch({
themeConfig: {
search: { provider: 'local' }
},
flexSearch: {
encode: 'balance',
tokenize: function (str: string) {
// 中文按字符分割,英文按空格分割
const result: string[] = []
const tokens = str.split(/[\s\u3000]+/)
for (const token of tokens) {
// 中文字符逐字分词
if (/[\u4e00-\u9fff]/.test(token)) {
for (let i = 0; i < token.length; i++) {
result.push(token.substring(i, i + 1))
}
} else {
result.push(token)
}
}
return result
},
resolution: 9,
cache: 100,
// 搜索结果排序
sort: function (a: number, b: number) {
return a - b
}
}
})FlexSearch 性能对比
| 指标 | 本地搜索 (MiniSearch) | FlexSearch |
|---|---|---|
| 索引构建 | 构建时 | 构建时 |
| 100 页搜索 | < 50ms | < 20ms |
| 500 页搜索 | < 200ms | < 50ms |
| 1000 页搜索 | < 500ms | < 100ms |
| 内存占用 | 中等 | 较高 |
| 索引文件 | 中等 | 较大 |
Pagefind
Pagefind 是一个轻量级静态搜索工具,构建后自动索引,索引文件极小。
安装
bash
npm install pagefind -D配置方式
Pagefind 需要在构建完成后运行索引命令:
json
// package.json
{
"scripts": {
"docs:build": "vitepress build docs && npx pagefind --site docs/.vitepress/dist",
"docs:preview": "vitepress preview docs"
}
}在 VitePress 中引入 Pagefind UI:
ts
// .vitepress/config.mts
export default defineConfig({
head: [
['link', { href: '/pagefind/pagefind-ui.css', rel: 'stylesheet' }],
['script', { src: '/pagefind/pagefind-ui.js' }]
]
})创建搜索组件:
vue
<!-- .vitepress/theme/components/PagefindSearch.vue -->
<script setup lang="ts">
import { ref, onMounted } from 'vue'
const searchContainer = ref<HTMLElement>()
onMounted(async () => {
// @ts-expect-error Pagefind UI 是全局加载的
await new window.PagefindUI({
element: searchContainer.value,
showSubResults: true,
showImages: false
})
})
</script>
<template>
<div ref="searchContainer" class="pagefind-search" />
</template>
<style scoped>
.pagefind-search {
--pagefind-ui-scale: 1;
--pagefind-ui-primary: var(--vp-c-brand);
--pagefind-ui-text: var(--vp-c-text-1);
--pagefind-ui-background: var(--vp-c-bg-soft);
--pagefind-ui-border: var(--vp-c-divider);
--pagefind-ui-tag: var(--vp-c-brand);
--pagefind-ui-border-width: 1px;
--pagefind-ui-border-radius: 8px;
--pagefind-ui-image-border-radius: 8px;
--pagefind-ui-input-width: 100%;
}
</style>Pagefind 自定义配置
bash
# 指定语言
npx pagefind --site docs/.vitepress/dist --language zh
# 排除特定路径
npx pagefind --site docs/.vitepress/dist --exclude-selectors ".vp-code-group,.line-numbers"
# 自定义索引路径
npx pagefind --site docs/.vitepress/dist --output-path public/pagefindPagefind 优势
| 优势 | 说明 |
|---|---|
| 极小索引 | 100 页文档索引仅约 100KB |
| 零运行时 | 构建时索引,无需服务器 |
| 零配置 | 自动检测页面语言 |
| 隐私友好 | 无第三方服务 |
| 多语言 | 自动检测语言并正确分词 |
MeiliSearch
MeiliSearch 是自托管的搜索引擎,适合需要全文搜索的大型项目。
Docker 部署
bash
docker run -it --rm \
-p 7700:7700 \
-e MEILI_MASTER_KEY=your-master-key \
-v $(pwd)/meili_data:/meili_data \
getmeili/meilisearch:v1.5索引数据
ts
// scripts/meilisearch-sync.ts
import { MeiliSearch } from 'meilisearch'
import { readFileSync, readdirSync } from 'fs'
import { join } from 'path'
const client = new MeiliSearch({
host: 'http://localhost:7700',
apiKey: 'your-master-key'
})
interface DocEntry {
id: string
title: string
content: string
url: string
hierarchy: string[]
}
async function indexDocs() {
const index = client.index('docs')
// 配置索引设置
await index.updateSettings({
searchableAttributes: ['title', 'hierarchy', 'content'],
filterableAttributes: ['hierarchy'],
sortableAttributes: ['title'],
pagination: { maxTotalHits: 1000 }
})
// 读取并索引文档
const docsDir = 'docs'
const entries: DocEntry[] = []
function walkDir(dir: string, hierarchy: string[] = []) {
const files = readdirSync(dir, { withFileTypes: true })
for (const file of files) {
const fullPath = join(dir, file.name)
if (file.isDirectory()) {
walkDir(fullPath, [...hierarchy, file.name])
} else if (file.name.endsWith('.md')) {
const content = readFileSync(fullPath, 'utf-8')
// 简单提取标题
const titleMatch = content.match(/^#\s+(.+)$/m)
const title = titleMatch ? titleMatch[1] : file.name
const url = fullPath
.replace('docs/', '/')
.replace('.md', '')
entries.push({
id: fullPath.replace(/[/\\]/g, '_'),
title,
content: content.replace(/^---[\s\S]*?---/, '').trim(),
url,
hierarchy
})
}
}
}
walkDir(docsDir)
// 批量添加文档
await index.addDocuments(entries)
console.log(`索引完成:${entries.length} 篇文档`)
}
indexDocs().catch(console.error)VitePress 集成
vue
<!-- .vitepress/theme/components/MeiliSearchBox.vue -->
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { MeiliSearch } from 'meilisearch'
const query = ref('')
const results = ref<any[]>([])
const client = new MeiliSearch({
host: 'http://localhost:7700',
apiKey: 'your-search-api-key'
})
async function search() {
if (!query.value.trim()) {
results.value = []
return
}
const { hits } = await client.index('docs').search(query.value, {
limit: 10,
attributesToHighlight: ['title', 'content']
})
results.value = hits
}
</script>
<template>
<div class="meilisearch-box">
<input
v-model="query"
placeholder="搜索文档..."
@input="search"
/>
<ul v-if="results.length" class="results">
<li v-for="hit in results" :key="hit.id">
<a :href="hit.url" v-html="hit._formatted?.title || hit.title" />
</li>
</ul>
</div>
</template>MeiliSearch vs Algolia
| 特性 | MeiliSearch | Algolia |
|---|---|---|
| 部署方式 | 自托管 | 云服务 |
| 价格 | 免费开源 | 按用量付费 |
| 搜索速度 | 快(< 50ms) | 极快(< 10ms) |
| 容错性 | 支持拼写容错 | 支持拼写容错 |
| 中文支持 | ✅ | ✅ |
| 数据安全 | 自控 | 云端托管 |
| 运维成本 | 需要自行维护 | 零运维 |
搜索方案迁移
从本地搜索迁移到 Algolia
- 申请 DocSearch 或创建 Algolia 应用
- 配置 Algolia 参数到
config.mts - 将
provider从'local'改为'algolia' - 验证搜索功能 正常工作
- 提交爬虫配置(如需自定义抓取规则)
从 Algolia 迁移到 Pagefind
- 移除 Algolia 配置 和 head 中的脚本
- 安装 Pagefind 并配置构建脚本
- 创建 Pagefind UI 组件
- 测试搜索功能 确保结果正确
迁移注意事项
| 事项 | 说明 |
|---|---|
| 搜索体验 | 不同方案的搜索体验有差异,建议先预览 |
| 索引数据 | 切换方案需要重新索引 |
| 自定义 UI | 迁移后可能需要调整自定义搜索组件 |
| 中文分词 | 不同方案的中文分词效果不同 |
常见问题
本地搜索索引过大怎么办?
ts
// 方案 1:排除不需要索引的页面
search: {
provider: 'local',
options: {
exclude: (id) => id.includes('changelog') || id.includes('private/')
}
}
// 方案 2:切换到 FlexSearch 或 Pagefind搜索结果不包含某些页面?
检查以下可能原因:
- 页面被排除:检查
exclude配置 - Frontmatter 设置:页面
search: false会被跳过 - 构建缓存:清除
.vitepress/dist和.vitepress/cache后重新构建
Algolia 搜索结果更新不及时?
Algolia 爬虫按计划运行(通常每 24 小时一次)。如需即时更新:
- 登录 Algolia Dashboard 手动触发爬取
- 或配置 Webhook 在部署后自动触发
Pagefind 在开发模式下不可用?
Pagefind 需要构建后的文件才能生成索引,开发模式下无法使用。解决方案:
- 先运行一次构建:
npm run docs:build - 将
pagefind/目录复制到docs/public/下 - 开发模式下即可使用搜索
相关链接
- 搜索功能配置 — 内置搜索详解
- Algolia 搜索集成 — Algolia 深度教程
- FlexSearch
- Pagefind
- MeiliSearch
- MiniSearch