---
layout: post
title: 最近对博客搜索功能的优化记录
tags: [博客, 搜索, 优化]
---

  看看其他的博客也会有新的灵感啊～<!--more-->    

# 起因
  前段时间，我闲来无事在GitHub上搜和我使用相同模板[minimal](https://github.com/pages-themes/minimal)的博客。但搜索结果中有许多人用这个模板制作的是简历或作品集，这让我有些失望。不过这倒也能理解，因为这个模版并不算博客模板，没有文章列表之类的代码，这些都只能自己写。当然多找找还是能找到一些的，毕竟这个模板在GitHub Pages中算是最受欢迎，至少符合大众的审美。像我就搜到了一个叫[Guanzhou Hu的博客](https://github.com/josehu07/josehu07.github.io)，他对模板的样式做了不少的改动，而且改的还挺好看的，尤其是右上角的导航栏，看起来挺有意思，只是这个源代码……导航栏有点硬编码的感觉，我不是很喜欢这种实现方式……   

# 使用标签作为关键词进行搜索
  之后我又看了看其他博客，看到了[Matt Walker Blog](https://github.com/mhwalker/mhwalker.github.io)。他没有对模板做很多改动，只是把section元素变得更宽了，但是他没有改手机版自适应的样式，导致界面基本上没法在手机上查看。不过在他的首页中，我对他把文章标签放在文章列表这个操作非常感兴趣，因为每次我都有给文章打标签，但是几乎没什么用。他的标签点进去之后会跳转到该标签下的所有文章，我其实很早就想做这个功能了，但是在不用插件的情况下Jekyll基本上做不出来这种功能，因为没有插件的情况下是不能使用Liquid标签创建文件的，我看了下他的实现，原来是提前创建好的标签页面然后进行筛选的，这个实现我也不喜欢，这样的话我每次打标签都要新建一个标签对应的页面，这种事情不让程序做我会很不爽……（其实现在的GitHub Pages构建网站都是用的Actions了，完全可以自己写一个可以使用插件的Actions来进行构建，不过我也懒得折腾了🤣）   
  要么还有一个选择，可以单独搞一个页面，里面有所有标签对应的文章，点击文章的标签之后使用锚链接定位到对应标签所在的位置。但这样会导致一个页面有可能有一堆相同的文章链接，结果这个页面比归档页面的链接还多，那就感觉有点糟糕了……  
  不过我想起来以前做的[博客全文搜索功能](/2021/07/23/search.html)，如果把标签作为关键词进行查询，那也能起到筛选出标签对应文章的作用吧？而且这样即使我没给那个文章打标签也能搜出来，其实也算不错的选择，另外自从我做出来那个全文搜索的功能之后也没用过几次，没有关键词的话也一时半会想不出来搜什么比较好。于是说做就做，直接把Matt Walker Blog那段在文章列表生成标签的代码复制过来，感觉好像还不错😆？   
  顺便我也把文章里面的标签也加了链接到搜索的功能，不过原来的代码用的是`.join`实现的，现在加上这个功能的话就只能老老实实用循环写了😥……   

# 搜索后使用高亮标记关键词
  上面的标签搜索效果还不错，只是有些关键词搜完之后有点难发现。我搜索出来之后怎么证明搜到的内容里面一定有对应的关键词呢？虽然从程序的角度来说这是理所应当的事情，一定是有的数据才可能被搜到，但有时候不用Ctrl+F看一眼都不知道是哪里搜到了……所以我觉得应该像其他网站一样对搜到的内容用高亮进行标记。标记应该用什么呢？用样式也许不错，不过现在的H5标签里有一个叫mark的标签可以直接用，用这个标签包裹的内容背景颜色就会变成黄色，就像用荧光笔标记了一样，这样就不需要写样式了。   
  至于关键词用查询字符串传过去就好了，那我该怎么做呢？我用的搜索脚本叫[Simple-Jekyll-Search](https://github.com/christian-fei/Simple-Jekyll-Search)，它的文档其实根本没有写怎么把搜索的请求传到模版里，还好它有个[关于模版的测试脚本](https://github.com/christian-fei/Simple-Jekyll-Search/blob/master/tests/Templater.test.js)里面有写，有个query关键词可以把搜索内容给模版渲染出来，既然做了这个功能怎么不写在文档里😅，不过这个项目已经停止，也没法提出什么建议了……   
  这个功能听起来相当简单，我都懒得写了，这种简单的功能直接让AI写才对！于是我把需求告诉它，让它给我实现一份，于是这就是让AI给我写的高亮关键词的JS代码（经过了一点修改）：   
```javascript
$(function () {
    const urlParams = new URLSearchParams(window.location.search);
    const keyword = urlParams.get('kw')?.trim();

    if (!keyword) return;

    // 转义正则表达式特殊字符，避免安全问题
    const escapedKeyword = keyword.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
    // 创建不区分大小写的正则表达式（全局匹配）
    const regex = new RegExp(`(${escapedKeyword})`, 'gi');

    // 递归遍历并高亮文本节点
    const escapeHTML = str => str.replace(/[&<>"']/g, 
        tag => ({
            '&': '&amp;',
            '<': '&lt;',
            '>': '&gt;',
            '"': '&quot;',
            "'": '&#39;'
        }[tag] || tag));
    function highlightTextNodes(element) {
        $(element).contents().each(function () {
            if (this.nodeType === Node.TEXT_NODE) {
                const $this = $(this);
                const text = escapeHTML($this.text());

                // 使用正则替换并保留原始大小写
                if (regex.test(text)) {
                    const replaced = text.replace(regex, '<mark>$1</mark>');
                    $this.replaceWith(replaced);
                }
            } else if (
                this.nodeType === Node.ELEMENT_NODE &&
                !$(this).is('script, style, noscript, textarea')
            ) {
                highlightTextNodes(this);
            }
        });
    }

    $('section').each(function () {
        highlightTextNodes(this);
    });
});
```
  （2025.04.28更新：解决了一个潜在的解析问题）   
  我测试了一下，非常符合我的需求，各种情况都能按照我的预期工作，虽然说功能非常简单，但是能正常运行，AI写的还是挺不错的。   

# 近期的其他修改
  除了对搜索功能的优化，我还做了些别的功能：   
## 随机跳转文章
  前段时间我看到有其他人的博客增加了一个随机跳转文章的功能，不过他的博客是动态博客，实现也比较奇葩，是渲染页面时就已经决定好要随机的文章，也就是说无论用户想不想随便看看，程序都已经随机好了。当然用着静态博客的我来说，从原理上也做不到这一点，不过既然我之前在做[相似文章推荐功能时](/2024/10/01/suggest.html)已经对搜索功能的数据进行了缓存，那么直接用缓存的内容直接随机就好了吧……所以就随便写了写，代码也极其简单：
```html
<a href="javascript:getSearchJSON(function(data){window.location = data[Math.floor(Math.random()*data.length)].url})">Random</a>
```
## 给文章内标题添加锚链接
  最近在修改我的博客的时候我更新了一下[给文章生成目录的组件](https://github.com/allejo/jekyll-toc)，在这时候我想看看它还有什么有意思的组件可以用，然后就发现了[jekyll-anchor-headings](https://github.com/allejo/jekyll-anchor-headings)，它可以像GitHub展示Markdown文件一样在标题上添加点击后就可以直接跳转到对应标题的锚链接，而且示例里也给出了怎么做[可以像GitHub的风格](https://github.com/allejo/jekyll-anchor-headings/wiki/Examples#github-style-octicon-links)。看起来挺有意思，所以就给自己加上了😆。   
## 添加能跳转到原始Markdown的链接
  在修改博客的时候我参考了一下Jekyll的官方文档，在这个时候发现了page.path这个变量。我想了一下这个变量可以用来链接到我的文章内容，然后就在文章标签位置的右侧加上了这个链接，为了能让它显示在右侧，我用的是`float: right`，但是这样会导致和文章标签不在同一行，查了一下才知道用了浮动就会强制将元素转换成块级元素，而文章标签用的是行内元素，所以对不齐，没办法就只能把这一整行都转换成块级元素了……于是代码如下：
```html
{% raw %}<span style="float: right;"><a href="{{ site.github.repository_url }}/tree/master/{{ page.path }}">查看原始文件</a></span>{% endraw %}
```

# 感想
  多看看其他人的博客看来也挺不错，可以看看其他人的想法，说不定就有可以参考的价值呢……不只是文章内容，网站本身的一些功能也是作者的想法啊……而对于那些只套别人模版，没什么自己的改动的博客，那就没什么意思了（当然不会代码的那就没办法了～）。有些人说博客中只有文章才是最重要的，但我觉得对于技术博客来说网站的代码也是展示自己的部分，所以折腾博客本身也是很重要的！