Astro 專題 - 多語系商品探索儀表板 (Multi-lang Product Explorer)-2

更新 發佈閱讀 45 分鐘

在 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 出現警告


為什麼這樣做對儀表板更好?

  1. 零伺服器成本:搜尋索引是靜態檔案,不需要額外的資料庫或 API 費用。
  2. 毫秒級響應:Pagefind 在瀏覽器端解析索引,速度遠超傳統 API。
  3. 多語系隔離:透過 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 之後才執行。

  1. 執行 Astro Buildnpm run build (產生靜態 HTML 到 dist/ 資料夾)。
  2. 執行 Pagefind Indexing: 執行指令(如下)後,Pagefind 才會在 dist/ 裡面建立 _pagefind/ 資料夾。
    npx pagefind --site dist

執行完上述第二步後,你才會在 dist/_pagefind/ 目錄下看到:

  • pagefind-ui.js
  • pagefind-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 模式下通常無法運作

  1. pagefind-ui.js 檔案在 src/ 裡並不存在,它只存在於 dist/
  2. 所以當你在 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 子路徑),手動指定這個路徑可以確保它能正確抓到資料。

💡 完整的運作流程圖

  1. 瀏覽器載入: 頁面讀取 pagefind-ui.js
  2. 執行腳本: 執行你寫的這段 new PagefindUI(...)
  3. 抓取資料: UI 根據 bundlePath 去伺服器請求搜尋索引檔案。
  4. 渲染: 在 #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 的結果」。

💡 實際運作邏輯示意圖

  1. Build 階段: Pagefind 看到 <main data-pagefind-body>,於是只抓取裡面的 <h1> 和產品列表。
  2. 標籤化: 它發現 data-pagefind-filter,在資料庫裡標記:這幾筆資料屬於 language:en
  3. 搜尋階段: 當使用者搜尋時,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 的樣式部分,這通常是因為:

  1. CSS 語法錯誤: 在 Search.astro 的 <style> 區塊中寫了 Tailwind 無法辨識的語法。
  2. Tailwind 指令放錯位置: 在組件樣式中使用了 @tailwind 或不當的 @apply

💡 檢查與修正建議

1. 檢查 Search.astro 的 <style>

請打開 src/components/Search.astro,查看底部的樣式區塊。

  • 如果你在組件裡寫了:
    /* ❌ 錯誤:組件內部不應該重複出現這些 */
    <style>
    @tailwind base;
    @tailwind components;
    @tailwind utilities;
    </style>
    修正: 請把這些刪除。這些指令應該只出現在全域的 CSS 檔案中(例如 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>

💡 為什麼這樣改?

  1. Tailwind v4 範圍限制: v4 的 @apply 要求必須在一個能讀取到 @theme 的 context 下執行。在單獨的 Astro 組件 <style> 裡,如果沒有 @import "tailwindcss";,它就會失效。
  2. 避免 onInvalidCandidate: 這個報錯本質上是 Tailwind 說:「你叫我 apply 一個叫 shadow-xl 的東西,但我根本不知道那是啥。」

💡 建議的長期做法

如果真的很想用 Tailwind 的 @apply 來修飾 Pagefind:

  1. 把那段 .pagefind-ui__drawer { @apply ... } 剪下。
  2. 貼到你的 src/styles/global.css(或是你專案引入 Tailwind 的那個主要 CSS 檔)裡面。
  3. 這樣 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)。你只需要:

  1. 全域搜尋並取代專案中所有的 _pagefind 為 pagefind
  2. 重新 Build
