使用 pagefind 添加博客搜索功能

(ノ>ω<)ノ

这周用 pagefind 给博客添加一个 搜索页面,如果你感兴趣可以搜索看看。

一直都想给博客添加一个搜索功能,加上最近开始写周刊,里面整理了不少的链接,有时候我想找点东西,能够搜索的话就会方便很多。

以前尝试过 algolia ,但配置起来有点麻烦,也显得很重,效果并不满意。

这周看到了一个开源项目 pagefind,它可以对构建好的 HTML 进行索引和搜索,配置简单,开箱即用,没有额外的第三方服务,很符合我的需要。

关于 pagefind

Pagefind 是一个完全 静态 的搜索库,其目标是在大型网站上运行良好,同时 尽可能少地占用用户带宽 ,并且无需托管任何基础设施。

pagefind

pagefind 的一些特点:

  • 零配置支持多语言网站
  • 使用 NodeJS 索引库索引任何内容(例如 PDF、JSON 文件或字幕)
  • 以相同的低带宽占用提供所有功能

对我来说,它最大的优点就是配置简单,不依赖第三方服务,而且搜索效果也不错。

配置 pagefind

配置 pagefind 很简单,你只需要两步:

  1. 执行 npx -y pagefind --site publish 生成索引,其中 publish 是你的静态页面存放的目录
  2. 引用对应的 CSS,JS,并且添加一个 DOM 元素让 pagefind 挂载
<link href="/pagefind/pagefind-ui.css" rel="stylesheet">
<script src="/pagefind/pagefind-ui.js"></script>
<div id="search"></div>
<script>
  window.addEventListener('DOMContentLoaded', (event) => {
    new PagefindUI({ element: "#search", showSubResults: true });
  });
</script>

这样你就拥有搜索功能了 ヾ(´∀ ˋ)ノ

下面分享一下我是如何集成的。

CI/CD 中添加索引指令

我的博客1是部署在 Netlify,我只需要在构建的最后,添加 一行执行索引的指令 就完成索引了。

#!/bin/bash

# Enable command printing (debug mode)
set -x

# Only build when `publish` change
git diff --quiet $CACHED_COMMIT_REF $COMMIT_REF publish

# index.xml is for https://www.v2ex.com/xna
cp -f publish/rss.xml publish/index.xml

npx -y pagefind --site publish --force-language zh-CN

# Disable debug mode
set +x

如果你仔细看的话,会发现我的指令添加了 --force-language zh-CN 的 option,这会告诉 pagefind 要将不同语言的页面都生成到同一个索引里。

pagefind 的默认逻辑是按照语言各自建立索引,当你的页面的 lang 是 en 的时候,它只会检索 en 页面对应的索引,而不会检索 zh-CN 页面的索引。

但是我希望在一个统一的地方,能够搜索到我博客所有的内容,因此我需要它将不同语言的页面索引放到一起。

引用 pagefind 资源

相比于在文章页面上添加搜索,我更倾向于一个独立的搜索页面,避免搜索结果影响文章页面的布局。单独一个页面,在我不需要的时候也可以轻易移除。

<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <!-- ... -->
    <link href="/pagefind/pagefind-ui.css" rel="stylesheet">
    <script src="/pagefind/pagefind-ui.js"></script>
  </head>
  <body>
    <div id="search"></div>
    <script>
      window.addEventListener('DOMContentLoaded', (event) => {
        new PagefindUI({
          element: "#search",
          showSubResults: true,
          showImages: false,
          autofocus: true,
          excerptLength: 50,
        });
      });
    </script>
  </body>
</html>

页面上我设置了 lang="zh-CN" ,pagefind 会基于 lang 去匹配索引和组件的多语言文字。之前我的页面都是 lang="en" ,pagefind 将页面按照 en 索引,导致我搜索不到任何内容。因此我也更新了博客页面的 lang 属性,从 en 改为了 zh-CN,这样 对于使用屏幕阅读器的人也会友好一点

showImages 我设置成了 false,因为我的博客文章基本没有封面图,图片还会增加搜索时的带宽消耗。

excerptLength 可以考虑设置一个大一点值,这样关键字附近的上下文会多一些。

页面完整代码你可以查看 搜索页面 的源码。

自定义样式

调整 excerpt 的样式

参考 Vanilla CSS is all you need,可以给 <mark> 添加一个样式,看起来像是用记号笔圈起来一样。

pagefind-mark-style.webp
图1  excerpt 的样式

首先,新增一个 <span> 用于承载样式,可以通过 processResult 将 excerpt 返回的 <mark> 替换成 <mark><span></span> 。虽然只用一个 <mark> 也可以实现类似样式,但 border 连接的地方混色会有问题。

new PagefindUI({
  element: "#search",
  showSubResults: true,
  showImages: false,
  autofocus: true,
  excerptLength: 50,
  processResult: function (result) {
    result.excerpt = result.excerpt.replaceAll("<mark>", "<mark><span></span>")
    result?.sub_results.forEach((sr) => {
      sr.excerpt = sr.excerpt.replaceAll("<mark>", "<mark><span></span>")
    })
    return result;
  }
});

然后将下面的样式按需要调整一下,集成到你的样式代码里:

样式
@layer components {
  .circled-text {
    --circled-color: oklch(var(--lch-blue-dark));
    --circled-padding: -0.5ch;

    background: none;
    color: var(--circled-color);
    position: relative;
    white-space: nowrap;

    span {
      opacity: 0.5;
      mix-blend-mode: multiply;

      @media (prefers-color-scheme: dark) {
        mix-blend-mode: screen;
      }
    }

    span::before,
    span::after {
      border: 2px solid var(--circled-color);
      content: "";
      inset: var(--circled-padding);
      position: absolute;
    }

    span::before {
      border-inline-end: none;
      border-radius: 100% 0 0 75% / 50% 0 0 50%;
      inset-block-start: calc(var(--circled-padding) / 2);
      inset-inline-end: 50%;
    }

    span::after {
      border-inline-start: none;
      border-radius: 0 100% 75% 0 / 0 50% 50% 0;
      inset-inline-start: 30%;
    }
  }
}

一些建议

由于 pagefind 依赖 JS,但是用户可能禁用了 JS,或者某些原因导致 pagefind 的 JS 加载错误,此时搜索就失效了。针对这种场景,考虑添加一段文本,引导用户使用搜索引擎搜索:

如果你未启用 JavaScript,也可以使用你喜欢的搜索引擎进行搜索。例如:KagiGoogle

写在最后

如果你还有更好的搜索服务,欢迎分享 (゚∀。)

感谢 pagefind 提供这样简单好用的搜索服务!如果你也有搜索静态页面的需要,试试看吧!

脚注:

1

如果你对我的博客是如何搭建感兴趣,可以看:

Webmentions (加载中...)

如果你想回应这篇文章,可以在你的文章或社交媒体帖子中链接这篇文章,然后提交你的 URL,你的回应随后会显示在此页面上。 (关于 Webmention)


作 者: Spike Leung

创建于: 2024-11-16 Sat 16:26

修改于: 2026-03-13 Fri 21:44

许可证: 署名—非商业性使用—相同方式共享 4.0

支持我: 用你喜欢的方式