Pagefind 教學:如何為 Eleventy 靜態網站加入純前端搜尋

文章一多,讀者只靠導覽列和分類頁,找舊文就像在咖啡渣裡找關鍵字。這時先別急著架資料庫:對以文章為主的 Eleventy 靜態網站來說,Pagefind 通常已經很夠用。

這篇會帶你弄懂 Pagefind 的工作方式,接著完成 layout 標記、build 後建立索引,以及繁體中文搜尋的驗收。讀完後,你應該能判斷 Pagefind 是否適合自己的網站,也知道搜尋失靈時該從哪裡查起。

Pagefind 是什麼?

Pagefind 是在 build 後處理靜態 HTML 的搜尋工具。它會讀取網站輸出的 HTML,建立一組可由瀏覽器載入的索引檔;訪客搜尋時,結果在前端計算,不需要另開一個搜尋 API 或資料庫伺服器。

所以它很適合個人部落格、文件站和產品說明站。文章更新後重新 build,索引也跟著重建。反過來說,如果你的資料需要登入權限、個人化結果或即時更新,Pagefind 不是後端搜尋的替代品——咖啡濾紙也不能拿來當資料庫。

開始前先確認三件事

你需要一個能正常輸出 _site/ 的 Eleventy 專案,並確認共用 layout 的 <html> 有語言標記:

<!doctype html>
<html lang="zh-Hant">
	<head>
		
	</head>
	<body>
		
	</body>
</html>

語言標記不是裝飾。Pagefind 會在索引時讀取 <html lang>,依語言分開建立索引;瀏覽器初始化搜尋時,也會依目前頁面的語言載入對應索引。繁體中文網站通常可以使用 zh-Hant,但仍應用實際文章內容測試。

另外,請先單獨確認 Eleventy build 沒有錯誤。Pagefind 只能處理已經產生的靜態檔案,不能替你修好前一步的模板錯誤。

第一步:選擇 Pagefind UI 版本

Pagefind 的搜尋索引和搜尋介面是兩件事。索引先由 CLI 建立,UI 再載入 /pagefind/ 裡產生的資產。

目前常見的兩種 UI 寫法,不能直接混在一起:

  • Pagefind 1.4 及更早版本常用 Default UI:載入 pagefind-ui.csspagefind-ui.js,再用 new PagefindUI(...) 初始化。
  • Pagefind 1.5 開始提供 Component UI:使用 pagefind-component-ui.csspagefind-component-ui.js 和自訂元素,例如 <pagefind-modal>

先看專案的 package.json 版本,再照對應文件操作。本文以 Pagefind 1.4 的 Default UI 示範,因為這是目前專案使用的版本。

在共用 layout 放入 CSS 與搜尋容器:

<link rel="stylesheet" href="/pagefind/pagefind-ui.css">

<div id="search"></div>

接著在 JavaScript 中載入並初始化 UI:

const pagefindScript = document.createElement('script');
pagefindScript.src = '/pagefind/pagefind-ui.js';
pagefindScript.onload = () => {
	new window.PagefindUI({
		element: '#search',
		showSubResults: false,
	});
};
document.body.appendChild(pagefindScript);

你也可以在頁面載入時直接用 <script> 載入;只有在訪客打開搜尋時才載入,則能避免每個頁面一開始都下載搜尋程式。重點是:不論採用哪種載入時機,索引尚未建立前,/pagefind/ 下的檔案都不存在,看到 404 不一定是 UI 寫錯。

第二步:告訴 Pagefind 要索引什麼

Pagefind 預設會處理頁面內容,但你可以用 data-pagefind-body 把範圍縮小,用 data-pagefind-ignore 排除固定介面:

<body>
	<header data-pagefind-ignore>
		網站導覽
	</header>

	<main data-pagefind-body>
		
	</main>

	<footer data-pagefind-ignore>
		頁尾資訊
	</footer>
</body>

這三個標記各自做一件事:

  1. lang="zh-Hant" 提供語言資訊,讓 Pagefind 進行對應的語言處理。
  2. data-pagefind-body 指定頁面真正要被搜尋的內容。
  3. data-pagefind-ignore 排除導覽列、頁尾或分享按鈕,避免固定文字污染每一筆結果。

有一個容易漏掉的規則:只要網站任何頁面使用了 data-pagefind-body,沒有這個標記的頁面就不會被索引。因此,如果首頁、分類頁也要出現在搜尋結果裡,它們同樣要標記搜尋主體。

data-pagefind-ignore 是「排除元素」;它不會讓整頁消失。把它放在 header 或 footer 上,文章正文仍然會被索引。

第三步:在 Eleventy build 後建立索引

先建立網站,再把輸出目錄交給 Pagefind。使用專案已安裝並鎖定版本的 CLI,通常比每次用 npx -y 下載最新版更容易重現:

pnpm exec eleventy
pnpm exec pagefind --site _site --glob '**/*.html'

