May 10,2024
0
折腾
AI 摘要
TL;DR
这是对上一篇文章的补充,聊一下如何利用 React 来提升开发体验,因为上一篇提到的 Pagefind 需要进行拼接 DOM,我们把这部分逻辑放到 React 中。
Astro 中使用 React 组件
首先我觉得 Astro 的设计理念挺不错的,“群岛架构”——开发者可以把页面划分为不同的岛屿,有静态的、有可交互的,还可以集成不同的框架。我们要聊的 React 搜索组件就是利用了这个思想,把搜索组件作为一个可交互的组件集成到 Astro 页面中。
引入 pagefind.js
上篇文章我们借助了 ES Module 通过 script 标签的方式引入了 pagefind.js。但是 React 我们就需要换一种方式了,因为在 Module 中无法直接访问 window。经过研究之后,我发现可以使用动态导入的方式来实现。
export function Search() {
useEffect(() => {
async function loadPagefind() {
try {
// @ts-ignore import after building
window.pagefind = await import(/*@vite-ignore */ `/pagefind/pagefind.js?${Date.now()}`);
await window.pagefind.init();
} catch (e) {
window.pagefind = { search: () => ({ results: [] }) };
}
}
loadPagefind();
}, []);
}
这里有几个地方值得注意:
- pagefind.js 不是作为依赖项导入,而是作为静态文件资源导入,
/*@vite-ignore*/
就是告诉 vite 不要处理这个依赖 - 为什么导入语句后要增加
?${Date.now()}
参数?如果没有这个参数,会报Module not found
,具体原因可以参考这条 issue
处理搜索结果
export function Search() {
const [results, setResults] = useState([]);
// ...
const handleSearch = async (keyword: string) => {
const { results } = await window.pagefind.search(keyword);
const data = await Promise.all(results.map((item: any) => item.data()));
setResults(data);
};
// ...
return (
<>
{results.map((item) => (
// 构建 UI
))}
</>
)
}
上一篇文章这个步骤会涉及到拼接 DOM 的部分,这里我们把它抽离成 React 逻辑,不用拼接 DOM 了,效率 Up。
client:load
最后,我们把组件注册到 Astro 的页面中:
---
import {Search} from '@/components/Search';
---
<Search client:load />
client:load
指令的作用,可以参考文档。简单理解就是 Astro 并不会编译这个组件,而是把它放到页面中,让客户端去加载。