Skip to main content

搜尋功能

本文簡單介紹適合靜態網站的搜尋套件,這些套件不限於 Hugo,所有 SSG 都能使用。這些套件侷限在無伺服器的搜尋套件,不包含需要後端伺服器的套件,包含

其中 Fuse.js 和 Pagefind 有經過撰文者實際使用測試,其他三者的資訊則是由 Claude 閱讀原始碼後整理出來的。

搜尋系統的工作原理

大多數的搜尋套件都需要兩大步驟完成搜尋:先建索引表,搜尋時拆字查表。這代表將這些工具嵌入到網站的步驟是「建置時也要建立索引,客戶端搜尋時查詢索引」。在非 CJK 語言中建立索引表相對容易,因為每個單字天生都有空白間隔,然而 CJK 語言全部黏在一起,因此適用英文的搜尋工具很有可能在 CJK 語言搜尋結果表現的一踏糊塗。

Fuse.js

Fuse.js 恰好是不需要建索引表的套件,使用 Bitap 演算法,優點是無須索引,缺點是毫無詞彙理解,對他來說所有字元都是單一獨立的,完全沒有詞彙的概念,因此文章越多搜尋表現越糟糕。

除此之外,Fuse.js 只提供引擎,你需要手動建立整個搜尋介面。

Fuse.js 由於不需索引,因此在 Hugo 中使用可以做到 0 JS 參與,JSON 表用 Hugo 的 outputFormats 完成,客戶端搜尋的 JS 用 CDN/resources.Get/自己 vendor 套件。

FlexSearch

TODO

Pagefind

Pagefind 的原理就和段落開頭講的完全一樣,標準的索引表和查表流程。對比其他工具的優勢是 pagefind 會將索引表拆成多個子表,因此即使頁面超級多也完全不必擔心用戶需要等待瀏覽器下載一個超大的 JSON 表。

Pagefind 的缺點是客戶端需要載入更大的包才能下載,雖然小於 100KB,甚至比一張圖片還小了,但是對於個人網站來說整個 JSON 表都很難超過 100KB。Pagefind 官方完全沒有提到任何有關 AJAX/SPA 功能的支援,沒有提供初始化、銷毀的 API,這種網站不適合使用 Pagefind。

Pagefind 的優點是他確實會切字,確保更好的中文搜尋體驗,然而他為了確保客戶端輕量就使用了瀏覽器原生的 Intl.Segmenter,中文表現尚佳,但是索引和查詢使用不同的拆字方式明顯會有問題,請見以下 issue

除此之外,Pagefind 不只提供引擎,還提供了完整的搜尋介面,只不過自定義的部分不是很完善。

tinysearch

tinysearch 的目的是 tiny,search 精確度只是次要的。tinysearch 的算法依賴詞界,由於只用空白分詞因此和 CJK 語言不相容。

詳見開發者本人介紹 tinysearch 的文章

Orama

TODO

五者對照總結

[!INFO] 此表格由 Claude 閱讀各個專案的源碼後完成。

Fuse.jsFlexSearchPagefindtinysearchOrama
索引時機查詢時即時計算客戶端 JS建置期建置期客戶端 JS
CJK 分詞方式算法特性無須分詞逐字元 unigram(無語意)中文最強的 Jieba 系斷詞不支援官方擴充套件 @orama/tokenizers 用 Intl.Segmenter
查詢端分詞算法特性無須分詞同套 JS encoder瀏覽器原生 Intl.Segmenter不支援瀏覽器原生 Intl.Segmenter
CJK 品質中等(容錯但無詞界概念)中等(雜訊多)最佳(真正的詞界算法)最差良好(有基本詞界)

筆者推薦

使用 Pagefind,沒什麼好調整的,維護負擔輕鬆很多。

Fuse.js 和 FlexSearch 都要自己做完整的搜尋互動,簡單一點的實現,Blowfish 需要

複雜一點的像 hextra 需要

無論是哪種版本都至少要維護 JS/JSON/HTML,都是 UI/UX 方面的設定。這些甚至都還沒有做到多語言的優化(hextra 的 flexsearch.js 不算優化,只是硬切詞讓 CJK 從不能搜尋變成能搜尋),而 Pagefind 是開箱即用,沒有維護負擔的。

反過來說如果想要自定義外觀,Pagefind 就不是那麼適合,只是做搜尋功能我們要的是好看的外觀,還是好的搜尋結果呢?