WebAssembly 客戶端搜尋的實戰突破:從 VS Code docfind 看靜態網站的新世代架構

WebAssembly 客戶端搜尋的實戰突破:從 VS Code docfind 看靜態網站的新世代架構

如果你最近有用過 VS Code 官方文件的搜尋,應該會對那種「一打字就有結果」的流暢感印象深刻:沒有頁面跳轉、沒有明顯等待時間,整個搜尋就像是本地 App 的體驗,而不是傳統那種要丟給伺服器跑一輪的 Web 搜尋。

這背後的關鍵,就是他們新打造的搜尋引擎 docfind——一個用 Rust 開發、再編譯成 WebAssembly(WASM),完整在瀏覽器端執行的搜尋系統。索引、演算法、查詢流程,全都被封裝在使用者的瀏覽器裡跑完,不再依賴伺服器端的搜尋服務。

從工程實務角度來看,docfind 的設計不只是「把搜尋搬到前端」這麼簡單,而是針對靜態網站搜尋長期存在的痛點,一次性做了一組非常有參考價值的技術組合:高壓縮索引結構、多階段的字串壓縮、模糊搜尋、自動化關鍵字抽取,再加上很工程味的二進位修補(patching)手法,把整體體驗壓到極致。

### 靜態網站搜尋的既有選項:功能、成本與體積的拉扯

先把背景講清楚。過去我們在做文件站或技術部落格的搜尋,大概會在下面幾條路線中選一條:

1. **Algolia / Typesense 這類 Search-as-a-Service**
– 優點:查詢品質好、功能成熟(排名、同義詞、分析報表等一應俱全)。
– 缺點:要付費、要管理 API Key、要處理索引同步,而且把搜尋流量綁在外部服務上。

2. **Lunr.js 這類純 JavaScript 本地搜尋庫**
– 優點:完全在客戶端跑,不需要自己的搜尋伺服器。
– 缺點:索引體積很容易炸裂。實際數字:3MB 的原始內容,索引可能膨脹到 10MB 甚至更多,對於初次載入是一種負擔。

3. **Stork 之類 WebAssembly 搜尋工具**
– 優點:同樣走 WASM,本地執行,效能比純 JS 好。
– 缺點:索引體積對某些場景仍舊不夠精簡,而且專案維護活躍度、長期可靠性要打個問號。

4. **傳統搜尋引擎的站內搜尋(例如 Google Custom Search)**
– 優點:實作成本低,幾乎不用維護。
– 缺點:體驗很割裂,使用者被丟去搜尋頁面,沒辦法做到即時預覽、即打即顯示。

5. **乾脆不做搜尋,只靠側欄 / 導覽**
– 對幾十篇文章的部落格可能勉強可以,對幾千份文件的技術文檔來說就完全不實際了。

docfind 要解決的,就是:**能不能在「完全客戶端執行」的前提下,同時兼顧索引體積、搜尋品質與維護成本?**

### WebAssembly + Rust:帶來的幾個關鍵結構性改變

VS Code 團隊的選擇是:用 Rust 寫核心邏輯,透過 WebAssembly 跑在瀏覽器裡,再搭配一組非常有針對性的演算法與資料結構來壓縮索引、優化查詢體驗。

下面分點整理 docfind 幾個最關鍵的技術決策,這些也是我覺得最值得借鏡的部分。

#### 1. 用 FST(確定有限狀態轉換器)做索引:把字串搜尋壓榨到極致

docfind 採用 Rust 的 `fst` 函式庫,把所有需要搜尋的字串(像是關鍵字、詞片段)放進 FST(Finite State Transducer)結構中。

FST 的特性是:

– 能在「排序後的字串集合」上做到非常高壓縮率的儲存。
– 查詢時走的是一條確定的狀態路徑,不需要逐一掃描所有字串。

直覺一點的比喻:
不是在城市裡亂走找地址,而是整座城市的地址都事先排好,組成一張高度壓縮的「路線圖」,你一輸入關鍵字,就直接沿著那張圖走到結果。

對搜尋來說,這有兩個直接好處:

– **索引檔變小**:減少下載時間與瀏覽器記憶體佔用。
– **查詢延遲極低**:可以支援「使用者一打字就即時回應」的互動體驗。

#### 2. RAKE 關鍵字抽取:讓搜尋了解文件的「重點」是什麼

docfind 沒有單純把全文一次塞進索引,而是先用 RAKE(Rapid Automatic Keyword Extraction)演算法,從每份文件中抽取出代表性關鍵字與片語。

RAKE 做的事,簡化說就是:

– 從文本裡拆出候選片語。
– 根據出現頻率、共現關係等特徵,幫每個片語打分。
– 最後留下最有代表性的那一批關鍵字/詞組。

這層處理的收益有:

– **索引更聚焦**:不需要為每個微不足道的字建索引,縮小體積。
– **結果相關性更好**:使用者隨便打一個關鍵詞,就能匹配到被 RAKE 判定為「重點」的內容,而不是被大量雜訊淹沒。

對技術文件站來說,這尤其重要。很多文件用詞重複度很高,如果不做關鍵字抽取,結果很容易被大量不關鍵的字占滿。

#### 3. 用 FSST 壓縮短字串:每一個 byte 都要省

docfind 進一步在標題、摘要等短字串上套用了 FSST(Fast Static Symbol Table)短字串壓縮演算法。

FSST 的思路是:

– 根據資料內容建立一張靜態的符號表。
– 用這張表把常見的字串片段轉成更短的編碼。

這對搜尋索引來說非常剛好:

– 標題、摘要、關鍵字這些字串在索引裡大量重複出現。
– 一旦壓縮率好,整體索引大小就能顯著往下壓。

