为什么不需要后端搜索?
很多博客使用 Algolia 或自建搜索 API,但对于个人博客来说,一个 JSON 文件就够了。
幻梦的搜索方案:Hugo 编译时生成一个 index.json,包含全站文章索引;用户搜索时在前端直接匹配,无需任何后端服务。
生成索引
layouts/_default/index.json 负责生成搜索索引:
{{- range where .Site.RegularPages "Type" "posts" -}}
{
"title": {{ .Title | jsonify }},
"permalink": {{ .Permalink | jsonify }},
"date": {{ .Date.Format "2006-01-02" | jsonify }},
"description": {{ .Description | jsonify }},
"content": {{ .Plain | truncate 500 | jsonify }},
"tags": {{ .Params.tags | jsonify }},
"categories": {{ .Params.categories | jsonify }}
}
{{- end -}}
每篇文章提取标题、链接、日期、描述、前 500 字正文和标签分类。一个 50 篇文章的博客,索引通常不超过 200KB。
搜索算法
class SearchEngine {
private index: SearchEntry[] = [];
search(query: string): SearchResult[] {
const q = query.toLowerCase().trim();
if (q.length < 1) return [];
return this.index
.filter(entry =>
entry.title.toLowerCase().includes(q) ||
entry.content.toLowerCase().includes(q) ||
entry.tags.some(t => t.toLowerCase().includes(q)) ||
(entry.description && entry.description.toLowerCase().includes(q))
)
.map(entry => ({
...entry,
score: this.calculateScore(entry, q),
excerpt: this.generateExcerpt(entry.content, q)
}))
.sort((a, b) => b.score - a.score)
.slice(0, 10);
}
}
匹配优先级:标题匹配 > 标签匹配 > 正文匹配。搜索结果按相关度排序,只返回前 10 条。
关键词高亮
搜索结果中的关键词用 <mark> 标签高亮:
highlight(text: string, query: string): string {
const regex = new RegExp(`(${this.escapeRegex(query)})`, 'gi');
return text.replace(regex, '<mark>$1</mark>');
}
快捷键支持
搜索模态框支持 Ctrl+K / Cmd+K 全局快捷键:
document.addEventListener('keydown', (e) => {
if ((e.ctrlKey || e.metaKey) && e.key === 'k') {
e.preventDefault();
this.open();
}
});
上下文摘录
搜索结果展示包含关键词的上下文片段(前 30 字符 + 关键词 + 后 70 字符),让用户判断结果是否相关。
一个 JSON 文件,几行 TypeScript,零后端依赖。个人博客的搜索就该这么简单。
评论