留言
avatar-img
李昀瑾的沙龍
0會員
32內容數
李昀瑾的沙龍的其他內容
2026/02/05
環境架構設計 astro v5 tailwind v4 vercel 佈署 i18n 多語系架構
2026/02/05
環境架構設計 astro v5 tailwind v4 vercel 佈署 i18n 多語系架構
2026/01/29
第三題:跨框架同步(React 與 Vue 的對話) 這是 Nano Stores 最強大的地方。假設你有一個專案,因為歷史原因或團隊偏好,同時使用了 React 和 Vue。 練習目標 建立一個 React 的輸入框(Input)。 建立一個 Vue 的預覽文字(Preview)。 當
2026/01/29
第三題:跨框架同步(React 與 Vue 的對話) 這是 Nano Stores 最強大的地方。假設你有一個專案,因為歷史原因或團隊偏好,同時使用了 React 和 Vue。 練習目標 建立一個 React 的輸入框(Input)。 建立一個 Vue 的預覽文字(Preview)。 當
2026/01/27
第二題:深色模式(Dark Mode) 深色模式(Dark Mode) 的核心在於如何讓狀態「持久化」(即使重新整理網頁,設定也不會消失),以及如何與系統或手動設定同步。 在 Nano Stores 中,我們可以使用原生提供的 persistent 擴充功能,這能讓我們省去手寫 localSto
2026/01/27
第二題:深色模式(Dark Mode) 深色模式(Dark Mode) 的核心在於如何讓狀態「持久化」(即使重新整理網頁,設定也不會消失),以及如何與系統或手動設定同步。 在 Nano Stores 中,我們可以使用原生提供的 persistent 擴充功能,這能讓我們省去手寫 localSto
看更多
你可能也想看
Thumbnail
債券投資,不只是高資產族群的遊戲 在傳統的投資觀念中,海外債券(Overseas Bonds)常被貼上「高資產族群專屬」的標籤。過去動輒 1 萬甚至 10 萬美元的最低申購門檻,讓許多想尋求穩定配息的小資族望而卻步。 然而,在股市波動劇烈的環境下,尋求穩定的美元現金流與被動收入成為許多投資人
Thumbnail
債券投資,不只是高資產族群的遊戲 在傳統的投資觀念中,海外債券(Overseas Bonds)常被貼上「高資產族群專屬」的標籤。過去動輒 1 萬甚至 10 萬美元的最低申購門檻,讓許多想尋求穩定配息的小資族望而卻步。 然而,在股市波動劇烈的環境下,尋求穩定的美元現金流與被動收入成為許多投資人
Thumbnail
透過川普的近期債券交易揭露,探討債券作為資產配置中「穩定磐石」的重要性。文章分析降息對債券的潛在影響,以及股神巴菲特的操作策略。並介紹玉山證券「小額債」平臺,如何讓小資族也能低門檻參與海外債券市場,實現「低門檻、低波動、固定收益」的務實投資方式。
Thumbnail
透過川普的近期債券交易揭露,探討債券作為資產配置中「穩定磐石」的重要性。文章分析降息對債券的潛在影響,以及股神巴菲特的操作策略。並介紹玉山證券「小額債」平臺,如何讓小資族也能低門檻參與海外債券市場,實現「低門檻、低波動、固定收益」的務實投資方式。
Thumbnail
解析「債券」如何成為資產配置中的穩定錨,提供低風險高回報的投資選項。 藉由玉山證券的低門檻債券服務,投資者可輕鬆入手,平衡風險並穩定財務。
Thumbnail
解析「債券」如何成為資產配置中的穩定錨,提供低風險高回報的投資選項。 藉由玉山證券的低門檻債券服務,投資者可輕鬆入手,平衡風險並穩定財務。
Thumbnail
相較於波動較大的股票,債券能提供固定現金流,而玉山證券推出的小額債,更以1000 美元的低門檻,讓學生與新手也能參與全球優質企業債投資。玉山E-Trader平台即時報價、條件式篩選與清楚的交易流程等特色,大幅降低投資難度,對於希望分散風險、建立穩定現金流的人來說,玉山小額債是一個值得嘗試的理財起點。
Thumbnail
相較於波動較大的股票,債券能提供固定現金流,而玉山證券推出的小額債,更以1000 美元的低門檻,讓學生與新手也能參與全球優質企業債投資。玉山E-Trader平台即時報價、條件式篩選與清楚的交易流程等特色,大幅降低投資難度,對於希望分散風險、建立穩定現金流的人來說,玉山小額債是一個值得嘗試的理財起點。
Thumbnail
這份是我在使用Lovable做網站時,邊做邊紀錄的完整攻略,分享給需要的人。 給開發者/接案者的前言: Lovable 是一個強大的 AI 全端開發工具,要用它來賺錢,您必須清楚它的邊界在哪裡。這份指南將協助您完全掌控這個工具,從而自信地向客戶報價。 第一章:深度認識 Lovable 1.1
Thumbnail
這份是我在使用Lovable做網站時,邊做邊紀錄的完整攻略,分享給需要的人。 給開發者/接案者的前言: Lovable 是一個強大的 AI 全端開發工具,要用它來賺錢,您必須清楚它的邊界在哪裡。這份指南將協助您完全掌控這個工具,從而自信地向客戶報價。 第一章:深度認識 Lovable 1.1
Thumbnail
這是一場從「網路連結 → 線下見面」的活動,我一開始其實有些猶豫,畢竟地點對我來說不近,加上平常在社群裡其實不太主動互動。 但因為主辦人西打誠意滿滿地邀請,甚至還提出補貼車資,最後我決定自費參加。現在回頭看,真的很值得! 場地很有感,氛圍超溫暖 一踏進場地就被暖黃的燈光包圍,小閣樓超舒適,還
Thumbnail
這是一場從「網路連結 → 線下見面」的活動,我一開始其實有些猶豫,畢竟地點對我來說不近,加上平常在社群裡其實不太主動互動。 但因為主辦人西打誠意滿滿地邀請,甚至還提出補貼車資,最後我決定自費參加。現在回頭看,真的很值得! 場地很有感,氛圍超溫暖 一踏進場地就被暖黃的燈光包圍,小閣樓超舒適,還
Thumbnail
從實際應用中學習 Python 程式設計,提升技能並建立作品集。文章提供八個循序漸進的 Python 專案範例,涵蓋檔案操作、網路爬蟲、Web 應用、自動化腳本、數據分析、遊戲開發、API 互動及應用程式部署,並附上實戰建議及學習資源。
Thumbnail
從實際應用中學習 Python 程式設計,提升技能並建立作品集。文章提供八個循序漸進的 Python 專案範例,涵蓋檔案操作、網路爬蟲、Web 應用、自動化腳本、數據分析、遊戲開發、API 互動及應用程式部署,並附上實戰建議及學習資源。
Thumbnail
網站開發專案成功的關鍵在於與客戶的有效溝通。本文分享一個成功案例,說明如何透過明確掌握專案需求、主動提供技術方案、定期回報進度、完善技術協助及建立良好客戶關係,順利完成一個中文影片學習分享網站的建置,並獲得客戶高度滿意與後續合作機會。
Thumbnail
網站開發專案成功的關鍵在於與客戶的有效溝通。本文分享一個成功案例,說明如何透過明確掌握專案需求、主動提供技術方案、定期回報進度、完善技術協助及建立良好客戶關係,順利完成一個中文影片學習分享網站的建置,並獲得客戶高度滿意與後續合作機會。
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News