如何使用 Pagefind 为 Astro 网站添加全文搜索

4/25/2024
0
折腾

我最近在用 Astro 重构自己的博客网站,本来想给网站添加一个搜索功能,在 Astro 官网搜索一番后,发现官方没有做相关的插件或者教程,只有一条关于 Pagefind 的教程链接,我抱着试一试的心态去看了一下,然后结合 Pagefind 文档,花了一天时间成功搞定,效果如下:

astro & pagefind 效果图

Pagefind

我们先来看下官方的介绍:

Pagefind is a fully static search library that aims to perform well on large sites, while using as little of your users’ bandwidth as possible, and without hosting any infrastructure.

我英语不好,用 ChatGPT 翻译看一下:

Pagefind 是一个完全静态的搜索库,旨在在大型网站上表现良好,同时尽量减少用户的带宽使用,并且无需托管任何基础设施。

其实就是说 Pagefind 是一个静态的全文本搜索库,拥有良好的性能以及多平台支持。多提一句,它是基于 Rust 实现的。

创建索引

首先将 pagefind 作为依赖安装到我们的项目:

pnpm add astro-pagefind pagefind

其次,我们要为 Pagefind 创建索引,它的实现方式是提取构建的静态文件,一般都是在 dist 文件夹创建索引:

pnpx pagefind --site dist

最好把创建索引的命令放在 package.json 中,这样,每次 CI 构建时,都会自动创建最新索引,例如:

{
  "scripts": {
    "build": "astro build && pagefind --site dist"
  }
}

注意,本地开发的时候,我们需要先运行 pnpm build 创建索引,然后再启动开发服务器 pnpm dev,这样在开发时也会有索引。

搜索 UI

上面的步骤运行完之后,我们可以看到 Pagefind 在 dist 目录下生成的文件。由于 Pagefind 自带默认的搜索 UI,可以直接从 dist 目录下引用。如果你想自己构建搜索 UI,那么只需要引入 pagefind.js 即可,这也是我选择的开发方式。

我们可以把搜索 UI 封装成一个组件,比如 Search.astro,首先完成我们的 HTML UI 结构:

<a class="font-bold hover:text-blue-500 cursor-pointer transition-colors duration-200" id="search-icon">/search</a>

<div class="hidden" id="search-box">
  <div
    id="search-backdrop"
    class="fixed z-[1000] left-0 top-0 backdrop-blur-3xl h-screen w-screen overflow-hidden flex justify-center items-center bg-zinc-900/20 dark:bg-zinc-500/20"
  >
  </div>
  <div
    class="fixed left-1/2 top-32 -translate-x-1/2 z-[1001] w-[560px] bg-zinc-200 rounded-lg overflow-hidden shadow-xl dark:bg-zinc-900"
  >
    <div class="p-4">
      <input
        id="search-input"
        class="w-full p-2 border-2 border-blue-500 rounded-lg focus:ring-0 focus:border-blue-500 outline-none dark:bg-zinc-950"
        type="text"
        placeholder="搜索文档"
      />
    </div>

    <div id="results" class="min-h-52 max-h-96 p-4 pt-0 overflow-auto space-y-2">
      <div class="w-full min-h-48 text-5xl text-center flex justify-center items-center select-none">
        <div>🤔🔍</div>
      </div>
    </div>
  </div>
</div>

引入 Pagefind JS 文件:

{/* ...html */}
<script type="module">
  const pagefind = await import('/pagefind/pagefind.js');
  pagefind.init();
</script>

用户输入关键词后,调用 Pagefind API 实现搜索逻辑,并且将搜索结果渲染到页面上,我在这里使用了拼接 DOM 的方式,如果使用内置 UI 的话,就不用拼接。

<script type="module">
const searchInput = document.querySelector('#search-input');
searchInput.addEventListener('input', handleSearch);
async function handleSearch(e) {
    const { value } = e.target;
    let html = '';
    const search = await pagefind.search(value);
    const results = search.results.map((item) => item.data());
    const data = await Promise.all(results);
    data.map(({ meta: { title }, url, excerpt }) => {
      html += `
      <div class="bg-white dark:bg-zinc-800 p-2 rounded-lg shadow-xl">
        <a href="${url}" class="text-blue-500 hover:text-blue-600 transition-colors duration-200">${title}</a>
        <p class="text-gray-500 text-sm line-clamp-1 text-ellipsis mt-1 ml-4">${excerpt.replaceAll('<mark', '<mark class="bg-blue-500 text-white"')}</p>
      </div>
    `;
    });
    resultEl.innerHTML = html
}
</script>

最后,我把它引入到了 Header 组件,这样保证了搜索功能在全局都是可用的。

---
import Search from "@/components/Search.astro";
---
{/* ... */}
<Search />
{/* ... */}

优化搜索结果

开发完成后,我在本地试了下搜索功能,结果有点糟糕。我们之前创建的索引是包含了 dist 整个目录下的文件,所以会有比较多的冗余信息,所以需要进行过滤。

页面级限制 data-pagefind-body

data-pagefind-body 属性可以可以添加到页面的 body 元素上,如果我们的网站上存在一个此属性,那么在创建索引时,其余没有添加此属性的页面都会被忽略。

如果你只想创建某个页面的索引,就在页面的 body 元素上添加此属性,此时,其它页面的都不会创建索引。

<body data-pagefind-body>
{/*  */}
</body>

元素级限制 data-pagefind-ignore

如果你想限制页面上的某些元素不能被索引时,可以添加 data-pagefind-ignore 属性,例如敏感信息等:

<div class="phone" data-pagefind-ignore>
  {/*  */}
</div>

属性级限制 data-pagefind-index-attrs

如果你想把某些元素的 HTML 的属性也添加到索引里的话,可以使用在指定元素上添加此属性,这在搜索图片时非常有用:

<img src="/hero.png" title="Image Title" alt="Image Alt" data-pagefind-index-attrs="title,alt" />

总结

通过上面这些开发配置,Pagefind 作为我博客的搜索引擎已经可以满足日常使用了。如果你需要其它功能,可以参考官方文档开发和配置。

最后,我想说 Pagefind 目前在 GitHub 上有 3k 多 star,而 Issues 也不多,98 个左右,所以作为一个轻量级的文本搜索库来讲,足够在个人博客里使用,而且它在官网上也说了,能够支撑大型网站。免费又开源,这还不得赶紧去人家的 GitHub 上点个 star 嘛。


商业转载请联系站长获得授权,非商业转载请注明本文出处及文章链接,您可以自由地在任何媒体以任何形式复制和分发作品,也可以修改和创作,但是分发衍生作品时必须采用相同的许可协议。
本文采用 CC BY-NC-SA 4.0 - 非商业性使用 - 相同方式共享 4.0 国际进行许可。