2026-01-28 23:20:39 +08:00
|
|
|
---
|
2026-01-30 19:17:04 +08:00
|
|
|
layout: post
|
2026-01-30 21:11:05 +08:00
|
|
|
title: Hugo静态博客如何实现搜索功能
|
|
|
|
|
date: 2026-01-28T02:00:00.000Z
|
|
|
|
|
updated: 2026-01-28T02:00:00.000Z
|
2026-01-30 19:17:04 +08:00
|
|
|
draft: false
|
|
|
|
|
author: 小赵同学
|
2026-01-30 21:11:05 +08:00
|
|
|
categories:
|
2026-01-30 19:17:04 +08:00
|
|
|
- 建站
|
2026-01-30 21:11:05 +08:00
|
|
|
tags:
|
2026-01-30 19:17:04 +08:00
|
|
|
- Hugo
|
|
|
|
|
- Search
|
|
|
|
|
- PJAX
|
|
|
|
|
- 折腾
|
2026-01-30 21:11:05 +08:00
|
|
|
gemini_comment: null
|
|
|
|
|
location: null
|
2026-01-30 19:17:04 +08:00
|
|
|
show_theme: 1
|
2026-01-30 21:11:05 +08:00
|
|
|
top_img: 'https://picsum.photos/800/400'
|
|
|
|
|
description: 文章慢慢多了起来,有时候自己想找以前写的内容都得翻半天。于是抽空给博客加了个简单的搜索功能,没用什么高大上的第三方服务,纯前端实现,简单好用。
|
|
|
|
|
ai_comment: >-
|
|
|
|
|
作为同样被博客搜索问题困扰过的技术小白,看完默默打开了我的hexo配置文件...这种轻量级解决方案简直是我的救星!尤其是把搜索框藏在随笔标题里的小彩蛋,程序员的小幽默总是这么可爱。等我也搞定了就来评论区还愿,希望到时候别卡在json模板那一步(笑)。
|
2026-03-01 11:48:36 +08:00
|
|
|
slug: 20260128
|
2026-01-28 23:20:39 +08:00
|
|
|
---
|
|
|
|
|
|
|
|
|
|
最近发现博客文章数量慢慢上来了,有时候想找自己以前写的某个知识点,翻半天都翻不到,确实有点不方便。
|
|
|
|
|
|
|
|
|
|
其实早就想加个搜索功能了,但看了一圈,像 Algolia 这种第三方服务虽然强大,但配置起来感觉有点繁琐,而且对于咱们这种访问量不大的静态博客来说,是不是有点“杀鸡用牛刀”了?
|
|
|
|
|
|
|
|
|
|
想了想,还是决定用最简单的方式来实现:直接生成一个 JSON 索引文件,用 JS 在前端搜一下。虽然比较基础,但胜在轻量,而且完全可控。
|
|
|
|
|
|
|
|
|
|
折腾了几个小时,终于搞定了,顺便把交互做得稍微优化了一下,支持了 PJAX 和暗黑模式。这里简单记录一下实现过程,希望能给同样在折腾 Hugo 的朋友一点参考。
|
|
|
|
|
|
|
|
|
|
## 效果展示
|
|
|
|
|
|
|
|
|
|
为了不破坏页面的简洁感,我把搜索入口藏在了两个地方:
|
|
|
|
|
|
|
|
|
|
1. **右下角悬浮工具栏**:点那个放大镜图标,会弹出一个搜索框。
|
|
|
|
|
2. **首页“随笔”标题**:这个算是个小彩蛋吧。在首页找到“随笔”这两个字,鼠标放上去会有“点击搜索”的提示,点一下就能直接在原地搜索,不用跳转。
|
|
|
|
|
|
|
|
|
|
## 实现思路
|
|
|
|
|
|
|
|
|
|
原理其实非常简单,主要就三步:
|
|
|
|
|
|
|
|
|
|
1. 让 Hugo 构建时多生成一个 `index.json` 文件,里面包含所有文章的标题、链接和摘要。
|
|
|
|
|
2. 前端 JS 通过 `fetch` 请求这个 JSON 文件。
|
|
|
|
|
3. 根据用户输入的关键词,在 JSON 数据里进行匹配,然后把结果渲染出来。
|
|
|
|
|
|
|
|
|
|
## 代码实现
|
|
|
|
|
|
|
|
|
|
### 1. 配置 Hugo 输出 JSON
|
|
|
|
|
|
|
|
|
|
首先在 `hugo.toml` (或者 `config.toml`) 里配置一下输出格式,告诉 Hugo 首页除了 HTML 还要输出 JSON。
|
|
|
|
|
|
|
|
|
|
```toml
|
|
|
|
|
[outputs]
|
|
|
|
|
home = ["HTML", "RSS", "JSON"]
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 2. 创建 JSON 模板
|
|
|
|
|
|
|
|
|
|
在主题的 `layouts/index.json` 创建一个模板文件,定义 JSON 的数据结构。这里我只取了标题、日期、链接和摘要,尽量让文件小一点。
|
|
|
|
|
|
|
|
|
|
```go
|
|
|
|
|
[
|
|
|
|
|
{{- range $index, $e := where .Site.RegularPages "Type" "post" -}}
|
|
|
|
|
{{- if $index -}}, {{- end -}}
|
|
|
|
|
{
|
|
|
|
|
"title": {{ .Title | jsonify }},
|
|
|
|
|
"date": {{ .Date.Format "2006-01-02" | jsonify }},
|
|
|
|
|
"permalink": {{ .Permalink | jsonify }},
|
|
|
|
|
"summary": {{ .Summary | plainify | jsonify }}
|
|
|
|
|
}
|
|
|
|
|
{{- end -}}
|
|
|
|
|
]
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
这样每次 `hugo` 构建的时候,网站根目录就会生成一个 `index.json`。
|
|
|
|
|
|
|
|
|
|
### 3. 前端 JS 逻辑
|
|
|
|
|
|
|
|
|
|
逻辑主要在 `main.js` 里。为了提升体验,我做了一些小优化:
|
|
|
|
|
|
|
|
|
|
* **懒加载**:不会一打开网页就下载 JSON,而是等你点击搜索按钮时才去加载,帮大家省点流量。
|
|
|
|
|
* **防抖处理**:输入时不会每敲一个字都去搜索,而是稍微停顿一下再触发,避免频繁计算。
|
|
|
|
|
* **PJAX 适配**:因为博客用了 PJAX 做无刷新跳转,一开始遇到个坑,点击搜索结果页面会白屏或者刷新。后来发现需要在渲染结果后,手动调用一下 `window.pjax.refresh()`,让 PJAX 重新接管这些新生成的链接。
|
|
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
|
// 简单的搜索逻辑
|
|
|
|
|
const results = window.searchIndex.filter(item => {
|
|
|
|
|
// 简单的关键词匹配
|
|
|
|
|
return item.title.includes(query) || item.summary.includes(query);
|
|
|
|
|
});
|
|
|
|
|
displayResults(results);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 4. 那个“随笔”变搜索框的交互
|
|
|
|
|
|
|
|
|
|
这个交互其实主要是 CSS 在控制。
|
|
|
|
|
|
|
|
|
|
默认是一个普通的 `<h2>` 标题,点击后通过 JS 隐藏标题,显示输入框。为了过渡自然一点,加了一些简单的 CSS 动画。
|
|
|
|
|
|
|
|
|
|
```css
|
|
|
|
|
/* 默认隐藏提示 */
|
|
|
|
|
.search-hint {
|
|
|
|
|
opacity: 0;
|
|
|
|
|
transition: all 0.3s ease;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 鼠标放上去显示 */
|
|
|
|
|
.section-header.normal-mode:hover .search-hint {
|
|
|
|
|
opacity: 1;
|
|
|
|
|
transform: translateX(0);
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
为了照顾移动端用户,我在 CSS 里加了媒体查询,手机上直接显示“点击搜索”的提示,不然手机上没有 hover 状态,可能会不知道这里可以点。
|
|
|
|
|
|
|
|
|
|
## 遇到的坑
|
|
|
|
|
|
|
|
|
|
折腾过程中最大的坑还是 **PJAX**。
|
|
|
|
|
|
|
|
|
|
一开始写好的时候,搜索功能本身没问题,但是点搜索结果跳转的时候,要么页面刷新,要么音乐播放器断了。查了半天文档,才发现是因为动态插入的 HTML(搜索结果列表),PJAX 默认是监听不到的。
|
|
|
|
|
|
|
|
|
|
解决办法就是在插入 HTML 后,手动通知一下 PJAX:
|
|
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
|
if (window.pjax) window.pjax.refresh(searchResults);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
另外,为了体验好一点,我还加了个逻辑:点击搜索结果后,自动关闭搜索框,不然跳转完搜索框还挡在那里,确实有点傻。
|
|
|
|
|
|
|
|
|
|
## 总结
|
|
|
|
|
|
|
|
|
|
虽然只是个小功能,但前前后后也调了好久,特别是细节上的打磨。不过看到最后丝滑的搜索体验,感觉还是挺值得的。
|
|
|
|
|
|
|
|
|
|
如果你也在用 Hugo,不想接复杂的第三方搜索,不妨试试这个方案,轻量、简单,完全够用了。
|
|
|
|
|
|
|
|
|
|
代码就不全贴了,核心逻辑都在上面,有兴趣的朋友可以自己折腾一下。如果有更好的实现方式,也欢迎留言交流哈!
|