在 Astro v5 的架構下,為多語系儀表板加入搜索功能,最推薦的方案是 Pagefind。它是一個極其快速、靜態化的搜尋庫,非常適合 Astro 這種注重效能的框架。
以下是將 Pagefind 整合進你的專案的步驟:
1. 安裝與設定
Pagefind 是在構建(Build)完成後掃描靜態檔案的,所以我們需要安裝它:npx astro add astro-pagefind
- astro.config.mjs 自動配置
...
import pagefind from 'astro-pagefind'
// https://astro.build/config
export default defineConfig({
....
integrations: [pagefind()],
})
2. 建立 UI 搜尋組件
我們可以使用 Pagefind 內建的 UI 庫,或者自定義組件。為了符合儀表板的風格,我們在 src/components/Search.astro 建立一個優雅的搜尋框:
---
// Search.astro
---
<link href="/_pagefind/pagefind-ui.css" rel="stylesheet" />
<script src="/_pagefind/pagefind-ui.js" is:inline></script>
<div id="search" class="w-full max-w-2xl mx-auto"></div>
<script>
window.addEventListener('DOMContentLoaded', (event) => {
new PagefindUI({
element: "#search",
showSubResults: true,
// 這裡可以根據當前語系自動過濾
bundlePath: "/_pagefind/"
});
});
</script>
<style is:global>
/* 使用 Tailwind v4 語法調整 Pagefind UI 外觀 */
/* 引用你的 Tailwind 主入口(路徑依你的專案結構而定) */
@reference "../styles/global.css";
:root {
--pagefind-ui-primary: var(--color-brand);
--pagefind-ui-text: #374151;
--pagefind-ui-background: #ffffff;
--pagefind-ui-border: #e5e7eb;
}
.pagefind-ui__drawer {
@apply shadow-xl rounded-lg border border-gray-100 p-4;
}
</style>
💡 /_pagefind :詳見 額外筆記-⭐ /_pagefind...
💡 is:inline :詳見 額外筆記-⭐ is:inline
💡 is:global :詳見 額外筆記-⭐ is:global
💡 new PagefindUI :
詳見 額外筆記-⚠️ 如果 new PagefindUI IDE 出現錯誤
詳見 額外筆記-⭐ new PagefindUI() 介紹
3. 實作多語系搜索過濾
為了確保「中文搜尋」不會搜尋到「英文內容」,我們需要在內容層級標記語系。
在你的 Layout 或 Product 頁面模板中,對 main 標籤添加 data-pagefind-body,並標記語系屬性:
---
// src/pages/[lang]/products.astro
const { lang } = Astro.params;
---
<main data-pagefind-body data-pagefind-filter={`language:${lang}`}>
<h1 class="text-3xl font-bold">Products ({lang})</h1>
</main>
💡 data-pagefind... :詳見 額外筆記-⭐ data-pagefind...
4. 進階:自動化 Metadata (Content Layer)
在 Astro v5 中,你可以利用 schema 讓搜尋結果更精準。Pagefind 會自動抓取 <h1>, <p>,但你也可以手動指定:
<div class="product-card" data-pagefind-index-attrs="title">
<h2 data-pagefind-meta="title">{product.data.title}</h2>
<span data-pagefind-meta="price">{product.data.price}</span>
<img src="{product.data.image}" data-pagefind-meta="image[src]" />
</div>
5. 本地測試與預覽
由於 Pagefind 是在 Build 之後運行的,一般的 npm run dev 看不到效果。請執行:
npm run build
npx serve dist
這時你就可以在本地體驗極速的全文檢索了!
💡 npm run build :
詳見 額外筆記-⚠️ 如果 npm run build 出現錯誤 - 1
詳見 額外筆記-⚠️ 如果 npm run build 出現錯誤 - 2
詳見 額外筆記-⚠️ 如果 npm run build 出現錯誤 - 3
詳見 額外筆記-⚠️ 如果 npm run build 出現警告
為什麼這樣做對儀表板更好?
- 零伺服器成本:搜尋索引是靜態檔案,不需要額外的資料庫或 API 費用。
- 毫秒級響應:Pagefind 在瀏覽器端解析索引,速度遠超傳統 API。
- 多語系隔離:透過 data-pagefind-filter,用戶在中文介面只會搜到中文產品,體驗最流暢。
📚 額外筆記
⭐ /_pagefind...
<link href="/_pagefind/pagefind-ui.css" rel="stylesheet" />
<script src="/_pagefind/pagefind-ui.js" is:inline></script>
單純執行 npm install pagefind 只是把工具下載到你的 node_modules 裡面。
這兩個靜態檔案(.css 和 .js)是在索引(Indexing)階段由 Pagefind 工具產生的,通常發生在網站建構完成之後。
💡 具體的生成流程
Pagefind 的運作邏輯是「掃描已生成的 HTML 檔案」,所以它必須在 Astro 完成 build 之後才執行。
- 執行 Astro Build:
npm run build(產生靜態 HTML 到dist/資料夾)。 - 執行 Pagefind Indexing: 執行指令(如下)後,Pagefind 才會在
dist/裡面建立_pagefind/資料夾。npx pagefind --site dist
執行完上述第二步後,你才會在 dist/_pagefind/ 目錄下看到:
pagefind-ui.jspagefind-ui.css- 以及其他的索引資料庫檔案。
💡 為什麼你在開發模式 (npm run dev) 找不到它?
Pagefind 是一個靜態搜尋工具。在 dev 模式下,Astro 是動態渲染頁面的,沒有實際的 HTML 檔案讓 Pagefind 去掃描,因此:
在 src/ 資料夾下永遠不會看到 _pagefind。
只有在執行 build 並執行 pagefind 指令後的 dist/ 資料夾才會有。
💡 在 Astro 中自動化這個過程
為了方便,建議你修改 package.json 中的 scripts,讓每次 build 完後自動跑索引:
"scripts": {
"dev": "astro dev",
"start": "astro dev",
"build": "astro build && npx pagefind --site dist",
"preview": "astro preview"
}
💡 如何在頁面中引用?
由於這些檔案是在 build 之後才產生的,你在寫 Astro 組件時,通常會這樣引用:
<link href="/_pagefind/pagefind-ui.css" rel="stylesheet" />
<script src="/_pagefind/pagefind-ui.js"></script>
<div id="search"></div>
<script>
window.addEventListener('DOMContentLoaded', (event) => {
new PagefindUI({ element: '#search', showSubResults: true })
})
</script>
💡 總結
npm install: 只是下載開發工具。npx pagefind --site dist: 這步才會真的產出_pagefind資料夾。
⭐ is:inline
在 Astro (現代網頁框架),is:inline 是一個 模板指令 (Template Directive)。
主要用來告訴 Astro 編譯器:不要 處理該標籤 (如 <script> 或 <style>)。
💡 為什麼需要 is:inline ?
預設情況下,Astro 會對 <script> 和 <style> 標籤進行「自動優化」,包括:
- 搬移位置: 將腳本移至頁面底部或
<head>中。 - 打包與壓縮: 合併多個檔案、移除空格、縮短變數名稱。
- 模組化: 預設視為 JavaScript Module (type="module")。
加上 is:inline 後,這些自動處理都會停止。
💡 主要應用場景
1. 使用外部全域變數
如果你在 HTML 中直接引入了第三方套件(如 Google Analytics),且需要在後面的腳本中直接存取它,通常需要 is:inline。
<script is:inline>
// 這段代碼會原封不動地出現在 HTML 輸出的位置
// 不會被轉換成 type="module"
console.log('我是一個原始的腳本')
</script>
2. 避免樣式被處理
如果你希望某段 CSS 保持原樣(例如為了讓某些老舊瀏覽器讀取,或是不想讓 Astro 幫你加上作用域類別),可以使用在 <style> 上。
<style is:inline>
body {
background-color: ghostwhite;
}
</style>
3. 引用 public 資料夾的資源
如果你在 public/ 資料夾放了一個 my-script.js,你必須使用 is:inline 才能正確引用它,否則 Astro 會試圖去處理這個路徑。
💡 is:inline 的副作用 (注意!)
雖然它很方便,但使用時要承擔以下代價:
- 不支援 TypeScript: 在
is:inline的腳本中,你不能寫 TS,只能寫純 JS。 - 不支援匯入 (Import): 你不能在裡面使用
import語句引用 npm 套件。 - 效能開銷: 該腳本不會被壓縮,也不會與其他腳本合併,這可能會稍微增加網頁載入的大小。
⭐ is:global
is:global 主要用於 <style> 標籤。它的作用是 打破 Astro 的「樣式隔離 (Style Scoping)」機制。
💡 為什麼需要 is:global?
在 Astro 中,預設情況下,你在組件內寫的 CSS 是私有的。 如果你在 Button.astro 裡寫了 h1 { color: red; },這段樣式只會影響這個組件裡的 h1,不會影響到頁面上的其他 h1。
當你加上 is:global 後: 這段樣式就會變成「全域性」的,它會影響到整個網站中所有符合選擇器的元素。
---
// Layout.astro
---
<style is:global>
/* 加上 is:global 後,全站所有的 h1 都會變成紫色 */
h1 {
color: rebeccapurple;
}
/* 常用於設定全域字體或重置樣式 (Reset) */
body {
font-family: sans-serif;
}
</style>
💡 is:global vs is:inline
- is:global (針對範圍): 樣式依然會被 Astro 處理、壓縮、打包,只是它不再受限於當前組件,而是套用到全域。
- is:inline (針對處理方式): 告訴 Astro 不要處理 這個標籤,直接原封不動放到 HTML 裡。
💡 混合使用::global() 選擇器
如果你不想讓整個 <style> 塊都變成全域,只想讓 某個特定的 CSS 規則 變成全域,你可以使用 CSS 偽類語法:
<style>
/* 只有這個組件的 div 會受影響 */
div {
padding: 20px;
}
/* 即使在組件內,這也會影響全站的 .external-link */
:global(.external-link) {
color: blue;
text-decoration: underline;
}
</style>
⚠️ 如果 new PagefindUI IDE 出現錯誤
Cannot find name 'PagefindUI'.
這個錯誤是因為 TypeScript (或 JavaScript) 在編譯時期找不到 PagefindUI 這個變數的定義。
由於 pagefind-ui.js 是在 build 之後才產生的動態腳本,開發時你的程式碼並不知道這個全域變數(Global Variable)的存在。
以下是三種修復方法:
1. 最快解決法:使用 any 繞過檢查
如果你只是想趕快讓錯誤消失,可以在使用前直接告訴 TypeScript 這個變數是存在的:
<script is:inline>
window.addEventListener('DOMContentLoaded', (event) => {
// @ts-ignore
new PagefindUI({ element: '#search' })
})
</script>
注意:在 Astro 中使用 is:inline 可以確保腳本直接原封不動地放在 HTML 裡,不會被 Vite 處理。
2. 正確做法:在 <script> 中宣告型別
在你的 Astro 元件或 .ts 檔案中,手動宣告 PagefindUI 的存在,這樣既能解決錯誤,未來也比較好維護。
<script>
// 告訴 TypeScript 這個全域變數是由外部載入的
declare const PagefindUI: any;
window.addEventListener('DOMContentLoaded', () => {
if (typeof PagefindUI !== 'undefined') {
new PagefindUI({
element: "#search",
showImages: false
});
}
});
</script>
3. 檢查資源載入順序 (關鍵!)
如果你在執行時發現瀏覽器 Console 報錯 ReferenceError: PagefindUI is not defined,那通常是因為你的 JS 執行得比 pagefind-ui.js 載入還要快。
請確保你的引用順序如下:
<link href="/_pagefind/pagefind-ui.css" rel="stylesheet" />
<script src="/_pagefind/pagefind-ui.js" is:inline></script>
<div id="search"></div>
<script is:inline>
window.addEventListener('load', () => {
new PagefindUI({ element: '#search' })
})
</script>
💡 為什麼開發模式 (npm run dev) 還是會壞掉?
這點非常重要:Pagefind 在 dev 模式下通常無法運作。
pagefind-ui.js檔案在src/裡並不存在,它只存在於dist/。- 所以當你在
dev模式預覽時,瀏覽器會找不到/_pagefind/pagefind-ui.js(報 404 錯誤)。
💡 測試方法: 你必須先執行 npm run build(這會生成索引),然後執行 npx astro preview(預覽正式版),這時候搜尋功能才會真正動起來。
⭐ new PagefindUI() 介紹
new PagefindUI({
element: '#search',
showSubResults: true,
// 這裡可以根據當前語系自動過濾
bundlePath: '/_pagefind/',
})
這段程式碼是用來初始化 Pagefind 的搜尋介面(UI)。
當你在 HTML 中準備好一個 <div id="search"></div> 後,這段 JavaScript 會將 Pagefind 預設的搜尋框、按鈕、搜尋結果清單等元件「注入」到該標籤中,並設定它的行為方式。
💡 參數詳細解釋
1.element: '#search'
- 意思: 指定搜尋組件要掛載(Mount)在哪個 HTML 元素上。
- 效果: 它會尋找頁面中 id="search" 的標籤,並把搜尋 UI 塞進去。
2.showSubResults: true
- 意思: 是否顯示子結果(也就是頁面內的各個標題段落)。
- 效果:
。false:搜尋結果只會顯示「頁面標題」。
。true:搜尋結果會顯示「頁面標題」以及下方匹配到的「具體段落或標題(h2, h3)」,讓使用者直接看到關鍵字出現在頁面的哪個部分。
3.bundlePath: '/pagefind/'
- 意思: 告訴 PagefindUI 索引資料庫(Index library)存放在哪裡。
- 效果: 這非常重要。PagefindUI 需要去讀取那些在 build 階段產生的
.msgpack索引檔案。預設情況下它會找/_pagefind/,但在某些複雜的網址路徑下(例如 GitHub Pages 子路徑),手動指定這個路徑可以確保它能正確抓到資料。
💡 完整的運作流程圖
- 瀏覽器載入: 頁面讀取
pagefind-ui.js。 - 執行腳本: 執行你寫的這段
new PagefindUI(...)。 - 抓取資料: UI 根據
bundlePath去伺服器請求搜尋索引檔案。 - 渲染: 在
#search內部生成搜尋框。
⭐ data-pagefind...
<main data-pagefind-body data-pagefind-filter="{`language:${lang}`}">
<h1 class="text-3xl font-bold">Products ({lang})</h1>
</main>
1. data-pagefind-body
- 意思: 告訴 Pagefind「這才是重點內容」。
- 作用: 一旦 Pagefind 在你的專案中發現任何一個標籤帶有這個屬性,它就會只索引該標籤內部的文字。
- 為什麼要用它:
。過濾雜訊: 防止導覽列(Navbar)、側邊欄(Sidebar)或頁尾(Footer)的文字出現在搜尋結果中。
。提升品質: 搜尋結果會更專注於產品資訊。
2. data-pagefind-filter="language:${lang}"
- 意思: 為這段內容貼上一個「語言標籤」過濾器。
- 作用: 它將當前頁面的語言(如
en或zh-tw)存入索引資料庫中。 - 為什麼要用它:
。跨語言搜尋控制: 如果你的網站有中文版和英文版,你通常不希望使用者在中文介面搜到英文產品。
。實作功能: 在初始化PagefindUI時,你可以告訴它「只顯示language:zh-tw的結果」。
💡 實際運作邏輯示意圖
- Build 階段: Pagefind 看到
<main data-pagefind-body>,於是只抓取裡面的<h1>和產品列表。 - 標籤化: 它發現
data-pagefind-filter,在資料庫裡標記:這幾筆資料屬於language:en。 - 搜尋階段: 當使用者搜尋時,Pagefind 會根據這個標籤快速過濾出正確語言的結果。
💡 配合這個設定,你的 PagefindUI 應該怎麼改?
既然你已經在 HTML 加上了 filter,你在初始化搜尋框時,應該也要告訴它「預設過濾當前語言」:
new PagefindUI({
element: '#search',
// 加上這段,讓搜尋框自動只找符合當前語言的內容
filter: {
language: lang, // 这里的 lang 必須跟 HTML 屬性裡的 ${lang} 一致
},
})
💡 小提醒
- 屬性重複: 如果你在多個地方用了
data-pagefind-body,Pagefind 會把所有帶有這個屬性的區塊內容合併索引。 - HTML 拼字: 確保屬性名稱完全正確,Pagefind 依賴這些
data-開頭的自定義屬性來運作。
⚠️ 如果 npm run build 出現錯誤 - 1
npm run build
Error: Pagefind was not able to build an index.
Most likely, the directory passed to Pagefind was empty or did not contain any html files.
這個錯誤發生在 Pagefind 索引階段。意思是:Pagefind 去查看你指定的資料夾(通常是 dist),但發現裡面空空如也,或者是沒有任何 .html 檔案。
這通常由以下三個原因造成:
1. 指令順序錯誤或資料夾路徑不對
你可能在 package.json 裡的指令寫錯了。如果 Pagefind 在 Astro 還沒完成 build 之前就跑起來,或是找錯了資料夾,就會報這個錯。
請檢查你的 package.json:
// ❌ 錯誤範例:可能路徑寫成 ./public 或 ./src
"build": "astro build && npx pagefind --site public"
// ✅ 正確範例:Astro 預設輸出的資料夾是 dist
"build": "astro build && npx pagefind --site dist"
2. Astro 沒有產生任何 HTML (純 SSR 模式)
如果你在 astro.config.mjs 中將 output 設定為 'server',且沒有設定任何預先渲染(Prerender)的頁面,那麼 dist 資料夾裡就只有伺服器程式碼,而沒有 .html 檔案。
Pagefind 只能索引靜態的 HTML 檔案。
- 解決方法: 確保你的頁面有設定
export const prerender = true;或者將專案設定為output: 'static'(或'hybrid')。
3. Astro Build 失敗了
如果 astro build 本身因為程式錯誤而中斷,後面的 npx pagefind 就會因為找不到生成的 dist 而報錯。
💡 診斷與修正步驟
請按順序執行以下指令,看看哪一步出問題:
第一步:確認 dist 是否有東西 手動執行一次 build:
npm run build執行完後,去資料夾看:你的專案目錄下有沒有出現 dist/ 資料夾?裡面有沒有 index.html?
第二步:手動執行 Pagefind 指令 如果 dist 裡有 HTML 檔案,請手動輸入:
npx pagefind --site dist
觀察這次是否有成功產生 _pagefind 資料夾。
💡 常見的 package.json 最佳實作
為了確保穩定,建議將指令拆開寫,這樣報錯時比較好抓是哪一段出錯:
{
"scripts": {
"dev": "astro dev",
"build": "astro build && npm run postbuild",
"postbuild": "pagefind --site dist",
"preview": "astro preview"
}
}
💡 額外檢查點:Vercel 部署
如果你是在 Vercel 上看到這個錯,請檢查你的 "Build Command"。 Vercel 有時候會快取之前的狀態。
確保你的 Build Command 設定為 npm run build(且該指令包含 pagefind 那一段)。
💡 如何查看 Vercel 的 Build Command
在 Vercel 中查看或修改 Build Command(構建指令) 非常簡單,你可以透過網頁後台(Dashboard)進行查看。
以下是具體的操作步驟:
1. 進入專案設定
。登入 Vercel Dashboard。
。點擊進入你想查看的 Project(專案)。
。在上方導覽列中,點擊最右邊的 Settings(設定)。
2. 找到 Build & Development Settings
。在左側選單中,點擊第一項 General(通常進去就是這一頁)。
。向下捲動頁面,找到 Build & Development Settings 區塊。
。在這裡你就能看到 Build Command。
💡 常見情況說明
- 預設指令(Preset): 如果你的專案使用了 Next.js, Nuxt, 或 Vite 等框架,Vercel 會自動偵測並顯示預設指令(例如
next build)。這時該欄位會顯示為灰色或標註為Framework Default。 - 自定義指令(Override): 如果你需要修改指令(例如改成 npm run build:prod),請點擊旁邊的 OVERRIDE 切換開關,然後在輸入框中填入你的指令。
- 查看部署紀錄中的指令: 如果你想確認「某一次部署」實際執行了什麼指令,可以點擊 Deployments 標籤 -> 點擊該次部署 -> 點擊 Building 區塊展開日誌(Build Logs),日誌的最上方通常會印出實際執行的指令。
💡 vercel Build Command 設定
npm run build && npx pagefind --site .vercel/output/static --output-subdir pagefind
⚠️ 如果 npm run build 出現錯誤 - 2
npm run build
> blog@0.0.1 build
> astro build && pagefind --site dist
17:01:04 [content] Syncing content
17:01:04 [content] Synced content
17:01:04 [types] Generated 790ms
17:01:04 [build] output: "static"
17:01:04 [build] mode: "static"
17:01:04 [build] directory: D:\GitHubDesktop\project\__learn-project\115.01.17\blog\dist\
17:01:04 [build] adapter: @astrojs/vercel
17:01:04 [build] Collecting build info...
17:01:04 [build] ✓ Completed in 843ms.
file: D:/GitHubDesktop/project/__learn-project/115.01.17/blog/src/components/Search.astro?astro&ts
Stack trace:
at onInvalidCandidate (file:///D:/GitHubDesktop/project/__learn-project/115.01.17/blog/node_mhunk-CT46QCH7.mjs:21:1314)
....
問題出在 Tailwind CSS 編譯失敗,導致整個 astro build 中斷了。
因為 astro build 沒跑完,dist 資料夾就不會產生(或內容不完整),接著後面的 pagefind 指令就會報錯說找不到 HTML 檔案。
💡 為什麼會報錯?
錯誤訊息中提到 onInvalidCandidate 以及出現在 Search.astro 的樣式部分,這通常是因為:
- CSS 語法錯誤: 在
Search.astro的<style>區塊中寫了 Tailwind 無法辨識的語法。 - Tailwind 指令放錯位置: 在組件樣式中使用了
@tailwind或不當的@apply。
💡 檢查與修正建議
1. 檢查 Search.astro 的 <style>
請打開 src/components/Search.astro,查看底部的樣式區塊。
- 如果你在組件裡寫了:
/* ❌ 錯誤:組件內部不應該重複出現這些 */
修正: 請把這些刪除。這些指令應該只出現在全域的 CSS 檔案中(例如
<style>
@tailwind base;
@tailwind components;
@tailwind utilities;
</style>src/styles/global.css)。 - 檢查 @apply 語法: 如果你用了
@apply,請確保後面接的是正確的 Tailwind class,且結尾要有分號。
<style>
#search {
@apply mt-4; /* 確保 mt-4 是有效的 class */
}
</style>
2. 暫時移除 Search.astro 的樣式來測試
為了確認是不是樣式引起的問題,你可以嘗試先將 Search.astro 裡面的 <style>...</style> 整個註解掉,然後再跑一次 npm run build。
3. 清除快取
有時候 Tailwind 的編譯快取會卡住,請嘗試刪除 node_modules 和 .astro 資料夾後重新安裝:
rm -rf node_modules .astro dist
npm install
npm run build
💡 正常的 Search.astro 結構參考
一個標準的 Pagefind 搜尋組件應該長這樣:
---
// Props 或邏輯
---
<div id="search" class="my-8"></div>
<script is:inline src="/_pagefind/pagefind-ui.js"></script>
<link href="/_pagefind/pagefind-ui.css" rel="stylesheet">
<script is:inline>
window.addEventListener('DOMContentLoaded', () => {
new PagefindUI({
element: '#search',
bundlePath: '/_pagefind/'
});
});
</script>
<style>
/* 這裡盡量保持簡單,或直接使用 Tailwind class 寫在上面的 div 上 */
#search {
--pagefind-ui-primary: #034ad8;
}
</style>
⚠️ 如果 npm run build 出現錯誤 - 3
---
// Search.astro
---
...
<style is:global>
/* 使用 Tailwind v4 語法調整 Pagefind UI 外觀 */
:root {
--pagefind-ui-primary: var(--color-brand);
--pagefind-ui-text: #374151;
--pagefind-ui-background: #ffffff;
--pagefind-ui-border: #e5e7eb;
}
.pagefind-ui__drawer {
@apply shadow-xl rounded-lg border border-gray-100 p-4;
}
</style>
npm run build 出現錯誤 - 2 錯誤是由於 Tailwind CSS v4 的變更引起的。
在 Tailwind v4 中,如果你在 Astro 組件的 <style> 標籤內使用 @apply,但該組件沒有明確引入 Tailwind 的主設定檔,編譯器就會報錯(就是你看到的 onInvalidCandidate,因為它不知道 shadow-xl 這些變數定義在哪裡)。
💡 修正方法
將 <style is:global> 內容簡化,並將 @apply 移出。在 v4 中,最穩定的做法是直接使用 CSS 變數 或在全域 CSS 中處理樣式。
請嘗試將你的 Search.astro 修改為:
---
// Search.astro
---
<link href="/_pagefind/pagefind-ui.css" rel="stylesheet" />
<script src="/_pagefind/pagefind-ui.js" is:inline></script>
<div id="search" class="w-full max-w-2xl mx-auto"></div>
<script>
declare const PagefindUI: any
window.addEventListener('DOMContentLoaded', (event) => {
new PagefindUI({
element: '#search',
showSubResults: true,
bundlePath: '/_pagefind/',
})
})
</script>
<style is:global>
/* 修正:移除 @apply,改用純 CSS 控制,或將樣式移至 global.css */
:root {
--pagefind-ui-primary: #3b82f6; /* 暫時先用具體顏色,確認 build 能過 */
--pagefind-ui-text: #374151;
--pagefind-ui-background: #ffffff;
--pagefind-ui-border: #e5e7eb;
}
/* 如果一定要在這裡寫,請使用標準 CSS 屬性 */
.pagefind-ui__drawer {
box-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.1);
border-radius: 0.5rem;
border: 1px solid #f3f4f6;
padding: 1rem;
}
</style>
💡 為什麼這樣改?
- Tailwind v4 範圍限制: v4 的
@apply要求必須在一個能讀取到@theme的 context 下執行。在單獨的 Astro 組件<style>裡,如果沒有@import "tailwindcss";,它就會失效。 - 避免 onInvalidCandidate: 這個報錯本質上是 Tailwind 說:「你叫我 apply 一個叫
shadow-xl的東西,但我根本不知道那是啥。」
💡 建議的長期做法
如果真的很想用 Tailwind 的 @apply 來修飾 Pagefind:
- 把那段
.pagefind-ui__drawer { @apply ... }剪下。 - 貼到你的
src/styles/global.css(或是你專案引入 Tailwind 的那個主要 CSS 檔)裡面。 - 這樣 Tailwind 就能在全域掃描時正確解析這些 class。
⚠️ 如果 npm run build 出現警告
npm run build
> blog@0.0.1 build
> astro build && pagefind --site dist
....
Finished in 0.144 seconds
Warning: Running in pre-1.0 compatibility mode.
Pagefind 1.0 changes the default output directory from /_pagefind/ to /pagefind/
but references to the /_pagefind/ URL were found on your site, and the output directory is unconfigured.
To preserve your setup, the search files have been written twice, to both /_pagefind/ and /pagefind/
To remove this warning, either update your script and style references to the new `/pagefind/` URL
or run Pagefind with `--output-subdir _pagefind` to ensure pre-1.0 behaviour
這是一個常見的 Pagefind 版本升級警告。Pagefind 在 1.0 版本之後,將預設的資料夾名稱從 _pagefind 改成了 pagefind(去掉了底線)。
因為你的程式碼(Search.astro)中還在使用 /_pagefind/,Pagefind 為了不讓你的網站壞掉,自動幫你產生了兩份索引。
雖然現在能跑,但建議修正它以符合現代標準並消除警告。
💡 修正方案:升級到 1.0 標準路徑
請按照以下三個步驟修改你的程式碼:
1. 修改 Search.astro 中的資源引用
將所有 _pagefind 改為 pagefind:
<link href="/pagefind/pagefind-ui.css" rel="stylesheet" />
<script src="/pagefind/pagefind-ui.js" is:inline></script>
<div id="search" class="w-full max-w-2xl mx-auto"></div>
<script>
declare const PagefindUI: any
window.addEventListener('DOMContentLoaded', (event) => {
new PagefindUI({
element: '#search',
showSubResults: true,
// 修正前:/_pagefind/
bundlePath: '/pagefind/',
})
})
</script>
2. 修改 package.json 中的指令 (選做)
如果你希望更明確一點,可以在指令中指定輸出路徑,這能確保未來版本變動時依然穩定:
"scripts": {
"build": "astro build && pagefind --site dist --output-subdir pagefind"
}
3. 測試效果
修改完後,再次執行:
npm run build你會發現警告消失了,且 dist/ 資料夾內現在只會有一個乾淨的 pagefind/ 資料夾。
💡 為什麼 Pagefind 要改掉底線?
在許多靜態託管平台(如早期的 GitHub Pages)中,以底線 _ 開頭的資料夾有時會被視為特殊資料夾(例如 Jekyll 的原始碼)而被忽略。改為 pagefind/ 可以提高在各個平台的相容性。
💡 總結
這不是報錯(Error),只是警告(Warning)。你只需要:
- 全域搜尋並取代專案中所有的
_pagefind為pagefind。 - 重新 Build。