成功後,輸出目錄會出現 _site/pagefind/,裡面包含索引資料,以及 UI 需要的 JavaScript 和 CSS。Pagefind 本身沒有搜尋後端;這些檔案部署到同一個靜態網站後,瀏覽器就能載入它們。

如果想快速測試,也可以讓 Pagefind 在建立索引後提供本機靜態伺服器:

pnpm exec pagefind --site _site --glob '**/*.html' --serve

這個 --serve 是方便開發時測試的本機伺服器,不是正式環境必須部署的服務。不要直接用 file:// 開啟 HTML,因為瀏覽器的模組載入和 fetch 請求可能受到本機檔案權限限制。

第四步:把流程接進 package.json

手動跑兩個指令可以驗證概念,但很容易忘記第二個。把 Pagefind 接在 Eleventy 後面,每次 build 就會自動更新索引:

{
	"scripts": {
		"build": "NODE_ENV=production ELEVENTY_PROFILE=true pnpm exec eleventy && pnpm exec pagefind --site _site --glob \"**/*.html\""
	}
}

實際專案的 build script 可能還有清理、Vite 或其他收尾步驟;請把 Pagefind 放在「所有 HTML 都完成之後」。如果部署平台只執行 Eleventy,卻沒有執行 Pagefind,正式站自然不會憑空長出搜尋索引。

也可以在 Eleventy 的 eleventy.after 事件裡呼叫 Pagefind,但這會讓 Eleventy 設定檔同時負責建站和啟動外部程序。對多數專案而言,package.json&& 串接比較容易看懂、除錯,也能確保前一步失敗時不會誤把舊索引當成新成果。

繁體中文搜尋要怎麼驗收?

Pagefind 支援中文的分詞處理,但中文不像英文那樣以空白分隔;搜尋結果仍然取決於索引內容和查詢方式。這不是「加上 lang 就保證什麼都找得到」,所以請用真正的文章做測試。

部署前至少走過這份清單:

  • [ ] Eleventy build 成功,沒有模板或資產錯誤。
  • [ ] _site/pagefind/ 存在,且正式網址能載入 /pagefind/ 下的檔案。
  • [ ] 搜尋一個確定存在的文章標題,可以回傳結果。
  • [ ] 搜尋兩三個繁體中文詞組,例如「網站安全」或「靜態網站」。
  • [ ] 搜尋結果沒有被導覽列、頁尾或分享按鈕的固定文字洗版。
  • [ ] 修改文章後重新 build,結果中的摘要已更新。

若網站同時有多種語言,請讓每個頁面的 <html lang> 正確反映內容,不要全部硬寫成同一種語言。Pagefind 會依語言分開索引;標記錯誤,等於把書放進錯的書架,搜尋當然會繞遠路。

常見問題排查

搜尋框出現,卻沒有結果

先看瀏覽器 Network 面板:/pagefind/pagefind-ui.js/pagefind/pagefind-ui.css 和索引檔是否回傳 404。再確認 Pagefind 指向的是包含 HTML 的輸出目錄,而不是專案原始碼目錄。

中文結果很少

檢查 <html lang> 是否存在且寫對,並用文章內確實出現的詞組測試。也檢查 data-pagefind-body 是否誤放在空容器,或文章所在頁面根本沒有這個標記。不要只因為終端機出現 stemming 相關提示就移除語言標記:中文搜尋仍可運作,只是 Pagefind 不會以英文詞幹還原的方式處理中文。

首頁或分類頁沒有出現在結果裡

這通常是 data-pagefind-body 的範圍規則造成的。只標記文章 layout 時,沒有標記的首頁和分類頁會被排除;在同樣希望被搜尋的頁面補上搜尋主體即可。

搜尋結果一直出現導覽列文字

將不需要搜尋的固定區塊加上 data-pagefind-ignore,刪除舊的 _site/pagefind/ 後重新 build。只改模板、不重建索引,搜尋結果不會自己更新——索引沒有讀心術,只有讀檔術。

本機測試看起來壞掉

確認不是用 file:// 開啟 HTML,也確認 Pagefind 產出的 /pagefind/ 路徑和網站部署的 base path 一致。使用 --serve 或其他靜態檔案伺服器重新測試,通常很快就能分辨是瀏覽器載入問題還是索引內容問題。

結語:先解決搜尋,再考慮升級

對大多數 Eleventy 部落格來說,第一版站內搜尋不需要資料庫。你只要:

  1. 在 layout 加入與 Pagefind 版本相符的 UI。
  2. 設定正確的 langdata-pagefind-bodydata-pagefind-ignore
  3. 在每次 Eleventy build 後執行 Pagefind。
  4. 用實際的繁體中文查詢驗收。

這樣就能讓讀者搜尋已部署的文章。未來若真的需要會員權限、個人化排序或即時資料,再評估後端或託管搜尋服務;先把今天的搜尋做好,咖啡才不會冷得太有深度。

參考資料