使用pagefind实现博客搜索功能

profile image

利用为静态网站设计的客户端搜索库pagefind,快速简便地为博客添加了搜索功能。

本帖由 Jetbrains's Coding Agent Junie junie logo翻译。如有任何翻译错误,请告知我们!

Pagefind是为静态网站设计的客户端搜索库。它可以为使用静态站点生成器(SSG)创建的网站或使用Next.js、Gatsby、Hugo、Jekyll等框架构建的站点添加强大的搜索功能。由于可以在本地安装和配置,无需外部API或服务密钥,因此可以非常快速简便地应用。

工作原理

Pagefind通过以下过程运作:

  1. 索引阶段:在站点构建后,分析HTML文件生成搜索索引。在此阶段,文本内容、标题、元数据等被提取和处理,并生成一个js文件,使索引数据可以被使用。这意味着只有在构建过程中生成HTML时才能使用。
  2. 搜索API:索引生成后,可以使用提供的JavaScript API在站点上实现搜索界面。
  3. UI生成:当用户输入搜索词时,Pagefind使用预先生成的索引快速找到相关页面和部分并提供结果,我们只需使用这些结果实现UI即可。

各种功能和使用说明可在文档中找到。

Getting Started with Pagefind | Pagefind — Static low-bandwidth search at scale

快速开始!

编写脚本

让我们直接应用它。无需安装任何包,只需按照以下说明操作。我的开发环境使用Next.js 15pnpm

首先,在package.json中添加postbuild

json
"scripts": {
  // ...
  "postbuild": "npx pagefind --site .next --output-path public/pagefind",
  // ...
},

请记住,要使索引工作,必须生成HTML,而要生成HTML,必须构建Next。 添加此脚本并构建后,可以看到public文件夹下出现了pagefind相关文件。

Image.png

现在只需导入并使用这里生成的pagefind.js文件。

API调用

以下是完整代码:

typescript
export default function Search() {
  const [search, setSearch] = useState("");
  const [results, setResults] = useState<PagefindResult[]>([]);
  const [pagefind, setPagefind] = useState<any>(null);

  useEffect(() => {
    const initPagefind = async () => {
      try {
        // 尝试在运行时动态加载

        setPagefind(
          await import(
            // @ts-expect-error
            "./pagefind/pagefind.js"
          ),
        );
      } catch (error) {
        console.error("Pagefind初始化失败:", error);
      }
    };

    // 仅在客户端执行
    if (typeof window !== "undefined") {
      initPagefind();
    }
  }, []);

  const handleSearch = async (e: any) => {
    setSearch(e.target.value);
    if (!pagefind || e.target.value === "") {
      setResults([]);
      return;
    }

    const search = await pagefind.search(e.target.value);
    const results = await Promise.all(search.results.map((r) => r.data()));
    console.log(results);
    setResults(results);
  };

  return (
    <div>
      <input
        type="text"
        value={search}
        onChange={handleSearch}
        placeholder="请输入搜索词..."
      />

      <div>
        {results.map((result, i) => (
          <div key={result.url}>
            <a href={result.url}>{result.meta.title}</a>
          </div>
        ))}
      </div>
    </div>
  );
}

UI生成

现在我们已经进行了调用,让我们看看结果。

Image.png

主要可以查看的项目如下:

  • excerpt:这是对内容中包含关键词部分的部分解析。
  • meta: 从内容中获取元信息。对于标题,它是遇到的第一个h1标签,对于图像,它是h1标签之后遇到的第一个image标签。

所有这些都可以通过额外选项进行自定义,所以请仔细查看文档!

遇到的问题

我的博客提供韩语、英语、中文和日语四种语言的内容,但在搜索时,所有语言的文章都会出现。

Image.png

由于多语言页面是常见情况,pagefind自然提供了多语言搜索功能。但它是根据html标签的lang属性中的值来判断的。

但在Next.js中,这个设置需要在Layout中完成,而据我所知,在静态页面构建时没有办法获取lang值...(如果有方法,请告诉我)

解决方案

因此,我使用pagefind的filter功能作为变通方法。我在每个页面的h1标签上添加了以下额外属性:

html
<h1
  data-pagefind-filter="lang[data-lang]"
  data-lang={params.lang}
  className="text-3xl md:text-5xl font-bold"
>
  {title}
</h1>

然后在进行API请求时添加filters

typescript
const search = await pagefind.search(e.target.value, {
  filters: {
    lang: "ko",
  },
});

总结

通过小巧轻量的库pagefind,我快速实现了搜索功能。由于它独立生成搜索索引而不依赖外部服务,在隐私和控制方面具有优势,并且从用户体验角度提供快速响应时间,因此对于博客或文档站点非常有用。