Pagefind,但是 React

5/10/2024
0
折腾

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();
  }, []);
}

这里有几个地方值得注意:

  1. pagefind.js 不是作为依赖项导入,而是作为静态文件资源导入,/*@vite-ignore*/ 就是告诉 vite 不要处理这个依赖
  2. 为什么导入语句后要增加 ?${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 并不会编译这个组件,而是把它放到页面中,让客户端去加载。


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