博客 / 詳情

返回

搜索組件優化 - Command ⌘K

前言:

DevNow 項目中我們使用了 DocSearch 來實現搜索功能,但是由於有以下的限制:

  1. 您的網站必須是技術文檔或技術博客。
  2. 您必須是網站的所有者,或者至少具有更新其內容的權限
  3. 您的網站必須公開可用
  4. 您的網站必須已準備好生產環境。

由於這些條件的限制, DocSearch 只適合用在開源的技術文檔或技術博客中,所以從 DevNow 開源博客項目的定位來説,還是比較適適合的。為什麼突然想着要優化一下搜索組件呢?

今天心血來潮想在 blog 上找一篇文章,用搜素的功能發現搜不出來😂,搜索掛了?然後突然想起來之前由於想着在 blog 中可能加一些私有的配置或者嚐鮮的功能,所有 fork 了一份變成 私有項目了,這樣就不符合 DocSearch 的 網站必須是公開的這個限制了。

其實用下來我個人感覺還有以下幾點不是很滿意的地方:

  1. 配置麻煩,需要在 Algolia 上註冊賬號,然後創建一個應用,然後在項目裏配置一些 api-key 信息。
  2. 搜索信息索引的配置可能需要自己在平台上定製化一些 config ,開始可能會比較迷茫。
  3. 索引可能會不及時,默認一週會重新索引一次,所以剛發佈的文章可能無法被搜索到。

所以一怒之下就想着改一下,之前想着看看其他的方案,之前在在 shadcn/ui 中看到了一個 Command + K 的搜索組件,所以就想着能不能在 DevNow 中也實現一下。這個實現的方案是基於 cmdk 這個庫來實現的, Nextjs 官網應該也是這個實現方案。

cmdk

集成 shadcn/ui

在這裏還是推薦使用 shadcn/ui 來集成,因為它提供了很多的組件,而且還提供了一些 hooks 來幫助我們實現一些功能。想着後續可能還會用到一些組件,所以就直接集成了,不得不説這個UI庫的樣式真是深得我心啊。

安裝的話直接跟着 文檔 來就好,提供了一些常見框架的集成:

文檔

:::tip[注意]
在安裝前要先在 tsconfig.json 中配置好 resolve paths
@/* 這個是必須有的,否則 shadcn/ui 無法初始化成功

{
  "compilerOptions": {
    // ...
    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"] // <--- 這個必須有
    }
    // ...
  }
}

:::

安裝 Command

文檔在 這裏

pnpm dlx shadcn@latest add command

一個簡單的例子



const Search: FC<Props> = ({ category }) => {
  const [open, setOpen] = useState(false);
  useEffect(() => {
    const down = (e: KeyboardEvent) => {
      if (e.key === 'k' && (e.metaKey || e.ctrlKey)) {
        e.preventDefault();
        setOpen((open) => !open);
      }
    };

    document.addEventListener('keydown', down);
    return () => document.removeEventListener('keydown', down);
  }, []);

  return (
    <div className='mr-4 hidden items-center lg:flex'>
      <Button
        onClick={() => setOpen((open) => !open)}
        size='sm'
        className='h-[32px] bg-muted/50 px-2 py-0 text-sm text-muted-foreground shadow-none hover:bg-accent focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring'
      >
        Search
        <kbd className='pointer-events-none ml-1 inline-flex h-5 select-none items-center gap-1 rounded border bg-muted px-1.5 font-mono text-[10px] font-medium text-muted-foreground opacity-100'>
          <span className='text-xs'>⌘</span>K
        </kbd>
      </Button>

      {config.search && (
        <CommandDialog open={open} onOpenChange={setOpen}>
          <CommandInput placeholder='Type a command or search...' />
          <CommandList>
            <CommandEmpty>No results found.</CommandEmpty>
            {category.map((item, index) => (
              <Fragment key={index}>
                <CommandGroup heading={item.label}>
                  {item.children.map((child, cIndex) => (
                    <CommandItem
                      // 這裏需要注意的是:
                      // 選項點擊選中的事件是 onSelect 不是 onClick
                      onSelect={() => {
                        window.open(`${location.origin}/posts/${child.id}`, '_blank');
                      }}
                      key={cIndex}
                      className='cursor-pointer'
                    >
                      <span>{child.label}</span>
                    </CommandItem>
                  ))}
                </CommandGroup>
                {index === category.length - 1 && <CommandSeparator />}
              </Fragment>
            ))}
          </CommandList>
        </CommandDialog>
      )}
    </div>
  );
};

效果展示:

使用 DocSearch

DocSearch

使用 command 替換後:

command

大家覺得哪個更好呢,我個人覺得新版的會更加簡潔一點。

遇到的問題

第一版的實現到這裏就結束了,然後上線後發現了問題。發現線上的環境突然變卡了,請求也變多了,一看發現堵了很多請求:

文件請求問題

然後就是📌定位問題,發現原因是: 當我在 Astro 中集成 React 框架時,如果組件是運行要求在客户端運行時:

<Search client:only='react' />

在通過 client:only 來標記的組件中獲取所有文件的標題時,會把所有的請求一遍,這裏我的理解是在客户端沒有這些信息,所有執行的時候會構建一份。

這裏如果改下數據源,將數據傳遞給 Search的話,可以規避掉這個問題。


<Search category={category}  client:only='react'>

原文鏈接:搜索組件優化 - Command ⌘K

user avatar columsys 頭像 lihaixing 頭像 u_12219 頭像 shellingfordly 頭像 hexuan_5ea1a9d57db1a 頭像 zuckjet 頭像 yumiko_5c088de8aa1fe 頭像 aixiaodeludan_bqazwx 頭像 kuanrongdebeizi 頭像 chaoqipengbodehanbaobao 頭像 movestar 頭像 jjyin 頭像
15 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.