從使用者角度感受是什麼?
就是在網路不是那麼理想的環境下,**整套搜尋功能仍然能很快載入**。你感覺不到背後下載了一個「搜尋引擎」,但網頁就是能秒給結果。

#### 4. 二進位「修補」:讓編譯只做一次,後續流程直接改檔案

docfind 裡一個非常工程味、但又很實用的設計,是所謂的「範本修補」機制。

流程大致是:

1. 預先編譯出一個「帶空索引」的 WebAssembly 模組(WASM 檔)。
2. 在 CI / build 流程中,用 CLI 工具把真實的索引資料寫進 WASM 的全域變數或 data segment,等於直接改二進位檔。
3. 實際部署時只需要丟這個「已帶索引的 WASM 檔」給前端載入。

優點非常明確:

– **不用在每次索引更新時重跑整個編譯流程**,節省時間與 CI 資源。
– 可以把「程式碼」和「資料」分層思考:程式碼編譯一次,資料直接 patch 進去。
– 部署上看起來就是一個靜態檔案更新,非常符合靜態網站的思維。

這做法某種程度顯示了他們對 WASM 二進位格式的熟悉程度,也說明了:**要把效能和開發流程拉到極致,有時真的得下探到這個層級。**

#### 5. Levenshtein 自動機:拼錯字也要給你好結果

真實世界裡,使用者輸入關鍵字一定會打錯。docfind 在 WebAssembly 裡實作了 Levenshtein automaton,用來支援模糊匹配。

簡單講:

– 不是只匹配「完全相同的字串」,而是允許一定程度的編輯距離(多一字、少一字、打錯一個字母)。
– Levenshtein 自動機可以在這個容錯空間內高效地找出「距離最近」的候選字串。

實際體驗就是:

– 你打錯 API 名稱或變數名字母,搜尋結果仍然能命中正確的文件。
– 對英文技術關鍵字來說特別有感,打錯一個字母不至於讓你搜不到任何東西。

這讓整個系統更「耐用」,不會因為人類本來就會打錯字,而大幅下降搜尋的實用性。

#### 6. 單一 WASM 模組封裝:部署、維護成本直接歸零

最後一個很有魅力的設計,是 docfind 最終被打包成「單一 WASM 檔」,前端只要在偵測到使用者有搜尋意圖時,再載入這個模組。

這種作法有幾個實際好處:

– **不需要後端搜尋伺服器**:整體架構維持純靜態部署。
– **部署簡單**:更新索引等於更新一個靜態檔,丟到 CDN 就好。
– **可漸進啟用**:使用者沒開啟搜尋,就不會下載這個 WASM 檔,不浪費頻寬。

對於個人部落格、技術文件站、中小團隊的文件平台來說,這樣的設計非常友善:只需要在 build 流程中產出一個 WASM 搜尋模組,之後就幾乎不用再管任何「服務維運」層面的事情。

### 產業層面的影響:搜尋服務不再只能是伺服器的專利

站在整體產業的角度來看,我覺得 docfind 這一路線有幾個值得注意的變化:

1. **靜態網站終於有了「高品質本地搜尋」的實戰範例**
過去要在靜態網站上做好搜尋,不是綁 SaaS,就是接受 JS 搜尋庫的效能瓶頸。
docfind 用 Rust + WASM + 一組精挑細選的演算法,展示了另一條路:
– 零伺服器成本
– 體積可控、效能極佳
– 體驗比傳統站內搜尋好一個級距

2. **對文件搜尋 SaaS 的壓力會逐漸浮現**
對具備一定工程實力的團隊來說:
– 自建搜尋不再意味著要自己維運一個 Elasticsearch 或 Meilisearch。
– 把搜尋功能整合進 CI / build pipeline,長期成本可能比綁第三方服務更低。
這難免會擠壓一部分「以文件搜尋為主打」的小型 SaaS 的生存空間。

3. **離線、內網場景的優勢非常明顯**
尤其是在:
– 企業內網技術文件中心
– 離線可用的桌面應用或 PWA
– 隱私要求高、不方便把資料丟上雲端的場景
WebAssembly 本地搜尋幾乎是完美解:資料不出機器、無需對外服務、體驗又好。

4. **對前端工程師的技術要求提升,但也是一種「職能升級」**
要駕馭這種架構,你得理解:
– WebAssembly 模組如何在前端整合
– Rust / C 等語言與 JS 的互操作(FFI)
– 基本的索引結構與壓縮演算法概念
這會讓前端角色更貼近「全端演算法工程師」,不再只是 API 消費者,而是能直接對底層結構做優化的人。

### 總結:WebAssembly 正在改寫「前端能做到什麼」的邊界

docfind 展示的是一種很有說服力的未來樣貌:

– 搜尋這種原本被認為「一定要伺服器做」的工作,完全搬到瀏覽器執行。
– 透過 Rust 與 WebAssembly,把近乎原生等級的效能帶到客戶端。
– 用 FST、RAKE、FSST、Levenshtein automaton 這些專業級工具鏈,讓索引既小又快,體驗媲美 native。

我會把 docfind 看成一個清楚的信號:
**當 WebAssembly 生態漸漸成熟,會有越來越多我們原本習慣放在雲端、丟給伺服器做的重任,被重新思考、重新設計,最後落在使用者的裝置上。**

對開發者來說,這代表兩件事:

– 一方面,我們能用更低的運營成本,給使用者更好的體驗。
– 另一方面,也得開始認真面對「前端不只是畫面」的世界:演算法、資料結構、編譯與二進位格式,通通會變成日常工具箱的一部分。

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *