Pagefind是为静态网站设计的客户端搜索库。它可以为使用静态站点生成器(SSG)创建的网站或使用Next.js、Gatsby、Hugo、Jekyll等框架构建的站点添加强大的搜索功能。由于可以在本地安装和配置,无需外部API或服务密钥,因此可以非常快速简便地应用。
工作原理
Pagefind通过以下过程运作:
- 索引阶段:在站点构建后,分析HTML文件生成搜索索引。在此阶段,文本内容、标题、元数据等被提取和处理,并生成一个js文件,使索引数据可以被使用。这意味着只有在构建过程中生成HTML时才能使用。
- 搜索API:索引生成后,可以使用提供的JavaScript API在站点上实现搜索界面。
- UI生成:当用户输入搜索词时,Pagefind使用预先生成的索引快速找到相关页面和部分并提供结果,我们只需使用这些结果实现UI即可。
各种功能和使用说明可在文档中找到。
Getting Started with Pagefind | Pagefind — Static low-bandwidth search at scale
快速开始!
编写脚本
让我们直接应用它。无需安装任何包,只需按照以下说明操作。我的开发环境使用Next.js 15
和pnpm
。
首先,在package.json
中添加postbuild
。
"scripts": {
// ...
"postbuild": "npx pagefind --site .next --output-path public/pagefind",
// ...
},
请记住,要使索引工作,必须生成HTML,而要生成HTML,必须构建Next。 添加此脚本并构建后,可以看到public文件夹下出现了pagefind相关文件。
现在只需导入并使用这里生成的pagefind.js
文件。
API调用
以下是完整代码:
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生成
现在我们已经进行了调用,让我们看看结果。
主要可以查看的项目如下:
- excerpt:这是对内容中包含关键词部分的部分解析。
- meta: 从内容中获取元信息。对于标题,它是遇到的第一个
h1
标签,对于图像,它是h1
标签之后遇到的第一个image
标签。
所有这些都可以通过额外选项进行自定义,所以请仔细查看文档!
遇到的问题
我的博客提供韩语、英语、中文和日语四种语言的内容,但在搜索时,所有语言的文章都会出现。
由于多语言页面是常见情况,pagefind自然提供了多语言搜索功能。但它是根据html
标签的lang
属性中的值来判断的。
但在Next.js中,这个设置需要在Layout中完成,而据我所知,在静态页面构建时没有办法获取lang值...(如果有方法,请告诉我)
解决方案
因此,我使用pagefind的filter功能作为变通方法。我在每个页面的h1
标签上添加了以下额外属性:
<h1
data-pagefind-filter="lang[data-lang]"
data-lang={params.lang}
className="text-3xl md:text-5xl font-bold"
>
{title}
</h1>
然后在进行API请求时添加filters
:
const search = await pagefind.search(e.target.value, {
filters: {
lang: "ko",
},
});
总结
通过小巧轻量的库pagefind,我快速实现了搜索功能。由于它独立生成搜索索引而不依赖外部服务,在隐私和控制方面具有优势,并且从用户体验角度提供快速响应时间,因此对于博客或文档站点非常有用。