使用 pagefind 实现博客搜索功能

🌐

本帖由 DeepL 翻译。如有任何翻译错误,请告知我们!

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

工作原理

Pagefind 的工作流程如下

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

你可以在文档中了解更多关于这些功能和使用方法的信息。

Pagefind 入门 | Pagefind - 大规模静态低带宽搜索

快速入门!

编写脚本

让我们开门见山地开始吧!你不需要安装任何软件包,只需按照以下步骤操作即可。开发环境使用的是Next.js 15 和 pnpm

首先,在 package.json 中添加postbuild

"脚本":{ { ...
  // ...
  "postbuild":"npx pagefind --site .next --output-path public/pagefind"、
  // ...
},

**请注意,为了被索引,需要生成 HTML,而为了生成 HTML,需要构建 NEXT。**添加此脚本并运行编译后,您应该可以在 public 文件夹下看到与 pagefind 相关的文件。

Image.png

现在,我们只需导入并使用在此创建的pagefind.js

API 调用

首先,完整的代码如下所示

导出默认函数 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([]);
      返回;
    }

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

  返回 (
    <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
    </div
  );
}

创建用户界面

现在我们已经完成了调用,让我们来看看结果。

Image.png

您需要查看的主要内容有

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

所有这些都可以通过其他选项进行自定义,请查看文档!

问题

我的博客有四种语言(韩语、英语、中文和日语),搜索显示的是所有语言的文章。

Image.png

对于多语言页面来说,这种情况并不少见,因此 pagefind 当然提供了多语言搜索功能。不过,它是通过html标签的lang属性值来判断的。

但在 Next.js,你必须在 Layout 中设置该属性,而且据我所知,在创建静态页面时没有办法获取 lang 值...(如果有,请告诉我)

变通方法

于是我采取了一种变通方法,使用 pagefind 中的过滤器功能。我在每个页面的h1标签中添加了以下附加属性

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

当我们进行 API 请求时,我们可以添加过滤器

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

结论

我们已经了解了如何使用小型轻量级库 pagefind 快速实现搜索功能。在不依赖外部服务的情况下创建自己的搜索索引,不仅可以保护隐私和进行控制,还能提供快速响应的用户体验,这对运行博客或文档网站非常有用。