From 4baf51f4b183f4424256bd7990806203d0830d8b Mon Sep 17 00:00:00 2001 From: Vaica <94612053+zqlit@users.noreply.github.com> Date: Wed, 28 Jan 2026 23:20:39 +0800 Subject: [PATCH] =?UTF-8?q?added=20=E6=90=9C=E7=B4=A2=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../index.md | 124 ++++++ hugo.toml | 2 +- themes/Ying/assets/css/main.css | 387 ++++++++++++++++++ themes/Ying/assets/js/main.js | 241 +++++++++++ themes/Ying/layouts/_default/home.html | 17 +- themes/Ying/layouts/index.json | 11 + themes/Ying/layouts/partials/footer.html | 23 ++ 7 files changed, 803 insertions(+), 2 deletions(-) create mode 100644 content/post/2026-01-28-hugo-search-implementation/index.md create mode 100644 themes/Ying/layouts/index.json diff --git a/content/post/2026-01-28-hugo-search-implementation/index.md b/content/post/2026-01-28-hugo-search-implementation/index.md new file mode 100644 index 00000000..9c972c02 --- /dev/null +++ b/content/post/2026-01-28-hugo-search-implementation/index.md @@ -0,0 +1,124 @@ +--- +title: "Hugo静态博客如何实现搜索功能" +date: 2026-01-28 +description: "文章慢慢多了起来,有时候自己想找以前写的内容都得翻半天。于是抽空给博客加了个简单的搜索功能,没用什么高大上的第三方服务,纯前端实现,简单好用。" +tags: ["Hugo", "Search", "PJAX", "折腾"] +categories: ["建站"] +status: public +allowComment: true +--- + +最近发现博客文章数量慢慢上来了,有时候想找自己以前写的某个知识点,翻半天都翻不到,确实有点不方便。 + +其实早就想加个搜索功能了,但看了一圈,像 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 在控制。 + +默认是一个普通的 `
搜索索引加载失败
'; + }); + } + + // Perform Search + function performSearch() { + if(!searchInput) return; + const query = searchInput.value.trim().toLowerCase(); + if (query === '') { + searchResults.innerHTML = '输入关键词并回车进行搜索
'; + return; + } + + if (!window.isIndexLoaded) { + searchResults.innerHTML = '正在加载索引...
'; + // Retry after a short delay if index is loading + setTimeout(performSearch, 500); + return; + } + + const results = window.searchIndex.filter(item => { + const title = item.title ? item.title.toLowerCase() : ''; + const summary = item.summary ? item.summary.toLowerCase() : ''; + return title.includes(query) || summary.includes(query); + }); + + displayResults(results); + } + + // Display Results + function displayResults(results) { + if (results.length === 0) { + searchResults.innerHTML = '未找到相关文章
'; + return; + } + + let html = ''; + results.forEach(item => { + html += ` + + ${item.title} + ${item.date} +