<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet href="pretty-atom-feed.xsl" type="text/xsl"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="zh-Hant">
  <title>光輝咖碼</title>
  <subtitle>沒有一杯咖啡解決不了的問題；如果有，就交給 AI 吧。這裡分享網站架設、前端開發與各種技術筆記，然後再來一杯。</subtitle>
  <link href="https://kamadiam.com/feed/feed.xml" rel="self" />
  <link href="https://kamadiam.com/" />
  <updated>2026-08-07T03:32:15Z</updated>
  <id>https://kamadiam.com/</id>
  <author>
    <name>huihere</name>
  </author>
  <entry>
    <title>為什麼要發明 Pi？從作者角度理解 Pi vs Claude Code 該怎麼選</title>
    <link href="https://kamadiam.com/pi-vs-claude-code-why-author-built-pi/" />
    <updated>2026-08-07T03:32:15Z</updated>
    <id>https://kamadiam.com/pi-vs-claude-code-why-author-built-pi/</id>
    <content type="html">&lt;p&gt;終端裡的 AI 程式助手越來越多：Claude Code、Cursor，還有各種「多代理＋Plan Mode＋MCP」大禮包。功能清單看起來很強，但你有沒有遇過這種感覺——&lt;strong&gt;工具一更新，行為就變了&lt;/strong&gt;；你以為它在「幫你想」，其實有一段上下文是介面看不到的；出錯時又不知道該查哪一層？&lt;p&gt;（有點像咖啡店改菜單：你點的還叫同名，內容卻換了豆。AI 工具也會這樣，而且通常不會在杯子上貼告示。）&lt;p&gt;Pi Coding Agent 的作者 Mario Zechner，正是從這類痛點出發，選擇&lt;strong&gt;重做一個極小核心&lt;/strong&gt;，而不是繼續往既有工具堆功能。這篇不做沒有實測根據的「誰比較強」評比，也不喊誰全面勝利；目標很單純：用&lt;strong&gt;作者為什麼要發明 Pi&lt;/strong&gt; 當鏡頭，幫你看懂兩種哲學，再決定自己要的是 &lt;strong&gt;Pi 的可控性&lt;/strong&gt;，還是 &lt;strong&gt;Claude Code 的現成工作流&lt;/strong&gt;。&lt;p&gt;讀完你會帶走：作者動機怎麼對到設計、四個原則怎麼想、一張決策表幫你 30 秒對號入座。&lt;div class=&quot;table-of-contents&quot;&gt;&lt;h2 id=&quot;目錄&quot;&gt;目錄&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/pi-vs-claude-code-why-author-built-pi/#%E5%85%88%E8%AC%9B%E6%B8%85%E6%A5%9A%E9%80%99%E4%B8%8D%E6%98%AF%E3%80%8CClaude-Code-%E4%B8%8D%E5%A5%BD%E3%80%8D&quot;&gt;先講清楚：這不是「Claude Code 不好」&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/pi-vs-claude-code-why-author-built-pi/#%E4%BD%9C%E8%80%85%E7%82%BA%E4%BB%80%E9%BA%BC%E8%A6%81%E9%87%8D%E6%96%B0%E5%81%9A%E4%B8%80%E5%80%8B-Coding-Agent?&quot;&gt;作者為什麼要重新做一個 Coding Agent？&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/pi-vs-claude-code-why-author-built-pi/#Pi-%E7%9A%84%E5%9B%9B%E5%80%8B%E8%A8%AD%E8%A8%88%E5%8E%9F%E5%89%87%E5%B0%8D%E7%85%A7-Claude-Code-%E6%80%8E%E9%BA%BC%E6%83%B3&quot;&gt;Pi 的四個設計原則（對照 Claude Code 怎麼想）&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/pi-vs-claude-code-why-author-built-pi/#%E6%B1%BA%E7%AD%96%E8%A1%A8%E6%88%91%E8%A9%B2%E9%81%B8-Pi-%E9%82%84%E6%98%AF-Claude-Code?&quot;&gt;決策表：我該選 Pi 還是 Claude Code？&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/pi-vs-claude-code-why-author-built-pi/#%E6%8E%A8%E8%96%A6%E5%B7%A5%E5%85%B7/%E8%B3%87%E6%BA%90&quot;&gt;推薦工具／資源&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/pi-vs-claude-code-why-author-built-pi/#%E7%B5%90%E8%AA%9E%E9%81%B8%E5%93%B2%E5%AD%B8,%E5%86%8D%E9%81%B8%E6%8C%89%E9%88%95&quot;&gt;結語：選哲學，再選按鈕&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/pi-vs-claude-code-why-author-built-pi/#%E5%8F%83%E8%80%83%E8%B3%87%E6%96%99&quot;&gt;參考資料&lt;/a&gt;&lt;/ul&gt;&lt;/div&gt;&lt;h2 id=&quot;先講清楚這不是「Claude-Code-不好」&quot;&gt;先講清楚：這不是「Claude Code 不好」&lt;/h2&gt;&lt;p&gt;作者的公開文章把 Claude Code 當成「既有 Coding Agent 逐漸變複雜」的對照例子——但那是&lt;strong&gt;他對自己工作流的取捨&lt;/strong&gt;，不是對所有人的判決。以下的功能描述以相關官方文件與文章目前列出的內容為準；這類工具更新很快，實際行為仍要以你使用的版本和設定為準。&lt;ul&gt;&lt;li&gt;Claude Code 適合：想要內建 Plan、子代理、MCP、斜線指令，少自己組裝。&lt;li&gt;Pi 適合：想看見上下文與中間產物、把工作流放在檔案／CLI／tmux，願意自己決定要加什麼。&lt;/ul&gt;&lt;p&gt;讀完這篇，你應該能回答一句話：「我比較怕&lt;strong&gt;黑箱變動&lt;/strong&gt;，還是比較怕&lt;strong&gt;自己組工具的成本&lt;/strong&gt;？」&lt;p&gt;答得出來，比背一百個功能還有用——功能清單只告訴你「有什麼」；真正幫你判斷怎麼修、怎麼選的，是你對工具的使用原則。&lt;h2 id=&quot;作者為什麼要重新做一個-Coding-Agent?&quot;&gt;作者為什麼要重新做一個 Coding Agent？&lt;/h2&gt;&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;既有工具遇到的問題（作者說法）&lt;th&gt;Pi 的設計回應&lt;th&gt;你實際可以怎麼用&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Claude Code 從早期較簡單，逐漸變成作者用不到很多功能的複雜工具；發布常改 system prompt／工具定義，既有工作流與模型行為跟著變&lt;td&gt;核心「不需要就不建」；短 system prompt；差異放到外部擴充&lt;td&gt;固定自己的提示、&lt;code&gt;AGENTS.md&lt;/code&gt;、驗證流程；升級後重新檢查，不要假設行為永遠不變&lt;tr&gt;&lt;td&gt;Context engineering 很重要，但其他 harness 可能在 UI 看不到的地方注入上下文&lt;td&gt;Session、工具呼叫、來源與產物保持可見；規劃寫成檔案，不藏在黑箱代理裡&lt;td&gt;要求 Agent 把研究／計畫／檢查結果寫成可讀檔，人工看完再進下一階段&lt;tr&gt;&lt;td&gt;內建子代理把上下文傳遞與錯誤藏在另一層，出錯難追查&lt;td&gt;不提供專用子代理工具；需要時用 bash 再開一個 Pi，或用 Extension／Package 自己組&lt;td&gt;任務邊界與輸出格式清楚才拆 session，中間產物一律存檔&lt;/table&gt;&lt;p&gt;一句話總結作者動機：&lt;blockquote&gt;&lt;p&gt;當 Coding Agent 變得複雜、常改又不透明時，最可靠的回應不是再加一層功能——而是&lt;strong&gt;縮小核心、提高可觀測性，讓使用者自己決定要加什麼&lt;/strong&gt;。&lt;/blockquote&gt;&lt;p&gt;官方定位也呼應這點：「讓 Pi 適應你的工作流，而不是反過來。」——翻成口語：工具來遷就你的終端習慣，不是你遷就它的儀式流程。咖啡機有預設模式很方便；但如果你想自己調研磨度，誰也不能禁止你拿出手沖壺。&lt;h2 id=&quot;Pi-的四個設計原則對照-Claude-Code-怎麼想&quot;&gt;Pi 的四個設計原則（對照 Claude Code 怎麼想）&lt;/h2&gt;&lt;h3 id=&quot;1.-極簡核心,不是功能競賽&quot;&gt;1. 極簡核心，不是功能競賽&lt;/h3&gt;&lt;p&gt;作者文章描述：Pi 的 system prompt 與預設工具定義合計可低於約 1,000 tokens；預設核心大致是讀、寫、編輯與 bash。極簡&lt;strong&gt;不是&lt;/strong&gt;叫大家永遠只用四個工具，而是把 MCP、子代理、Plan Mode 等能力移到 Extensions、Skills、Prompt Templates、Packages——&lt;strong&gt;按痛點再加&lt;/strong&gt;。這裡的「四個」是核心設計的概括，不代表每個版本或擴充後都只剩四項能力。&lt;p&gt;Claude Code 走另一條路：代理人循環（收集上下文 → 執行動作 → 驗證結果）＋較豐富的內建工具與斜線指令（如 &lt;code&gt;/compact&lt;/code&gt;、&lt;code&gt;/diff&lt;/code&gt;、&lt;code&gt;/model&lt;/code&gt;），並以 &lt;code&gt;CLAUDE.md&lt;/code&gt;、Skills、MCP 擴充。對「我想立刻開工」的人，這通常比較省事。&lt;p&gt;白話對照：Claude Code 像開箱即用的咖啡機；Pi 像手沖壺——多一步，但你看得見水溫、粉量和時間。&lt;h3 id=&quot;2.-可觀測性優先&quot;&gt;2. 可觀測性優先&lt;/h3&gt;&lt;p&gt;Pi 偏好把計畫寫成 Markdown：人可以編、跨 session 重用，也能進版本控制。「Agent 做了什麼」變成可檢查的產物，而不是只信最後一句摘要。換句話說，規格、計畫和進度先落檔，人工過目再寫碼；這種做法也比較容易留下日後能追溯的線索。&lt;p&gt;Claude Code 也有規範檔與驗證迴圈，但作者批評的重點是：若規劃與子工作藏在內部狀態，除錯成本會變高。你若本來就習慣「先看 diff／計畫再准執行」，兩套都能做；差別在於&lt;strong&gt;狀態預設放在哪&lt;/strong&gt;——檔案系統，還是工具內建流程。&lt;h3 id=&quot;3.-用組合取代內建一體化&quot;&gt;3. 用組合取代內建一體化&lt;/h3&gt;&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Pi 刻意不內建&lt;th&gt;官方建議替代&lt;th&gt;什麼時候值得自己組&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;MCP&lt;td&gt;小型 CLI＋README，或自行加 Extension&lt;td&gt;工具少、要控 token、輸出想直接寫檔串接&lt;tr&gt;&lt;td&gt;子代理&lt;td&gt;tmux 再開 Pi、Extension、Package&lt;td&gt;要看見子過程，或真的要平行任務&lt;tr&gt;&lt;td&gt;Plan Mode&lt;td&gt;計畫寫入檔案，或自行擴充&lt;td&gt;計畫要跨 session、共同編輯、版控&lt;tr&gt;&lt;td&gt;背景 bash&lt;td&gt;tmux&lt;td&gt;長跑測試、開發伺服器、互動除錯&lt;tr&gt;&lt;td&gt;權限彈窗&lt;td&gt;容器／隔離帳號／自訂確認&lt;td&gt;不可信程式碼或機密資料&lt;/table&gt;&lt;p&gt;作者對大型 MCP 的具體顧慮是：工具說明每次塞進上下文，token 貴，輸出還常得繞回 Agent 才能保存。他提的替代是「小型 CLI + README + bash」——需要時才讀，輸出可直寫檔。&lt;p&gt;Claude Code 官方文件列有 MCP 整合，對「接資料庫／瀏覽器／外部服務、不想自己包 CLI」的人是加分。你要選的是：&lt;strong&gt;內建方便&lt;/strong&gt; vs &lt;strong&gt;按需載入、看得見成本&lt;/strong&gt;。&lt;h3 id=&quot;4.-安全責任放回執行環境&quot;&gt;4. 安全責任放回執行環境&lt;/h3&gt;&lt;p&gt;Pi 預設不走「權限彈窗＝安全」這條路；作者認為 Agent 一旦能讀寫檔、跑指令，單靠彈窗很難形成完整邊界。這&lt;strong&gt;不是&lt;/strong&gt;宣稱 Pi 比較安全，而是要求你用容器、隔離帳號、網路限制等&lt;strong&gt;真邊界&lt;/strong&gt;。含機密的主機開 YOLO，風險在你，不能照抄作者個人設定。&lt;p&gt;Claude Code 提供權限確認與相關隔離選項；對不想自己從零設計沙箱的人，通常比較「有提示、有流程」。兩邊都不是魔法盾——差在&lt;strong&gt;預設假設誰負責安全&lt;/strong&gt;，而且最後仍要由你確認實際的執行環境。&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;比喻&lt;th&gt;對應的做法&lt;th&gt;實際效果&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;門上貼「請勿打擾」&lt;td&gt;Agent 跳出權限彈窗，你點「允許」&lt;td&gt;只是&lt;strong&gt;提醒／確認&lt;/strong&gt;，對方真要闖還是進得去&lt;tr&gt;&lt;td&gt;門鎖上了&lt;td&gt;容器、隔離帳號、網路限制&lt;td&gt;&lt;strong&gt;物理／技術邊界&lt;/strong&gt;，進不去才是真隔離&lt;/table&gt;&lt;p&gt;彈窗是禮貌的提示；隔離才是真的鎖門。兩者看起來都在「管安全」，保護力差很多。&lt;h2 id=&quot;決策表我該選-Pi-還是-Claude-Code?&quot;&gt;決策表：我該選 Pi 還是 Claude Code？&lt;/h2&gt;&lt;p&gt;用這張表做決定（可以兩邊都裝，但同一目錄不要同時寫檔；要並行請用不同 worktree 或其他隔離方式）：&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;你的情境&lt;th&gt;較適合&lt;th&gt;先確認這件事&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;想快速使用整合好的流程，不想自己組工具&lt;td&gt;&lt;strong&gt;Claude Code&lt;/strong&gt;&lt;td&gt;能否接受 system prompt、工具與版本變動&lt;tr&gt;&lt;td&gt;想逐步檢查上下文與中間產物、計畫要進 Git&lt;td&gt;&lt;strong&gt;Pi&lt;/strong&gt;（或同等可觀測工作流）&lt;td&gt;是否願意維護 markdown、CLI、tmux&lt;tr&gt;&lt;td&gt;已在使用 Anthropic 生態、愛斜線指令與內建任務工作流&lt;td&gt;&lt;strong&gt;Claude Code&lt;/strong&gt;&lt;td&gt;MCP／Skills 的 token 與權限範圍&lt;tr&gt;&lt;td&gt;本地模型、控 token、設定要可移植（&lt;code&gt;AGENTS.md&lt;/code&gt;、&lt;code&gt;.pi/&lt;/code&gt;）&lt;td&gt;&lt;strong&gt;Pi&lt;/strong&gt;&lt;td&gt;短 prompt 在你的模型／專案規模是否夠用（要實測）&lt;tr&gt;&lt;td&gt;要查瀏覽器、資料庫、外部服務，且想少寫 glue code&lt;td&gt;Claude Code＋MCP，或 Pi＋按需 CLI／Extension&lt;td&gt;工具描述成本、輸出能否存檔&lt;tr&gt;&lt;td&gt;不可信程式碼或機密檔案&lt;td&gt;先隔離，再選任一 Agent&lt;td&gt;隔離是否真限制檔案與網路，而不只是警告&lt;/table&gt;&lt;h3 id=&quot;30-秒口訣&quot;&gt;30 秒口訣&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;怕&lt;strong&gt;黑箱與升級打破工作流&lt;/strong&gt; → 先試 &lt;strong&gt;Pi&lt;/strong&gt;。&lt;li&gt;怕&lt;strong&gt;自己組裝與維護成本&lt;/strong&gt; → 先用 &lt;strong&gt;Claude Code&lt;/strong&gt;。&lt;li&gt;都要一點 → 主流程用 Claude Code／IDE；終端批次、低成本實驗、可版控的 Agent 設定用 Pi。&lt;/ul&gt;&lt;h2 id=&quot;推薦工具/資源&quot;&gt;推薦工具／資源&lt;/h2&gt;&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;工具&lt;th&gt;適合誰&lt;th&gt;備註&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;&lt;a href=&quot;https://pi.dev/&quot;&gt;Pi Coding Agent&lt;/a&gt;&lt;/strong&gt;&lt;td&gt;想要極簡核心、可觀測產物、按需擴充的終端使用者&lt;td&gt;先用裸 Pi 跑通小任務，再加 Package／Extension&lt;tr&gt;&lt;td&gt;&lt;strong&gt;&lt;a href=&quot;https://docs.anthropic.com/en/docs/claude-code/overview&quot;&gt;Claude Code&lt;/a&gt;&lt;/strong&gt;&lt;td&gt;想要整合代理人工作流、MCP 與專案規範的 Anthropic 生態使用者&lt;td&gt;用 &lt;code&gt;CLAUDE.md&lt;/code&gt; 固定專案規範；升級後抽查行為&lt;tr&gt;&lt;td&gt;延伸閱讀&lt;td&gt;想系統學 Pi 工作流&lt;td&gt;站內：&lt;a href=&quot;https://kamadiam.com/blog/pi-coding-agent/&quot;&gt;Pi Coding Agent 完整指南&lt;/a&gt;&lt;/table&gt;&lt;p&gt;客觀取捨：Pi 把「省心的一體化」換成「可控、但需要自己組合」；Claude Code 則把更多流程先整合好，換取較低的起步成本。Pi 和 Claude Code 各有取捨，誰贏取決於你的風險與願不願意自己維護，不是全世界都該選同一個。&lt;h2 id=&quot;結語選哲學,再選按鈕&quot;&gt;結語：選哲學，再選按鈕&lt;/h2&gt;&lt;p&gt;作者發明 Pi，不是為了「功能贏過 Claude Code」，而是為了在&lt;strong&gt;複雜、常改、不透明&lt;/strong&gt;的 Coding Agent 浪潮裡，留下一個&lt;strong&gt;看得見、擴得動、核心夠小&lt;/strong&gt;的 harness。這個選擇不一定讓每個人都更省事，卻讓取捨本身變得比較清楚。&lt;p&gt;帶走三點：&lt;ol&gt;&lt;li&gt;&lt;strong&gt;動機&lt;/strong&gt;：縮小核心＋可觀測性，對抗黑箱與工作流被升級拆掉。&lt;li&gt;&lt;strong&gt;取捨&lt;/strong&gt;：Claude Code 用整合換省心；Pi 用組合換控制權。&lt;li&gt;&lt;strong&gt;下一步&lt;/strong&gt;：用上面的決策表對號入座，不要只比功能勾選清單。&lt;/ol&gt;&lt;p&gt;先挑一個小任務（例如修一個測試失敗）——若你要「開箱即用」，用 Claude Code 跑完並檢查差異；若你要「計畫與驗證都落檔」，用 Pi 把計畫寫成 Markdown 再改碼。跑完一次，比再讀十篇比較文更準。畢竟工具比較文可以收藏，失敗的測試不會因為被收藏就自己修好。&lt;p&gt;審完再來一杯。下一杯建議：直接動手，別再收藏文章了。&lt;h2 id=&quot;參考資料&quot;&gt;參考資料&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://mariozechner.at/posts/2025-11-30-pi-coding-agent/&quot;&gt;What I learned building an opinionated and minimal coding agent&lt;/a&gt; — Mario Zechner 說明為何做極簡 Pi、對照既有 Coding Agent 的痛點&lt;li&gt;&lt;a href=&quot;https://mariozechner.at/posts/2025-11-02-what-if-you-dont-need-mcp/&quot;&gt;What if you don&#39;t need MCP at all?&lt;/a&gt; — 作者對大型 MCP 成本與「小型 CLI + README」替代方案的論點&lt;li&gt;&lt;a href=&quot;https://github.com/badlogic/pi-mono/blob/main/packages/coding-agent/README.md&quot;&gt;Pi coding agent README&lt;/a&gt; — 官方定位、刻意不內建項目與擴充方向&lt;li&gt;&lt;a href=&quot;https://pi.dev/&quot;&gt;Pi Coding Agent 官網&lt;/a&gt; — 產品入口與文件入口&lt;li&gt;&lt;a href=&quot;https://docs.anthropic.com/en/docs/claude-code/overview&quot;&gt;Anthropic Claude Code 官方指南&lt;/a&gt; — Claude Code 的工作方式、MCP、&lt;code&gt;CLAUDE.md&lt;/code&gt; 與擴充能力&lt;li&gt;&lt;a href=&quot;https://newsletter.pragmaticengineer.com/p/building-pi-and-what-makes-self-modifying&quot;&gt;Building Pi, and what makes self-modifying software so fascinating&lt;/a&gt; — Pragmatic Engineer 訪談／整理，補作者背景脈絡&lt;/ul&gt;</content>
  </entry>
  <entry>
    <title>我如何用 PageSpeed Insights 優化 11ty 網站：為什麼比 WordPress 更容易直接修</title>
    <link href="https://kamadiam.com/11ty-pagespeed-insights-optimization-workflow/" />
    <updated>2026-08-06T01:25:00Z</updated>
    <id>https://kamadiam.com/11ty-pagespeed-insights-optimization-workflow/</id>
    <content type="html">&lt;p&gt;沒有一杯咖啡解決不了的效能問題；如果有，就先把 PageSpeed Insights 的診斷報告打開，看看是哪個資源在拖後腿。&lt;p&gt;我做 11ty 網站效能優化時，通常會先把網址丟進 &lt;a href=&quot;https://pagespeed.web.dev/&quot;&gt;PageSpeed Insights&lt;/a&gt;，看它指出的具體問題，再回到 11ty 的模板、CSS、圖片設定或 JavaScript 原始碼逐項修正。很多「改 HTML 屬性、補尺寸、延後腳本」這類步驟，其實可以交給 AI 動手；你負責寫清楚提示詞，再審核 diff 與實際結果。&lt;p&gt;這個流程在 11ty 上很直接：首圖太大，就處理首圖；圖片沒有明確寬高，就改 HTML 或圖片管道；第三方 JavaScript 阻塞主執行緒，就延後或移除那支腳本。改完重新 build、部署，再用相同條件測一次。&lt;p&gt;WordPress 當然也能優化，但問題有時藏在佈景主題、頁面編輯器、快取外掛和圖片外掛互相疊加的結果。你可能知道 PageSpeed Insights 要求什麼，卻不知道哪一層才是可以安全修改的地方。&lt;p&gt;這篇記錄我如何把 PageSpeed Insights 的建議轉成 11ty 的實際修改。重點不是宣稱哪個框架永遠比較快，而是：在 11ty 裡，診斷結果通常可以直接對應到你能控制的模板、資產或腳本。&lt;div class=&quot;table-of-contents&quot;&gt;&lt;h2 id=&quot;目錄&quot;&gt;目錄&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/11ty-pagespeed-insights-optimization-workflow/#%E5%85%88%E8%AA%AA%E7%B5%90%E8%AB%96PageSpeed-Insights-%E6%98%AF%E8%A8%BA%E6%96%B7%E5%B7%A5%E5%85%B7,%E4%B8%8D%E6%98%AF%E6%A1%86%E6%9E%B6%E8%A9%95%E5%88%86%E8%A1%A8&quot;&gt;先說結論：PageSpeed Insights 是診斷工具，不是框架評分表&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/11ty-pagespeed-insights-optimization-workflow/#%E6%88%91%E7%9A%84-11ty-%E5%84%AA%E5%8C%96%E5%BE%AA%E7%92%B0%E6%B8%AC%E9%87%8F%E5%AE%9A%E4%BD%8D%E7%9B%B4%E6%8E%A5%E4%BF%AE%E6%94%B9%E5%86%8D%E9%A9%97%E8%AD%89&quot;&gt;我的 11ty 優化循環：測量、定位、直接修改、再驗證&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/11ty-pagespeed-insights-optimization-workflow/#PageSpeed-Insights-%E6%8C%87%E5%87%BA-LCP-%E6%85%A2%E5%85%88%E6%89%BE%E7%9C%9F%E6%AD%A3%E7%9A%84%E6%9C%80%E5%A4%A7%E5%85%83%E7%B4%A0&quot;&gt;PageSpeed Insights 指出 LCP 慢：先找真正的最大元素&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/11ty-pagespeed-insights-optimization-workflow/#PageSpeed-Insights-%E6%8C%87%E5%87%BA-CLS%E5%85%88%E8%A3%9C%E5%B0%BA%E5%AF%B8,%E4%B8%8D%E8%A6%81%E5%8F%AA%E8%AA%BF%E5%88%86%E6%95%B8&quot;&gt;PageSpeed Insights 指出 CLS：先補尺寸，不要只調分數&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/11ty-pagespeed-insights-optimization-workflow/#PageSpeed-Insights-%E6%8C%87%E5%87%BA-JavaScript-%E5%A4%AA%E5%A4%9A%E5%85%88%E5%95%8F%E9%80%99%E6%94%AF%E8%85%B3%E6%9C%AC%E7%9C%9F%E7%9A%84%E9%9C%80%E8%A6%81%E5%97%8E?&quot;&gt;PageSpeed Insights 指出 JavaScript 太多：先問這支腳本真的需要嗎？&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/11ty-pagespeed-insights-optimization-workflow/#%E7%82%BA%E4%BB%80%E9%BA%BC%E5%90%8C%E4%B8%80%E5%80%8B%E5%BB%BA%E8%AD%B0,%E5%9C%A8-WordPress-%E4%B8%8A%E5%B8%B8%E5%B8%B8%E6%AF%94%E8%BC%83%E9%9B%A3%E8%90%BD%E5%9C%B0?&quot;&gt;為什麼同一個建議，在 WordPress 上常常比較難落地？&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/11ty-pagespeed-insights-optimization-workflow/#%E6%88%91%E6%9C%83%E7%94%A8-AI-%E5%8A%A0%E9%80%9F%E4%BF%AE%E6%AD%A3,%E4%BD%86%E4%B8%8D%E6%9C%83%E8%AE%93-AI-%E7%9B%B4%E6%8E%A5%E4%BA%82%E6%94%B9%E6%95%B4%E7%AB%99&quot;&gt;我會用 AI 加速修正，但不會讓 AI 直接亂改整站&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/11ty-pagespeed-insights-optimization-workflow/#%E4%BB%80%E9%BA%BC%E6%83%85%E6%B3%81%E4%B8%8D%E8%A9%B2%E5%8F%AA%E7%82%BA%E4%BA%86-PageSpeed-Insights-%E6%8F%9B%E6%88%90-11ty?&quot;&gt;什麼情況不該只為了 PageSpeed Insights 換成 11ty？&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/11ty-pagespeed-insights-optimization-workflow/#%E7%B5%90%E8%AA%9E%E6%8A%8A%E8%A8%BA%E6%96%B7%E5%BB%BA%E8%AD%B0%E8%AE%8A%E6%88%90%E5%8F%AF%E5%9F%B7%E8%A1%8C%E7%9A%84%E5%B7%A5%E4%BD%9C%E6%B8%85%E5%96%AE&quot;&gt;結語：把診斷建議變成可執行的工作清單&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/11ty-pagespeed-insights-optimization-workflow/#%E5%8F%83%E8%80%83%E8%B3%87%E6%96%99&quot;&gt;參考資料&lt;/a&gt;&lt;/ul&gt;&lt;/div&gt;&lt;h2 id=&quot;先說結論PageSpeed-Insights-是診斷工具,不是框架評分表&quot;&gt;先說結論：PageSpeed Insights 是診斷工具，不是框架評分表&lt;/h2&gt;&lt;p&gt;PageSpeed Insights 不能單獨拿來比較 11ty 和 WordPress 誰比較快。它會提供實驗室資料與欄位資料，兩者用途不同：&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Lighthouse 實驗室資料&lt;/strong&gt;：適合重現問題、觀察剛做的改動。&lt;li&gt;&lt;strong&gt;Chrome 使用者體驗報告（CrUX）欄位資料&lt;/strong&gt;：反映真實使用者的體驗，通常看第 75 百分位。&lt;/ul&gt;&lt;p&gt;新網站流量不足時，可能暫時沒有 CrUX 資料。這時仍然可以先用 PageSpeed Insights 和 Lighthouse 做預防性優化，但不要把一次測試的分數當成網站的永久成績單。&lt;p&gt;我會先看這三個 Core Web Vitals 指標：&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;指標&lt;th&gt;它在看什麼&lt;th&gt;我通常先檢查什麼&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;LCP&lt;td&gt;首屏最大文字或圖片多久出現&lt;td&gt;hero 圖片、TTFB、阻塞 CSS、圖片是否被 lazy-load&lt;tr&gt;&lt;td&gt;CLS&lt;td&gt;頁面載入時是否跳動&lt;td&gt;圖片寬高、字體、嵌入內容和廣告預留空間&lt;tr&gt;&lt;td&gt;INP&lt;td&gt;使用者點擊或輸入後多久得到回應&lt;td&gt;第三方腳本、GTM、搜尋或互動元件&lt;/table&gt;&lt;p&gt;如果 PageSpeed Insights 顯示 95 分，但 Search Console 的 Core Web Vitals 仍有大量需要改善的 URL，我不會只看著實驗室分數自我安慰，而會回頭確認真實裝置和真實網路條件。&lt;h2 id=&quot;我的-11ty-優化循環測量定位直接修改再驗證&quot;&gt;我的 11ty 優化循環：測量、定位、直接修改、再驗證&lt;/h2&gt;&lt;p&gt;我通常照下面的順序做：&lt;ol&gt;&lt;li&gt;固定同一個 URL 或首頁當測試條件，先跑 PageSpeed Insights，記下 LCP、CLS、INP、FCP 和診斷項目。&lt;li&gt;找出問題對應的實際 HTML 元素或網路請求，例如「LCP 元素」是哪張圖，或是哪支腳本造成長任務。&lt;li&gt;把診斷原文貼給 AI，一次只請它改一類問題（例如只修 LCP 首圖），並寫清楚「不要動哪些檔案」。&lt;li&gt;在本機 build，檢查輸出的網頁是否真的產生預期的 HTML 和資產。&lt;li&gt;部署後用相同 URL 重跑 PageSpeed Insights，記錄改了什麼、分數和診斷如何變化。&lt;li&gt;如果是 GTM 或分析工具，除了看效能，也確認事件和轉換追蹤沒有被犧牲。&lt;/ol&gt;&lt;p&gt;一次只改一類問題，比較容易判斷因果。這一輪只處理圖片，下一輪再處理第三方腳本；如果同時換模板、換 CDN、刪掉五個外掛，最後就算分數變好，也很難知道真正有效的是什麼。&lt;p&gt;如果要在本機重現，先讓網站用和正式環境接近的方式完成 build，再用 Lighthouse 測試可存取的 URL。不要直接拿開發伺服器的熱更新狀態和正式網站比較，否則很容易把「開發工具的噪音」誤認成效能問題。&lt;pre class=&quot;language-shell&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;pnpm&lt;/span&gt; run build
&lt;span class=&quot;token function&quot;&gt;pnpm&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;exec&lt;/span&gt; lighthouse https://example.com/某篇文章/ &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;
	&lt;span class=&quot;token parameter variable&quot;&gt;--output&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;html &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;
	--output-path&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;./lighthouse-report.html &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;
	--chrome-flags&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;--headless&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;PageSpeed Insights 適合看整體診斷與欄位資料；Lighthouse 則適合在你能控制的測試環境裡重現問題。兩者不是互相競爭的咖啡師，而是不同杯型的量杯。&lt;h3 id=&quot;通用提示詞骨架可複製後改診斷內容&quot;&gt;通用提示詞骨架（可複製後改診斷內容）&lt;/h3&gt;&lt;p&gt;無論修 LCP、CLS 還是腳本，我都會先用類似這樣的骨架：&lt;pre class=&quot;language-text&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;這是一個 11ty（Eleventy）網站專案。請依 PageSpeed Insights 診斷修正，一次只處理一類問題。

【目標頁面】
https://example.com/某篇文章/

【這次只要處理】
（例如：LCP 首圖太慢 / CLS 圖片缺尺寸 / 第三方 JS 長任務）

【PageSpeed Insights 原文診斷】
（把 Opportunities、Diagnostics、LCP element、相關 network request 整段貼上）

【專案限制】
- 只改與這次問題直接相關的模板、CSS、圖片設定或 JS
- 不要改 URL、permalink、追蹤 ID、部署腳本、與效能無關的內容文案
- 不要一次「整站優化到 100 分」
- 改完請說明改了哪些檔案、為什麼這樣改，並列出我應手動驗證的項目

請先定位對應檔案與輸出 HTML，再提出最小修改。改完請跑本機 build（若專案有現成指令），確認沒有 build 錯誤。&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;提示詞寫得越具體，AI 越不會順便重構半個站。咖啡加太多糖也不好喝；優化提示詞也一樣，甜度要剛好。&lt;h2 id=&quot;PageSpeed-Insights-指出-LCP-慢先找真正的最大元素&quot;&gt;PageSpeed Insights 指出 LCP 慢：先找真正的最大元素&lt;/h2&gt;&lt;p&gt;LCP 常常是文章首圖、hero 圖或 H1，但不能只憑猜測決定。先在報告裡確認 LCP element，再針對那個元素處理。最常見的錯誤，是把首屏圖片也加上 &lt;code&gt;loading=&amp;quot;lazy&amp;quot;&lt;/code&gt;。懶載入適合視窗外的圖片，但會讓瀏覽器更晚發現 LCP 資源。&lt;p&gt;我會先做幾件事：&lt;ul&gt;&lt;li&gt;把圖片轉成 WebP 或 AVIF。&lt;li&gt;讓圖片尺寸接近實際顯示寬度，不要把幾千像素的原圖直接送給手機。&lt;li&gt;為圖片寫上正確的 &lt;code&gt;width&lt;/code&gt; 和 &lt;code&gt;height&lt;/code&gt;。&lt;li&gt;首屏圖片視情況使用 &lt;code&gt;loading=&amp;quot;eager&amp;quot;&lt;/code&gt; 和 &lt;code&gt;fetchpriority=&amp;quot;high&amp;quot;&lt;/code&gt;。&lt;li&gt;只有最重要的一、兩個資源使用高優先權，不要所有圖片都搶同一個優先級。&lt;/ul&gt;&lt;p&gt;例如首圖可以像這樣：&lt;pre class=&quot;language-html&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/img/hero.webp&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;1200&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;630&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;文章主視覺&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;loading&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;eager&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;fetchpriority&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;high&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;如果網站圖片很多，我會再導入 &lt;a href=&quot;https://www.11ty.dev/docs/plugins/image/&quot;&gt;&lt;code&gt;@11ty/eleventy-img&lt;/code&gt;&lt;/a&gt;。它可以在 build 時產生多種格式和寬度，並依產生的圖片資料協助輸出尺寸與 &lt;code&gt;srcset&lt;/code&gt;。這比每次寫文章都手動處理圖片可靠。&lt;p&gt;不過圖片管道的輸出仍要檢查：即使圖片工具產生了 &lt;code&gt;srcset&lt;/code&gt;，也不代表瀏覽器一定會用到最適合的候選檔案；而且 hero 圖的載入策略仍要個別確認。&lt;h3 id=&quot;給-AI-的提示詞範例LCP&quot;&gt;給 AI 的提示詞範例（LCP）&lt;/h3&gt;&lt;pre class=&quot;language-text&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;請只處理 LCP，不要動第三方腳本，也不要順便改 CLS 版面預留。

PageSpeed Insights 指出 LCP 元素是：（貼上 LCP element 截圖或文字，例如 article hero img）
目前問題可能包含：圖片過大、格式不是 WebP/AVIF、誤用 loading=&quot;lazy&quot;、缺少 fetchpriority、缺少明確 width/height。

請在這個 11ty 專案中：
1. 找出產生該 LCP 圖片的 Markdown / layout / Image Transform 設定
2. 讓首屏圖片改為 loading=&quot;eager&quot;、fetchpriority=&quot;high&quot;，並確保有 width/height
3. 確認視窗外圖片仍可 lazy-load，不要全站改成 eager
4. 若已使用 @11ty/eleventy-img，優先在既有管道調整，不要另開一套圖片流程
5. 改完說明輸出 HTML 應長什麼樣子，並列出我要驗證的項目&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;AI-做完後,你要審核什麼LCP&quot;&gt;AI 做完後，你要審核什麼（LCP）&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;diff 範圍&lt;/strong&gt;：是不是只動了首圖／hero 相關模板或圖片設定，沒有順便改追蹤碼或全站 CSS。&lt;li&gt;&lt;strong&gt;輸出 HTML&lt;/strong&gt;：本機 build 後打開該頁原始碼，確認 LCP 圖真的是 &lt;code&gt;eager&lt;/code&gt; + &lt;code&gt;fetchpriority=&amp;quot;high&amp;quot;&lt;/code&gt;，且有 &lt;code&gt;width&lt;/code&gt;/&lt;code&gt;height&lt;/code&gt;。&lt;li&gt;&lt;strong&gt;不要誤傷下文圖片&lt;/strong&gt;：文章中段、側欄、相關文章縮圖仍應 lazy；若全部變 eager，會反而搶頻寬。&lt;li&gt;&lt;strong&gt;實際載入&lt;/strong&gt;：用瀏覽器 DevTools Network，確認首屏圖優先被請求，且尺寸合理（不是幾千像素原圖硬塞手機）。&lt;li&gt;&lt;strong&gt;重測&lt;/strong&gt;：同一 URL 再跑 PageSpeed Insights，看 LCP 與「Improve image delivery」類診斷是否改善。&lt;/ul&gt;&lt;h2 id=&quot;PageSpeed-Insights-指出-CLS先補尺寸,不要只調分數&quot;&gt;PageSpeed Insights 指出 CLS：先補尺寸，不要只調分數&lt;/h2&gt;&lt;p&gt;CLS 的問題常常不是 11ty 本身造成，而是輸出的 HTML 沒有告訴瀏覽器圖片會佔多大空間。圖片載入後才撐開版面，讀者原本要點的按鈕就可能被推走。&lt;p&gt;我的檢查順序是：&lt;ol&gt;&lt;li&gt;找出全站 &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt;，確認是否都有真實的 &lt;code&gt;width&lt;/code&gt; 和 &lt;code&gt;height&lt;/code&gt;，或有可預期的 &lt;code&gt;aspect-ratio&lt;/code&gt;。&lt;li&gt;檢查 YouTube、社群貼文和其他 iframe 是否有固定比例或預留高度。&lt;li&gt;檢查 web font 載入後是否造成文字換行和版面位移。&lt;li&gt;檢查 cookie、聯盟揭露或廣告區塊是否在內容上方突然插入。&lt;/ol&gt;&lt;p&gt;這也是 11ty 好處很明顯的地方：我可以在共用 layout、shortcode 或 Image Transform 統一修正，不必逐頁打開 WordPress 編輯器，也不必猜某個外掛會不會在前端再次改寫 HTML。&lt;h3 id=&quot;給-AI-的提示詞範例CLS&quot;&gt;給 AI 的提示詞範例（CLS）&lt;/h3&gt;&lt;pre class=&quot;language-text&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;請只處理 CLS（版面位移），不要改 LCP 優先權策略，也不要動分析／GTM 載入時機。

PageSpeed Insights 診斷：（貼上 layout shift 相關項目、受影響元素）

請在這個 11ty 專案中：
1. 找出缺少 width/height 的 &amp;lt;img&amp;gt;，以及可能造成位移的 iframe、字體、上方橫幅
2. 在 layout、shortcode 或 Image Transform 統一補上真實尺寸或 aspect-ratio
3. 若有 YouTube／社群嵌入，請用固定比例容器預留空間
4. 不要用「把內容延後載入」這種方式假裝解決 CLS
5. 改完列出改動檔案，並告訴我應在哪些頁面用肉眼看載入過程有沒有跳動&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;AI-做完後,你要審核什麼CLS&quot;&gt;AI 做完後，你要審核什麼（CLS）&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;尺寸是真的，不是亂填&lt;/strong&gt;：&lt;code&gt;width&lt;/code&gt;/&lt;code&gt;height&lt;/code&gt; 要對應實際比例；亂填會讓圖片被壓扁或裁切。&lt;li&gt;&lt;strong&gt;全站一致性&lt;/strong&gt;：共用 layout／Transform 改過後，抽查首頁、文章頁、分類頁各一頁，確認不是只修了示範頁。&lt;li&gt;&lt;strong&gt;字體與橫幅&lt;/strong&gt;：若 AI 只補了圖片尺寸，但診斷點的是 web font 或頂部通知條，要退回請它對準真正元素。&lt;li&gt;&lt;strong&gt;肉眼驗證&lt;/strong&gt;：硬重新整理時盯著標題與按鈕位置，載入過程不應上下跳；必要時開 DevTools 的 Layout Shift regions。&lt;li&gt;&lt;strong&gt;重測&lt;/strong&gt;：同一條件重跑 PageSpeed Insights，確認 CLS 下降，且沒有為了 CLS 把首圖又改回有害的 lazy。&lt;/ul&gt;&lt;h2 id=&quot;PageSpeed-Insights-指出-JavaScript-太多先問這支腳本真的需要嗎?&quot;&gt;PageSpeed Insights 指出 JavaScript 太多：先問這支腳本真的需要嗎？&lt;/h2&gt;&lt;p&gt;純文章型 11ty 網站可以預設不載入 JavaScript，但只要加上 GA4、GTM、聊天工具、社群分享 SDK 或搜尋功能，INP 和主執行緒就可能開始變差。這裡的重點不是看到一支腳本就立刻刪掉，而是確認它在什麼時機、做了多少工作，以及功能是否真的需要它。&lt;p&gt;我會先做腳本盤點：&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;腳本類型&lt;th&gt;我的處理方式&lt;th&gt;需要注意的取捨&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;不再使用的追蹤或聊天工具&lt;td&gt;直接移除&lt;td&gt;先確認沒有業務依賴&lt;tr&gt;&lt;td&gt;非關鍵分析&lt;td&gt;延後到 &lt;code&gt;window&lt;/code&gt; loaded 或 idle&lt;td&gt;早期 pageview 可能遺失&lt;tr&gt;&lt;td&gt;使用者點擊後才需要的功能&lt;td&gt;點擊時才載入&lt;td&gt;第一次互動可能需要等待&lt;tr&gt;&lt;td&gt;需要精準記錄的轉換事件&lt;td&gt;保留即時路徑或精簡標籤&lt;td&gt;不能只為了效能犧牲轉換資料&lt;tr&gt;&lt;td&gt;社群分享&lt;td&gt;優先用普通連結取代 SDK&lt;td&gt;互動效果較少，但前端更輕&lt;/table&gt;&lt;p&gt;GTM 不能只看 &lt;code&gt;gtm.js&lt;/code&gt; 檔案大小。真正的成本可能來自容器裡的 Facebook Pixel、Hotjar、GA4 或其他標籤執行後造成的長任務。&lt;p&gt;如果採用 &lt;code&gt;requestIdleCallback&lt;/code&gt; 或互動後載入，也要測試事件是否仍能在導向前送出，不能只看分數變好了就算完成。瀏覽器分數變漂亮了，分析資料卻少一半，這種優化就像把咖啡杯擦得發亮，結果裡面根本沒有咖啡。&lt;p&gt;下面是概念範例，不是可以直接套到所有分析工具的通用初始化碼。實際使用前，仍要依工具文件確認初始化順序、事件佇列和瀏覽器支援情況：&lt;pre class=&quot;language-js&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;loadAnalytics&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token comment&quot;&gt;// 在這裡載入或初始化非關鍵分析工具。&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;requestIdleCallback&#39;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; window&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;requestIdleCallback&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;loadAnalytics&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;timeout&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2000&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setTimeout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;loadAnalytics&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;requestIdleCallback&lt;/code&gt; 是「有空再做」的安排，不是「一定在某個時間完成」的保證；&lt;code&gt;timeout&lt;/code&gt; 也不是分析事件送達的保證。若事件很重要，應該設計可接受的備援，並用瀏覽器 Network 或工具的除錯模式確認它真的送出。&lt;h3 id=&quot;給-AI-的提示詞範例第三方-JavaScript&quot;&gt;給 AI 的提示詞範例（第三方 JavaScript）&lt;/h3&gt;&lt;pre class=&quot;language-text&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;請只處理第三方／非關鍵 JavaScript 對 INP 與主執行緒的影響，不要改圖片與版面尺寸。

PageSpeed Insights 診斷：（貼上 Unused JavaScript、Reduce JavaScript execution time、長任務、相關 URL）

腳本盤點現況（請先對照專案後再改）：
- 必須保留且需準確的：（例如 GA4 轉換、主要 pageview）
- 可延後的：（例如非關鍵分析）
- 可改為點擊才載入或改成普通連結的：（例如社群 SDK）
- 可移除的：（例如已停用的聊天工具）

請提出最小修改：
1. 先盤點 layout／site.js 實際載入了哪些腳本
2. 依上表延後、互動後載入或移除；不要默默刪掉我標記為必須保留的追蹤
3. 若使用 requestIdleCallback 或 loaded 後載入，請說明對早期 pageview 的取捨
4. 改完列出我應如何驗證：效能重測 + 分析事件是否仍會送出&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;AI-做完後,你要審核什麼JavaScript&quot;&gt;AI 做完後，你要審核什麼（JavaScript）&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;業務依賴&lt;/strong&gt;：確認沒有刪掉仍在用的聊天、廣告、轉換標籤；diff 裡出現追蹤 ID 變更要特別小心。&lt;li&gt;&lt;strong&gt;載入時機&lt;/strong&gt;：非關鍵腳本是否真的延後；關鍵轉換路徑是否仍即時或有可接受的備援。&lt;li&gt;&lt;strong&gt;功能回歸&lt;/strong&gt;：搜尋、主題切換、表單、分享按鈕在本機點一輪，確認第一次互動不會整頁壞掉。&lt;li&gt;&lt;strong&gt;分析驗證&lt;/strong&gt;：開 GA4／GTM 預覽或 Network 過濾相關請求，確認 pageview／轉換在典型瀏覽路徑仍會送出。&lt;li&gt;&lt;strong&gt;重測&lt;/strong&gt;：同一 URL 看 INP／主執行緒診斷是否改善；分數變好但事件消失，這輪算失敗，要回滾或縮小範圍。&lt;/ul&gt;&lt;h2 id=&quot;為什麼同一個建議,在-WordPress-上常常比較難落地?&quot;&gt;為什麼同一個建議，在 WordPress 上常常比較難落地？&lt;/h2&gt;&lt;p&gt;這不是說 WordPress 完全不能優化。簡單、乾淨的 WordPress 站也可能有很好的效能；反過來，11ty 如果塞滿大圖、第三方腳本和錯誤模板，也一樣會拿到差分數。&lt;p&gt;差異在於問題的可控程度：&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;PageSpeed Insights 顯示的問題&lt;th&gt;11ty 常見修改位置&lt;th&gt;WordPress 常見限制&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;LCP 圖片太慢&lt;td&gt;Markdown、template、&lt;code&gt;eleventy-img&lt;/code&gt;、HTML 屬性&lt;td&gt;佈景主題、圖片外掛、頁面編輯器可能都有關聯&lt;tr&gt;&lt;td&gt;CLS 版面位移&lt;td&gt;layout、CSS、Image Transform&lt;td&gt;主題元件、廣告或外掛動態插入內容&lt;tr&gt;&lt;td&gt;CSS 太多&lt;td&gt;直接整理模板和 CSS&lt;td&gt;主題與外掛各自載入，停用可能影響版面&lt;tr&gt;&lt;td&gt;JavaScript 阻塞&lt;td&gt;直接刪除、延後或修改載入策略&lt;td&gt;外掛可能依賴特定順序，更新後又重新注入&lt;tr&gt;&lt;td&gt;TTFB 偏高&lt;td&gt;靜態輸出、CDN、快取設定&lt;td&gt;PHP、資料庫、主機和快取外掛互相影響&lt;/table&gt;&lt;p&gt;簡單說，WordPress 的問題常是「我知道哪裡慢，但改了會不會破壞整個站？」；11ty 比較常是「我知道哪個檔案要改，改完 build 驗證就好」。&lt;h2 id=&quot;我會用-AI-加速修正,但不會讓-AI-直接亂改整站&quot;&gt;我會用 AI 加速修正，但不會讓 AI 直接亂改整站&lt;/h2&gt;&lt;p&gt;AI 很適合把 PageSpeed Insights 的診斷轉成候選 patch，例如：&lt;ul&gt;&lt;li&gt;找出 layout 中所有沒有尺寸的圖片。&lt;li&gt;檢查 hero 圖是否誤用了 lazy-loading。&lt;li&gt;依目前的模板補上 &lt;code&gt;fetchpriority&lt;/code&gt; 或 &lt;code&gt;srcset&lt;/code&gt;。&lt;li&gt;找出可能重複載入的 CSS 和 JavaScript。&lt;li&gt;產生一個小範圍的 CSS 或模板修改。&lt;/ul&gt;&lt;p&gt;但我會把 AI 當成能快速工作的實習生，而不是自動批准的部署工具。我的流程是：&lt;ol&gt;&lt;li&gt;先把 PageSpeed Insights 的原文診斷、目標指標和不能改動的範圍講清楚。&lt;li&gt;要 AI 一次只改一個檔案或一類問題。&lt;li&gt;讀 diff，確認它沒有順便改掉 URL、追蹤 ID 或其他不相關設定。&lt;li&gt;本機 build，檢查輸出 HTML。&lt;li&gt;用瀏覽器實際點一輪，確認圖片、選單、搜尋功能正常。&lt;li&gt;重新跑 PageSpeed Insights，並把改動和結果記錄下來。&lt;/ol&gt;&lt;h3 id=&quot;提示詞要寫清楚的五件事&quot;&gt;提示詞要寫清楚的五件事&lt;/h3&gt;&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;要寫清楚&lt;th&gt;為什麼重要&lt;th&gt;壞例子&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;目標頁面 URL&lt;td&gt;避免 AI 改到不相干的模板&lt;td&gt;「把網站變快」&lt;tr&gt;&lt;td&gt;只處理哪一個指標／診斷&lt;td&gt;方便對照前後測結果&lt;td&gt;「LCP、CLS、JS 一次修完」&lt;tr&gt;&lt;td&gt;診斷原文或截圖重點&lt;td&gt;讓 AI 對準真實元素，不是猜&lt;td&gt;「感覺首圖有點慢」&lt;tr&gt;&lt;td&gt;禁止改動的範圍&lt;td&gt;防止誤改追蹤、部署、文案&lt;td&gt;完全不設限制&lt;tr&gt;&lt;td&gt;完成後要回報什麼&lt;td&gt;你審核時有檢查清單&lt;td&gt;「改好跟我說一聲」&lt;/table&gt;&lt;h3 id=&quot;AI-交卷後的總審核清單&quot;&gt;AI 交卷後的總審核清單&lt;/h3&gt;&lt;p&gt;不管這輪修的是圖片還是腳本，我都會用同一張清單過一遍：&lt;ol&gt;&lt;li&gt;&lt;strong&gt;範圍&lt;/strong&gt;：diff 是否只涵蓋這次約定的問題？有沒有「順便重構」？&lt;li&gt;&lt;strong&gt;正確性&lt;/strong&gt;：輸出 HTML／CSS／JS 是否真的對應診斷（例如修 LCP 卻只改了下文 lazy 圖）？&lt;li&gt;&lt;strong&gt;回歸&lt;/strong&gt;：導覽、搜尋、深色模式、表單、內嵌內容是否仍可用？&lt;li&gt;&lt;strong&gt;資料&lt;/strong&gt;：若動過 GTM／分析，事件與轉換是否仍正確？&lt;li&gt;&lt;strong&gt;驗證&lt;/strong&gt;：同一 URL、相近條件重測後，目標指標是否改善？有沒有其他指標明顯變差？&lt;li&gt;&lt;strong&gt;紀錄&lt;/strong&gt;：這輪改了什麼、前後分數／診斷差異寫下來，方便之後回顧或回滾。&lt;/ol&gt;&lt;p&gt;AI 可以幫我縮短「從診斷到修改」的時間，但不能替我判斷該不該犧牲分析資料，也不能保證它產生的 HTML 一定符合這個網站的模板結構，最好是本機開發審核做完再部署。&lt;p&gt;先上線再祈禱，比較像賭咖啡因，不像做優化。&lt;h2 id=&quot;什麼情況不該只為了-PageSpeed-Insights-換成-11ty?&quot;&gt;什麼情況不該只為了 PageSpeed Insights 換成 11ty？&lt;/h2&gt;&lt;p&gt;如果網站核心是 WooCommerce、會員、論壇、即時資料或複雜表單，WordPress 的動態能力可能比純靜態效能更重要。若團隊每天都需要非技術人員從後台發文，也要把編輯效率算進去。&lt;p&gt;我會把選擇簡化成這樣：&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;如果你的情況是&lt;th&gt;比較合理的方向&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;文章和教學為主，願意用 Git，PageSpeed Insights 長期有明確問題&lt;td&gt;評估 11ty，或先把部分頁面靜態化&lt;tr&gt;&lt;td&gt;WordPress 站很簡單，只是圖片和腳本沒整理&lt;td&gt;先做圖片、快取、外掛和第三方腳本盤點&lt;tr&gt;&lt;td&gt;每天靠後台編輯，沒有技術人員維護 build&lt;td&gt;留在 WordPress，優先找出可安全調整的瓶頸&lt;tr&gt;&lt;td&gt;電商、會員和複雜表單是核心&lt;td&gt;留 WordPress，或評估 headless WordPress 加靜態前台&lt;/table&gt;&lt;p&gt;換成 11ty 也不是 SEO 自動升級。URL、301、sitemap、canonical、內容品質和內部連結仍然要維護。11ty 解決的是「前端和輸出 HTML 更容易控制」，不是替你完成整套 SEO。&lt;h2 id=&quot;結語把診斷建議變成可執行的工作清單&quot;&gt;結語：把診斷建議變成可執行的工作清單&lt;/h2&gt;&lt;p&gt;我喜歡用 11ty 做效能優化，並不是因為它能保證每個網站都拿到滿分，而是因為 PageSpeed Insights 指出問題後，通常可以直接找到對應的模板、CSS、圖片或腳本來修改。&lt;p&gt;最值得記住的三件事：&lt;ul&gt;&lt;li&gt;LCP 先找真正的最大元素，首圖不要盲目 lazy-load；提示詞要貼診斷原文，審核輸出 HTML 的 &lt;code&gt;eager&lt;/code&gt;／&lt;code&gt;fetchpriority&lt;/code&gt;。&lt;li&gt;CLS 先補圖片尺寸和版面預留；審核時確認尺寸是真比例，並用肉眼看載入有沒有跳。&lt;li&gt;INP 先盤點第三方 JavaScript；AI 延後腳本後，一定要驗證分析和轉換資料還在。&lt;/ul&gt;&lt;p&gt;如果你也在維護 11ty 網站，可以先選一個代表性頁面跑一次 &lt;a href=&quot;https://pagespeed.web.dev/&quot;&gt;PageSpeed Insights&lt;/a&gt;，把第一個最具體的診斷貼進上面的提示詞骨架，讓 AI 做最小修改，再用同一張審核清單過完、相同條件重測。先從一個問題開始，通常比一次叫 AI「把整站優化到 100 分」更容易得到可驗證的結果。審完，再來一杯咖啡。&lt;h2 id=&quot;參考資料&quot;&gt;參考資料&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://pagespeed.web.dev/&quot;&gt;PageSpeed Insights&lt;/a&gt; — Google 的網頁效能檢測工具，可查看 Lighthouse 實驗室資料與 CrUX 欄位資料。&lt;li&gt;&lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/overview&quot;&gt;Lighthouse 文件&lt;/a&gt; — Lighthouse 的用途、報告與本機測試說明。&lt;li&gt;&lt;a href=&quot;https://web.dev/articles/vitals&quot;&gt;Web Vitals（web.dev）&lt;/a&gt; — LCP、CLS、INP 等 Core Web Vitals 的定義與評估方式。&lt;li&gt;&lt;a href=&quot;https://web.dev/articles/lcp&quot;&gt;Largest Contentful Paint（web.dev）&lt;/a&gt; — LCP 的計算方式與常見優化方向。&lt;li&gt;&lt;a href=&quot;https://web.dev/articles/cls&quot;&gt;Cumulative Layout Shift（web.dev）&lt;/a&gt; — CLS 的成因、計算方式與版面穩定性建議。&lt;li&gt;&lt;a href=&quot;https://www.11ty.dev/docs/plugins/image/&quot;&gt;Eleventy Image&lt;/a&gt; — &lt;code&gt;@11ty/eleventy-img&lt;/code&gt; 的官方文件，說明圖片格式、尺寸與 &lt;code&gt;srcset&lt;/code&gt; 產生方式。&lt;/ul&gt;</content>
  </entry>
  <entry>
    <title>Pagefind 教學：如何為 Eleventy 靜態網站加入純前端搜尋</title>
    <link href="https://kamadiam.com/pagefind-eleventy-static-site-search/" />
    <updated>2026-08-04T06:51:00Z</updated>
    <id>https://kamadiam.com/pagefind-eleventy-static-site-search/</id>
    <content type="html">&lt;h1 id=&quot;Pagefind-教學如何為-Eleventy-靜態網站加入純前端搜尋&quot;&gt;Pagefind 教學：如何為 Eleventy 靜態網站加入純前端搜尋&lt;/h1&gt;&lt;p&gt;文章一多，讀者只靠導覽列和分類頁，找舊文就像在咖啡渣裡找關鍵字。這時先別急著架資料庫：對以文章為主的 Eleventy 靜態網站來說，Pagefind 通常已經很夠用。&lt;p&gt;這篇會帶你弄懂 Pagefind 的工作方式，接著完成 layout 標記、build 後建立索引，以及繁體中文搜尋的驗收。讀完後，你應該能判斷 Pagefind 是否適合自己的網站，也知道搜尋失靈時該從哪裡查起。&lt;div class=&quot;table-of-contents&quot;&gt;&lt;h2 id=&quot;目錄&quot;&gt;目錄&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/pagefind-eleventy-static-site-search/#Pagefind-%E6%98%AF%E4%BB%80%E9%BA%BC?&quot;&gt;Pagefind 是什麼？&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/pagefind-eleventy-static-site-search/#%E9%96%8B%E5%A7%8B%E5%89%8D%E5%85%88%E7%A2%BA%E8%AA%8D%E4%B8%89%E4%BB%B6%E4%BA%8B&quot;&gt;開始前先確認三件事&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/pagefind-eleventy-static-site-search/#%E7%AC%AC%E4%B8%80%E6%AD%A5%E9%81%B8%E6%93%87-Pagefind-UI-%E7%89%88%E6%9C%AC&quot;&gt;第一步：選擇 Pagefind UI 版本&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/pagefind-eleventy-static-site-search/#%E7%AC%AC%E4%BA%8C%E6%AD%A5%E5%91%8A%E8%A8%B4-Pagefind-%E8%A6%81%E7%B4%A2%E5%BC%95%E4%BB%80%E9%BA%BC&quot;&gt;第二步：告訴 Pagefind 要索引什麼&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/pagefind-eleventy-static-site-search/#%E7%AC%AC%E4%B8%89%E6%AD%A5%E5%9C%A8-Eleventy-build-%E5%BE%8C%E5%BB%BA%E7%AB%8B%E7%B4%A2%E5%BC%95&quot;&gt;第三步：在 Eleventy build 後建立索引&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/pagefind-eleventy-static-site-search/#%E7%AC%AC%E5%9B%9B%E6%AD%A5%E6%8A%8A%E6%B5%81%E7%A8%8B%E6%8E%A5%E9%80%B2-package.json&quot;&gt;第四步：把流程接進 package.json&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/pagefind-eleventy-static-site-search/#%E7%B9%81%E9%AB%94%E4%B8%AD%E6%96%87%E6%90%9C%E5%B0%8B%E8%A6%81%E6%80%8E%E9%BA%BC%E9%A9%97%E6%94%B6?&quot;&gt;繁體中文搜尋要怎麼驗收？&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/pagefind-eleventy-static-site-search/#%E5%B8%B8%E8%A6%8B%E5%95%8F%E9%A1%8C%E6%8E%92%E6%9F%A5&quot;&gt;常見問題排查&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/pagefind-eleventy-static-site-search/#%E7%B5%90%E8%AA%9E%E5%85%88%E8%A7%A3%E6%B1%BA%E6%90%9C%E5%B0%8B,%E5%86%8D%E8%80%83%E6%85%AE%E5%8D%87%E7%B4%9A&quot;&gt;結語：先解決搜尋，再考慮升級&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/pagefind-eleventy-static-site-search/#%E5%8F%83%E8%80%83%E8%B3%87%E6%96%99&quot;&gt;參考資料&lt;/a&gt;&lt;/ul&gt;&lt;/div&gt;&lt;h2 id=&quot;Pagefind-是什麼?&quot;&gt;Pagefind 是什麼？&lt;/h2&gt;&lt;p&gt;Pagefind 是在 build 後處理靜態 HTML 的搜尋工具。它會讀取網站輸出的 HTML，建立一組可由瀏覽器載入的索引檔；訪客搜尋時，結果在前端計算，不需要另開一個搜尋 API 或資料庫伺服器。&lt;p&gt;所以它很適合個人部落格、文件站和產品說明站。文章更新後重新 build，索引也跟著重建。反過來說，如果你的資料需要登入權限、個人化結果或即時更新，Pagefind 不是後端搜尋的替代品——咖啡濾紙也不能拿來當資料庫。&lt;h2 id=&quot;開始前先確認三件事&quot;&gt;開始前先確認三件事&lt;/h2&gt;&lt;p&gt;你需要一個能正常輸出 &lt;code&gt;_site/&lt;/code&gt; 的 Eleventy 專案，並確認共用 layout 的 &lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt; 有語言標記：&lt;pre class=&quot;language-njk&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-njk&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;!&lt;span class=&quot;token variable&quot;&gt;doctype&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;html&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;html&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;lang&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;zh-Hant&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
	&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;head&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
		
	&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;head&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
	&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
		
	&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;html&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;語言標記不是裝飾。Pagefind 會在索引時讀取 &lt;code&gt;&amp;lt;html lang&amp;gt;&lt;/code&gt;，依語言分開建立索引；瀏覽器初始化搜尋時，也會依目前頁面的語言載入對應索引。繁體中文網站通常可以使用 &lt;code&gt;zh-Hant&lt;/code&gt;，但仍應用實際文章內容測試。&lt;p&gt;另外，請先單獨確認 Eleventy build 沒有錯誤。Pagefind 只能處理已經產生的靜態檔案，不能替你修好前一步的模板錯誤。&lt;h2 id=&quot;第一步選擇-Pagefind-UI-版本&quot;&gt;第一步：選擇 Pagefind UI 版本&lt;/h2&gt;&lt;p&gt;Pagefind 的搜尋索引和搜尋介面是兩件事。索引先由 CLI 建立，UI 再載入 &lt;code&gt;/pagefind/&lt;/code&gt; 裡產生的資產。&lt;p&gt;目前常見的兩種 UI 寫法，不能直接混在一起：&lt;ul&gt;&lt;li&gt;Pagefind 1.4 及更早版本常用 Default UI：載入 &lt;code&gt;pagefind-ui.css&lt;/code&gt;、&lt;code&gt;pagefind-ui.js&lt;/code&gt;，再用 &lt;code&gt;new PagefindUI(...)&lt;/code&gt; 初始化。&lt;li&gt;Pagefind 1.5 開始提供 Component UI：使用 &lt;code&gt;pagefind-component-ui.css&lt;/code&gt;、&lt;code&gt;pagefind-component-ui.js&lt;/code&gt; 和自訂元素，例如 &lt;code&gt;&amp;lt;pagefind-modal&amp;gt;&lt;/code&gt;。&lt;/ul&gt;&lt;p&gt;先看專案的 &lt;code&gt;package.json&lt;/code&gt; 版本，再照對應文件操作。本文以 Pagefind 1.4 的 Default UI 示範，因為這是目前專案使用的版本。&lt;p&gt;在共用 layout 放入 CSS 與搜尋容器：&lt;pre class=&quot;language-njk&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-njk&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;link&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;stylesheet&quot;&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/pagefind/pagefind-ui.css&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;

&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;div&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;search&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;接著在 JavaScript 中載入並初始化 UI：&lt;pre class=&quot;language-js&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; pagefindScript &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createElement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;script&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
pagefindScript&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;src &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/pagefind/pagefind-ui.js&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
pagefindScript&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;onload&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;PagefindUI&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token literal-property property&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;#search&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;token literal-property property&quot;&gt;showSubResults&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;body&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;appendChild&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;pagefindScript&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;你也可以在頁面載入時直接用 &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; 載入；只有在訪客打開搜尋時才載入，則能避免每個頁面一開始都下載搜尋程式。重點是：不論採用哪種載入時機，索引尚未建立前，&lt;code&gt;/pagefind/&lt;/code&gt; 下的檔案都不存在，看到 404 不一定是 UI 寫錯。&lt;h2 id=&quot;第二步告訴-Pagefind-要索引什麼&quot;&gt;第二步：告訴 Pagefind 要索引什麼&lt;/h2&gt;&lt;p&gt;Pagefind 預設會處理頁面內容，但你可以用 &lt;code&gt;data-pagefind-body&lt;/code&gt; 把範圍縮小，用 &lt;code&gt;data-pagefind-ignore&lt;/code&gt; 排除固定介面：&lt;pre class=&quot;language-njk&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-njk&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
	&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;header&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;pagefind&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;ignore&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
		網站導覽
	&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;header&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;

	&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;main&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;pagefind&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
		
	&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;

	&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;footer&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;pagefind&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;ignore&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
		頁尾資訊
	&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;footer&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;這三個標記各自做一件事：&lt;ol&gt;&lt;li&gt;&lt;code&gt;lang=&amp;quot;zh-Hant&amp;quot;&lt;/code&gt; 提供語言資訊，讓 Pagefind 進行對應的語言處理。&lt;li&gt;&lt;code&gt;data-pagefind-body&lt;/code&gt; 指定頁面真正要被搜尋的內容。&lt;li&gt;&lt;code&gt;data-pagefind-ignore&lt;/code&gt; 排除導覽列、頁尾或分享按鈕，避免固定文字污染每一筆結果。&lt;/ol&gt;&lt;p&gt;有一個容易漏掉的規則：只要網站任何頁面使用了 &lt;code&gt;data-pagefind-body&lt;/code&gt;，沒有這個標記的頁面就不會被索引。因此，如果首頁、分類頁也要出現在搜尋結果裡，它們同樣要標記搜尋主體。&lt;p&gt;&lt;code&gt;data-pagefind-ignore&lt;/code&gt; 是「排除元素」；它不會讓整頁消失。把它放在 header 或 footer 上，文章正文仍然會被索引。&lt;h2 id=&quot;第三步在-Eleventy-build-後建立索引&quot;&gt;第三步：在 Eleventy build 後建立索引&lt;/h2&gt;&lt;p&gt;先建立網站，再把輸出目錄交給 Pagefind。使用專案已安裝並鎖定版本的 CLI，通常比每次用 &lt;code&gt;npx -y&lt;/code&gt; 下載最新版更容易重現：&lt;pre class=&quot;language-shell&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;pnpm&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;exec&lt;/span&gt; eleventy
&lt;span class=&quot;token function&quot;&gt;pnpm&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;exec&lt;/span&gt; pagefind &lt;span class=&quot;token parameter variable&quot;&gt;--site&lt;/span&gt; _site &lt;span class=&quot;token parameter variable&quot;&gt;--glob&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;**/*.html&#39;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;成功後，輸出目錄會出現 &lt;code&gt;_site/pagefind/&lt;/code&gt;，裡面包含索引資料，以及 UI 需要的 JavaScript 和 CSS。Pagefind 本身沒有搜尋後端；這些檔案部署到同一個靜態網站後，瀏覽器就能載入它們。&lt;p&gt;如果想快速測試，也可以讓 Pagefind 在建立索引後提供本機靜態伺服器：&lt;pre class=&quot;language-shell&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;pnpm&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;exec&lt;/span&gt; pagefind &lt;span class=&quot;token parameter variable&quot;&gt;--site&lt;/span&gt; _site &lt;span class=&quot;token parameter variable&quot;&gt;--glob&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;**/*.html&#39;&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--serve&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;這個 &lt;code&gt;--serve&lt;/code&gt; 是方便開發時測試的本機伺服器，不是正式環境必須部署的服務。不要直接用 &lt;code&gt;file://&lt;/code&gt; 開啟 HTML，因為瀏覽器的模組載入和 fetch 請求可能受到本機檔案權限限制。&lt;h2 id=&quot;第四步把流程接進-package.json&quot;&gt;第四步：把流程接進 package.json&lt;/h2&gt;&lt;p&gt;手動跑兩個指令可以驗證概念，但很容易忘記第二個。把 Pagefind 接在 Eleventy 後面，每次 build 就會自動更新索引：&lt;pre class=&quot;language-json&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;&quot;scripts&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token property&quot;&gt;&quot;build&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;NODE_ENV=production ELEVENTY_PROFILE=true pnpm exec eleventy &amp;amp;&amp;amp; pnpm exec pagefind --site _site --glob &#92;&quot;**/*.html&#92;&quot;&quot;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;實際專案的 build script 可能還有清理、Vite 或其他收尾步驟；請把 Pagefind 放在「所有 HTML 都完成之後」。如果部署平台只執行 Eleventy，卻沒有執行 Pagefind，正式站自然不會憑空長出搜尋索引。&lt;p&gt;也可以在 Eleventy 的 &lt;code&gt;eleventy.after&lt;/code&gt; 事件裡呼叫 Pagefind，但這會讓 Eleventy 設定檔同時負責建站和啟動外部程序。對多數專案而言，&lt;code&gt;package.json&lt;/code&gt; 用 &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; 串接比較容易看懂、除錯，也能確保前一步失敗時不會誤把舊索引當成新成果。&lt;h2 id=&quot;繁體中文搜尋要怎麼驗收?&quot;&gt;繁體中文搜尋要怎麼驗收？&lt;/h2&gt;&lt;p&gt;Pagefind 支援中文的分詞處理，但中文不像英文那樣以空白分隔；搜尋結果仍然取決於索引內容和查詢方式。這不是「加上 &lt;code&gt;lang&lt;/code&gt; 就保證什麼都找得到」，所以請用真正的文章做測試。&lt;p&gt;部署前至少走過這份清單：&lt;ul&gt;&lt;li&gt;[ ] Eleventy build 成功，沒有模板或資產錯誤。&lt;li&gt;[ ] &lt;code&gt;_site/pagefind/&lt;/code&gt; 存在，且正式網址能載入 &lt;code&gt;/pagefind/&lt;/code&gt; 下的檔案。&lt;li&gt;[ ] 搜尋一個確定存在的文章標題，可以回傳結果。&lt;li&gt;[ ] 搜尋兩三個繁體中文詞組，例如「網站安全」或「靜態網站」。&lt;li&gt;[ ] 搜尋結果沒有被導覽列、頁尾或分享按鈕的固定文字洗版。&lt;li&gt;[ ] 修改文章後重新 build，結果中的摘要已更新。&lt;/ul&gt;&lt;p&gt;若網站同時有多種語言，請讓每個頁面的 &lt;code&gt;&amp;lt;html lang&amp;gt;&lt;/code&gt; 正確反映內容，不要全部硬寫成同一種語言。Pagefind 會依語言分開索引；標記錯誤，等於把書放進錯的書架，搜尋當然會繞遠路。&lt;h2 id=&quot;常見問題排查&quot;&gt;常見問題排查&lt;/h2&gt;&lt;h3 id=&quot;搜尋框出現,卻沒有結果&quot;&gt;搜尋框出現，卻沒有結果&lt;/h3&gt;&lt;p&gt;先看瀏覽器 Network 面板：&lt;code&gt;/pagefind/pagefind-ui.js&lt;/code&gt;、&lt;code&gt;/pagefind/pagefind-ui.css&lt;/code&gt; 和索引檔是否回傳 404。再確認 Pagefind 指向的是包含 HTML 的輸出目錄，而不是專案原始碼目錄。&lt;h3 id=&quot;中文結果很少&quot;&gt;中文結果很少&lt;/h3&gt;&lt;p&gt;檢查 &lt;code&gt;&amp;lt;html lang&amp;gt;&lt;/code&gt; 是否存在且寫對，並用文章內確實出現的詞組測試。也檢查 &lt;code&gt;data-pagefind-body&lt;/code&gt; 是否誤放在空容器，或文章所在頁面根本沒有這個標記。不要只因為終端機出現 stemming 相關提示就移除語言標記：中文搜尋仍可運作，只是 Pagefind 不會以英文詞幹還原的方式處理中文。&lt;h3 id=&quot;首頁或分類頁沒有出現在結果裡&quot;&gt;首頁或分類頁沒有出現在結果裡&lt;/h3&gt;&lt;p&gt;這通常是 &lt;code&gt;data-pagefind-body&lt;/code&gt; 的範圍規則造成的。只標記文章 layout 時，沒有標記的首頁和分類頁會被排除；在同樣希望被搜尋的頁面補上搜尋主體即可。&lt;h3 id=&quot;搜尋結果一直出現導覽列文字&quot;&gt;搜尋結果一直出現導覽列文字&lt;/h3&gt;&lt;p&gt;將不需要搜尋的固定區塊加上 &lt;code&gt;data-pagefind-ignore&lt;/code&gt;，刪除舊的 &lt;code&gt;_site/pagefind/&lt;/code&gt; 後重新 build。只改模板、不重建索引，搜尋結果不會自己更新——索引沒有讀心術，只有讀檔術。&lt;h3 id=&quot;本機測試看起來壞掉&quot;&gt;本機測試看起來壞掉&lt;/h3&gt;&lt;p&gt;確認不是用 &lt;code&gt;file://&lt;/code&gt; 開啟 HTML，也確認 Pagefind 產出的 &lt;code&gt;/pagefind/&lt;/code&gt; 路徑和網站部署的 base path 一致。使用 &lt;code&gt;--serve&lt;/code&gt; 或其他靜態檔案伺服器重新測試，通常很快就能分辨是瀏覽器載入問題還是索引內容問題。&lt;h2 id=&quot;結語先解決搜尋,再考慮升級&quot;&gt;結語：先解決搜尋，再考慮升級&lt;/h2&gt;&lt;p&gt;對大多數 Eleventy 部落格來說，第一版站內搜尋不需要資料庫。你只要：&lt;ol&gt;&lt;li&gt;在 layout 加入與 Pagefind 版本相符的 UI。&lt;li&gt;設定正確的 &lt;code&gt;lang&lt;/code&gt;、&lt;code&gt;data-pagefind-body&lt;/code&gt; 和 &lt;code&gt;data-pagefind-ignore&lt;/code&gt;。&lt;li&gt;在每次 Eleventy build 後執行 Pagefind。&lt;li&gt;用實際的繁體中文查詢驗收。&lt;/ol&gt;&lt;p&gt;這樣就能讓讀者搜尋已部署的文章。未來若真的需要會員權限、個人化排序或即時資料，再評估後端或託管搜尋服務；先把今天的搜尋做好，咖啡才不會冷得太有深度。&lt;h2 id=&quot;參考資料&quot;&gt;參考資料&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://pagefind.app/docs/&quot;&gt;Pagefind 官方文件&lt;/a&gt;：總覽、安裝與基本整合方式。&lt;li&gt;&lt;a href=&quot;https://pagefind.app/docs/running-pagefind/&quot;&gt;Running Pagefind&lt;/a&gt;：說明 &lt;code&gt;--site&lt;/code&gt;、&lt;code&gt;--serve&lt;/code&gt; 等 CLI 用法。&lt;li&gt;&lt;a href=&quot;https://pagefind.app/docs/indexing/&quot;&gt;Customizing the index&lt;/a&gt;：說明 &lt;code&gt;data-pagefind-body&lt;/code&gt; 與 &lt;code&gt;data-pagefind-ignore&lt;/code&gt;。&lt;li&gt;&lt;a href=&quot;https://pagefind.app/docs/multilingual/&quot;&gt;Multilingual Search&lt;/a&gt;：說明 &lt;code&gt;lang&lt;/code&gt;、多語言索引與中文分詞。&lt;li&gt;&lt;a href=&quot;https://pagefind.app/docs/ui-usage/&quot;&gt;Pagefind UI Usage&lt;/a&gt;：說明 Default UI 的載入和初始化方式。&lt;li&gt;&lt;a href=&quot;https://www.11ty.dev/docs/events/&quot;&gt;Eleventy Configuration Events&lt;/a&gt;：查閱 &lt;code&gt;eleventy.after&lt;/code&gt; 等 Eleventy 事件。&lt;/ul&gt;</content>
  </entry>
  <entry>
    <title>11ty 與 Hostinger 快取怎麼設定？從 .htaccess 到更新驗收</title>
    <link href="https://kamadiam.com/11ty-hostinger-cache-setup/" />
    <updated>2026-08-04T03:44:00Z</updated>
    <id>https://kamadiam.com/11ty-hostinger-cache-setup/</id>
    <content type="html">&lt;p&gt;你把新文章部署上去，自己看得到最新版，讀者卻還在看昨天的內容？先別急著重跑 11ty，也別一杯接一杯咖啡地清快取。很多時候，問題不是建置失敗，而是瀏覽器、Hostinger、CDN 或 HTTP headers 還在提供舊檔案。&lt;p&gt;這篇會帶你用 11ty 建置 &lt;code&gt;_site&lt;/code&gt;，再依檔案類型安排 HTML、CSS、JavaScript、圖片、字型和 Pagefind 搜尋資產的快取策略。你也會看到如何在 Hostinger hPanel 清除快取，以及用 &lt;code&gt;curl&lt;/code&gt; 檢查公開網站實際回傳的 headers。快取不是壞掉的記憶體，只是它記得比你久了一點。&lt;div class=&quot;table-of-contents&quot;&gt;&lt;h2 id=&quot;目錄&quot;&gt;目錄&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/11ty-hostinger-cache-setup/#%E5%85%88%E8%A8%98%E4%BD%8F%E5%BF%AB%E5%8F%96%E5%8E%9F%E5%89%87&quot;&gt;先記住快取原則&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/11ty-hostinger-cache-setup/#11ty-%E8%88%87-Hostinger-%E5%90%84%E8%87%AA%E8%B2%A0%E8%B2%AC%E4%BB%80%E9%BA%BC?&quot;&gt;11ty 與 Hostinger 各自負責什麼？&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/11ty-hostinger-cache-setup/#%E9%83%A8%E7%BD%B2%E5%89%8D%E5%85%88%E7%A2%BA%E8%AA%8D-_site&quot;&gt;部署前先確認 _site&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/11ty-hostinger-cache-setup/#%E4%BE%9D%E5%B0%88%E6%A1%88%E8%A8%AD%E5%AE%9A-public/.htaccess&quot;&gt;依專案設定 public/.htaccess&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/11ty-hostinger-cache-setup/#%E9%99%A4%E4%BA%86%E5%BF%AB%E5%8F%96,.htaccess-%E9%82%84%E8%83%BD%E8%B2%A0%E8%B2%AC%E5%93%AA%E4%BA%9B%E4%BA%8B%E6%83%85?&quot;&gt;除了快取，.htaccess 還能負責哪些事情？&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/11ty-hostinger-cache-setup/#HSTS-%E4%B8%8D%E6%98%AF%E6%AA%94%E6%A1%88%E5%BF%AB%E5%8F%96&quot;&gt;HSTS 不是檔案快取&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/11ty-hostinger-cache-setup/#%E9%83%A8%E7%BD%B2%E5%BE%8C%E5%88%B0-hPanel-%E6%B8%85%E9%99%A4%E5%BF%AB%E5%8F%96&quot;&gt;部署後到 hPanel 清除快取&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/11ty-hostinger-cache-setup/#%E7%94%A8-curl-%E9%A9%97%E6%94%B6-response-headers&quot;&gt;用 curl 驗收 response headers&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/11ty-hostinger-cache-setup/#%E5%BF%AB%E5%8F%96%E8%A8%AD%E5%AE%9A%E6%B1%BA%E7%AD%96%E8%A1%A8&quot;&gt;快取設定決策表&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/11ty-hostinger-cache-setup/#%E5%B8%B8%E8%A6%8B%E5%95%8F%E9%A1%8C&quot;&gt;常見問題&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/11ty-hostinger-cache-setup/#%E9%83%A8%E7%BD%B2%E9%A9%97%E6%94%B6%E5%B7%A5%E5%85%B7&quot;&gt;部署驗收工具&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/11ty-hostinger-cache-setup/#%E7%B5%90%E8%AA%9E%E5%85%88%E5%88%86%E5%B1%A4,%E5%86%8D%E6%B1%BA%E5%AE%9A-TTL&quot;&gt;結語：先分層，再決定 TTL&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/11ty-hostinger-cache-setup/#%E5%8F%83%E8%80%83%E8%B3%87%E6%96%99&quot;&gt;參考資料&lt;/a&gt;&lt;/ul&gt;&lt;/div&gt;&lt;h2 id=&quot;先記住快取原則&quot;&gt;先記住快取原則&lt;/h2&gt;&lt;p&gt;不要把所有檔案都快取一年。比較穩定的分法是：&lt;ul&gt;&lt;li&gt;&lt;strong&gt;HTML&lt;/strong&gt;：短時間快取，或要求瀏覽器重新驗證，因為文章和首頁會更新。&lt;li&gt;&lt;strong&gt;CSS、JavaScript、圖片、字型&lt;/strong&gt;：如果檔名包含版本號或內容指紋，可以長效快取。&lt;li&gt;&lt;strong&gt;Pagefind 搜尋資產&lt;/strong&gt;：搜尋索引會隨文章更新，不能直接套用一般靜態資產的一年 TTL。&lt;/ul&gt;&lt;p&gt;簡單說，內容頁要容易更新；URL 已經跟著內容改變的資產，才適合快取久一點。&lt;h2 id=&quot;11ty-與-Hostinger-各自負責什麼?&quot;&gt;11ty 與 Hostinger 各自負責什麼？&lt;/h2&gt;&lt;p&gt;11ty 負責把 Markdown、模板和資產建置成 &lt;code&gt;_site&lt;/code&gt; 裡的靜態檔案。它不會替你決定訪客的瀏覽器或 Hostinger CDN 要快取多久。&lt;p&gt;上線後的快取主要分成三層：&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;快取層級&lt;th&gt;作用&lt;th&gt;更新不生效時先檢查什麼&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;瀏覽器&lt;td&gt;減少訪客重複下載檔案&lt;td&gt;用無痕視窗測試&lt;tr&gt;&lt;td&gt;Hostinger 快取&lt;td&gt;減少來源站重複處理請求&lt;td&gt;hPanel 的 Clear cache 或 Cache Manager&lt;tr&gt;&lt;td&gt;CDN&lt;td&gt;讓訪客從較近的節點取得檔案&lt;td&gt;依 CDN 介面執行 Purge／Flush&lt;/table&gt;&lt;p&gt;所以，重新執行 11ty 只會更新輸出檔案，不能保證所有快取層立刻換成新版本。&lt;h2 id=&quot;部署前先確認-_site&quot;&gt;部署前先確認 &lt;code&gt;_site&lt;/code&gt;&lt;/h2&gt;&lt;p&gt;在專案根目錄執行：&lt;pre class=&quot;language-bash&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;npx @11ty/eleventy&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;本專案的 &lt;code&gt;pnpm run build&lt;/code&gt; 還會在 Eleventy 完成後執行 Pagefind，並寫入 &lt;code&gt;_site/pagefind/&lt;/code&gt;。如果只是先檢查 Eleventy 輸出，可以執行上面的指令；正式部署則建議直接執行：&lt;pre class=&quot;language-bash&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;pnpm&lt;/span&gt; run build&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;接著確認 &lt;code&gt;_site&lt;/code&gt; 裡有：&lt;ul&gt;&lt;li&gt;&lt;code&gt;_site/index.html&lt;/code&gt;&lt;li&gt;最新文章的 HTML&lt;li&gt;CSS 和 JavaScript&lt;li&gt;圖片與字型&lt;li&gt;&lt;code&gt;pagefind/&lt;/code&gt; 搜尋資產（如果網站有使用 Pagefind）&lt;/ul&gt;&lt;p&gt;上傳時，要把 &lt;code&gt;_site&lt;/code&gt; &lt;strong&gt;裡面的內容&lt;/strong&gt;放到 Hostinger 的網站根目錄，常見位置是 &lt;code&gt;public_html&lt;/code&gt;。不要把整個專案原始碼上傳到公開目錄，也不要多包一層 &lt;code&gt;_site&lt;/code&gt; 資料夾。&lt;h2 id=&quot;依專案設定-public/.htaccess&quot;&gt;依專案設定 &lt;code&gt;public/.htaccess&lt;/code&gt;&lt;/h2&gt;&lt;p&gt;本專案的 &lt;code&gt;public/.htaccess&lt;/code&gt; 使用 Apache 的 &lt;code&gt;mod_expires&lt;/code&gt;、&lt;code&gt;mod_headers&lt;/code&gt; 和 &lt;code&gt;mod_setenvif&lt;/code&gt; 管理快取與 response headers。下面是與目前設定相同的重點片段；完整檔案還包含重新導向、安全標頭、Gzip 和目錄列表設定。&lt;pre class=&quot;language-apache&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-apache&quot;&gt;&amp;lt;IfModule mod_expires.c&gt;
  ExpiresActive On
  ExpiresDefault &quot;access plus 5 minutes&quot;
  ExpiresByType text/html &quot;access plus 3 minutes&quot;
  ExpiresByType text/css &quot;access plus 1 month&quot;
  ExpiresByType application/javascript &quot;access plus 1 month&quot;
  ExpiresByType application/x-javascript &quot;access plus 1 month&quot;
  ExpiresByType image/jpeg &quot;access plus 1 month&quot;
  ExpiresByType image/gif &quot;access plus 1 month&quot;
  ExpiresByType image/png &quot;access plus 1 month&quot;
  ExpiresByType image/webp &quot;access plus 1 month&quot;
  ExpiresByType image/svg+xml &quot;access plus 1 month&quot;
  ExpiresByType font/ttf &quot;access plus 1 month&quot;
  ExpiresByType font/otf &quot;access plus 1 month&quot;
  ExpiresByType font/woff &quot;access plus 1 month&quot;
  ExpiresByType font/woff2 &quot;access plus 1 month&quot;
&amp;lt;/IfModule&gt;

&amp;lt;IfModule mod_headers.c&gt;
  &amp;lt;FilesMatch &quot;&#92;.(?:html?)$&quot;&gt;
    Header set Cache-Control &quot;public, max-age=180&quot;
  &amp;lt;/FilesMatch&gt;
  &amp;lt;FilesMatch &quot;&#92;.(?:css|js|gif|jpe?g|png|webp|svg|ico|woff2?)$&quot;&gt;
    Header set Cache-Control &quot;public, max-age=31536000, immutable&quot;
  &amp;lt;/FilesMatch&gt;
  Header always set Strict-Transport-Security &quot;max-age=31536000; includeSubDomains&quot;
&amp;lt;/IfModule&gt;

&amp;lt;IfModule mod_setenvif.c&gt;
  SetEnvIfNoCase Request_URI &quot;^/pagefind/&quot; pagefind_asset
&amp;lt;/IfModule&gt;
&amp;lt;IfModule mod_headers.c&gt;
  Header set Cache-Control &quot;no-cache, must-revalidate&quot; env=pagefind_asset
&amp;lt;/IfModule&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;這些設定分別做什麼?&quot;&gt;這些設定分別做什麼？&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;ExpiresActive On&lt;/code&gt;：啟用 Apache 的過期時間設定。&lt;li&gt;&lt;code&gt;ExpiresDefault &amp;quot;access plus 5 minutes&amp;quot;&lt;/code&gt;：沒有特別指定類型的檔案，預設快取五分鐘。&lt;li&gt;&lt;code&gt;ExpiresByType text/html &amp;quot;access plus 3 minutes&amp;quot;&lt;/code&gt;：HTML 的 &lt;code&gt;Expires&lt;/code&gt; 基準為三分鐘。&lt;li&gt;CSS、JavaScript、圖片和字型的 &lt;code&gt;ExpiresByType&lt;/code&gt;：先設定為快取一個月，之後資產規則會再設定明確的 &lt;code&gt;Cache-Control&lt;/code&gt;。&lt;li&gt;&lt;code&gt;Cache-Control: public, max-age=31536000, immutable&lt;/code&gt;：把符合檔名規則的 CSS、JavaScript、部分圖片、圖示與 woff 字型實際設定為快取一年。&lt;li&gt;&lt;code&gt;SetEnvIfNoCase Request_URI &amp;quot;^/pagefind/&amp;quot; pagefind_asset&lt;/code&gt;：找出 &lt;code&gt;/pagefind/&lt;/code&gt; 開頭的檔案。&lt;li&gt;&lt;code&gt;Header set Cache-Control &amp;quot;no-cache, must-revalidate&amp;quot; env=pagefind_asset&lt;/code&gt;：讓 Pagefind 更新後立即重新驗證。&lt;li&gt;&lt;code&gt;Strict-Transport-Security&lt;/code&gt;：不是檔案快取，而是告訴瀏覽器未來一年都使用 HTTPS；目前也包含 &lt;code&gt;includeSubDomains&lt;/code&gt;。&lt;/ul&gt;&lt;h3 id=&quot;一年快取有一個容易忽略的風險&quot;&gt;一年快取有一個容易忽略的風險&lt;/h3&gt;&lt;p&gt;目前的 &lt;code&gt;&amp;lt;FilesMatch&amp;gt;&lt;/code&gt; 是按照副檔名套用一年快取，並沒有檢查檔名是否真的含有內容雜湊。因此沒有版本號的 &lt;code&gt;style.css&lt;/code&gt; 或 &lt;code&gt;app.js&lt;/code&gt; 也會進入一年快取。如果這些檔案在原 URL 直接更新，訪客可能長時間拿到舊版本。&lt;p&gt;另一方面，&lt;code&gt;mjs&lt;/code&gt;、&lt;code&gt;avif&lt;/code&gt;、&lt;code&gt;ttf&lt;/code&gt; 和 &lt;code&gt;otf&lt;/code&gt; 雖然有一個月的 &lt;code&gt;ExpiresByType&lt;/code&gt;，卻不符合目前的 &lt;code&gt;&amp;lt;FilesMatch&amp;gt;&lt;/code&gt;，不會拿到一年期的 &lt;code&gt;Cache-Control&lt;/code&gt;。要維持一年 TTL，部署流程就必須確保資產 URL 會隨內容改變，或另外為未版本化資產加上較短的規則。&lt;h3 id=&quot;為什麼-Pagefind-需要單獨設定?&quot;&gt;為什麼 Pagefind 需要單獨設定？&lt;/h3&gt;&lt;p&gt;Pagefind 的檔案可能同時符合一般 JavaScript 或資產檔案規則，因此先拿到一年的長效快取。接著，&lt;code&gt;env=pagefind_asset&lt;/code&gt; 條件會再套用重新驗證的 &lt;code&gt;Cache-Control&lt;/code&gt;：&lt;pre class=&quot;language-http&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-http&quot;&gt;&lt;span class=&quot;token header&quot;&gt;&lt;span class=&quot;token header-name keyword&quot;&gt;Cache-Control&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token header-value&quot;&gt;no-cache, must-revalidate&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;這個例外很重要：一般資產可以快取一年，但搜尋索引不應該跟著使用同樣長的 TTL。&lt;blockquote&gt;&lt;p&gt;&lt;code&gt;Header&lt;/code&gt; 需要 Apache &lt;code&gt;mod_headers&lt;/code&gt;，&lt;code&gt;ExpiresByType&lt;/code&gt; 需要 &lt;code&gt;mod_expires&lt;/code&gt;，Pagefind 的環境變數需要 &lt;code&gt;mod_setenvif&lt;/code&gt;。如果修改 &lt;code&gt;.htaccess&lt;/code&gt; 後出現 500 錯誤，先還原修改，再逐段確認 Hostinger 是否支援相關模組與語法。&lt;/blockquote&gt;&lt;h2 id=&quot;除了快取,.htaccess-還能負責哪些事情?&quot;&gt;除了快取，&lt;code&gt;.htaccess&lt;/code&gt; 還能負責哪些事情？&lt;/h2&gt;&lt;p&gt;&lt;code&gt;.htaccess&lt;/code&gt; 不只是快取設定檔，也可以集中管理 Apache 網站常見的伺服器規則。以下功能不一定全部都要啟用，應依網站需求和主機支援的 Apache 模組決定：&lt;ul&gt;&lt;li&gt;&lt;strong&gt;靜態網站路由&lt;/strong&gt;：使用 &lt;code&gt;mod_rewrite&lt;/code&gt; 管理重新導向或找不到頁面的處理方式。11ty 已經產生檔案式路由時，不要直接套用 SPA 的「全部導向 &lt;code&gt;/index.html&lt;/code&gt;」規則，否則原本的 404 和文章路徑可能一起被煮成一鍋。&lt;li&gt;&lt;strong&gt;301 重新導向&lt;/strong&gt;：將舊網址導向新網址，適合網站改版、文章改名或網址結構變更時使用。大量重定向最好由腳本產生，避免手動維護時遺漏或重複。&lt;li&gt;&lt;strong&gt;安全標頭&lt;/strong&gt;：可設定 &lt;code&gt;X-Frame-Options&lt;/code&gt;、&lt;code&gt;X-Content-Type-Options&lt;/code&gt; 等 response headers，降低點擊劫持和 MIME 類型嗅探等風險。新增標頭前，應先確認不會影響網站嵌入或第三方服務。&lt;li&gt;&lt;strong&gt;Gzip 壓縮&lt;/strong&gt;：使用 &lt;code&gt;mod_deflate&lt;/code&gt; 壓縮 HTML、CSS、JavaScript 和 XML 等文字檔，減少傳輸量。JPEG、PNG、WebP 等通常已經壓縮，不需要再次壓縮。&lt;li&gt;&lt;strong&gt;目錄保護&lt;/strong&gt;：&lt;code&gt;Options -Indexes&lt;/code&gt; 可避免訪客直接看到沒有首頁檔案的資料夾列表。&lt;li&gt;&lt;strong&gt;HTTP 導向 HTTPS&lt;/strong&gt;：使用 &lt;code&gt;mod_rewrite&lt;/code&gt; 將 HTTP 請求重新導向 HTTPS。啟用前要先確認 SSL 憑證正常；HSTS 只會要求瀏覽器後續使用 HTTPS，不會代替 HTTP → HTTPS 的重新導向。&lt;/ul&gt;&lt;p&gt;修改 &lt;code&gt;.htaccess&lt;/code&gt; 前，先確認 Hostinger 支援所需的 Apache 模組與語法。建議一次只加入一個功能，修改後用 &lt;code&gt;curl -I&lt;/code&gt; 和瀏覽器測試；若出現 500 錯誤，先還原最近一次修改，再逐段排查。&lt;h2 id=&quot;HSTS-不是檔案快取&quot;&gt;HSTS 不是檔案快取&lt;/h2&gt;&lt;p&gt;這一行：&lt;pre class=&quot;language-apache&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-apache&quot;&gt;Header always set Strict-Transport-Security &quot;max-age=31536000; includeSubDomains&quot;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;意思是瀏覽器在接下來一年內，連線到這個網站時優先使用 HTTPS。它不會決定 HTML、CSS 或圖片要存多久，因此不要把 HSTS 和 &lt;code&gt;Cache-Control&lt;/code&gt; 當成同一種設定。&lt;p&gt;只有在 HTTPS 已經完全正常，而且所有會使用到的子網域也都能使用 HTTPS 時，才適合啟用較長的 HSTS 期限。&lt;h2 id=&quot;部署後到-hPanel-清除快取&quot;&gt;部署後到 hPanel 清除快取&lt;/h2&gt;&lt;p&gt;每次部署後，依序做以下檢查：&lt;ol&gt;&lt;li&gt;進入 Hostinger hPanel 的網站 Dashboard，執行 &lt;strong&gt;Clear cache&lt;/strong&gt;。不同方案或 hPanel 版本的選單名稱可能略有不同。&lt;li&gt;如果只想處理單一頁面，到 &lt;strong&gt;Advanced → Cache Manager&lt;/strong&gt; 輸入 URL 後執行 &lt;strong&gt;Purge&lt;/strong&gt;。&lt;li&gt;如果網站啟用了 Hostinger CDN，到 &lt;strong&gt;Performance → CDN&lt;/strong&gt; 檢查 CDN 狀態，並依介面提供的選項清除快取。&lt;li&gt;用無痕視窗重新開啟首頁和剛更新的文章。&lt;/ol&gt;&lt;p&gt;不要一開始只清除自己的瀏覽器快取。如果所有訪客都看到舊版本，問題更可能在 Hostinger 快取、CDN 或部署檔案，而不是單一瀏覽器。&lt;h2 id=&quot;用-curl-驗收-response-headers&quot;&gt;用 &lt;code&gt;curl&lt;/code&gt; 驗收 response headers&lt;/h2&gt;&lt;p&gt;檢查首頁：&lt;pre class=&quot;language-bash&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;curl&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-I&lt;/span&gt; https://example.com/&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;檢查一個版本化資產：&lt;pre class=&quot;language-bash&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;curl&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-I&lt;/span&gt; https://example.com/assets/app.8f31c.css&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;你可以觀察 &lt;code&gt;Cache-Control&lt;/code&gt;、&lt;code&gt;Expires&lt;/code&gt;、&lt;code&gt;ETag&lt;/code&gt;、&lt;code&gt;Last-Modified&lt;/code&gt;、&lt;code&gt;Age&lt;/code&gt; 和 &lt;code&gt;Content-Type&lt;/code&gt;。&lt;p&gt;依本專案目前設定，首頁通常會看到三分鐘的 &lt;code&gt;max-age&lt;/code&gt;；符合資產副檔名規則的 CSS 則可能看到一年的 &lt;code&gt;max-age&lt;/code&gt;。Pagefind URL 則應該要求重新驗證：&lt;pre class=&quot;language-bash&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;curl&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-I&lt;/span&gt; https://example.com/pagefind/pagefind.js&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;如果重新請求時內容沒有改變，伺服器可能回傳 &lt;code&gt;304 Not Modified&lt;/code&gt;，代表條件式快取正常運作。若 &lt;code&gt;curl&lt;/code&gt; 已經拿到新內容，但瀏覽器仍是舊內容，再用無痕視窗、另一個網路和 DevTools Network 交叉確認。&lt;h2 id=&quot;快取設定決策表&quot;&gt;快取設定決策表&lt;/h2&gt;&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;你的情況&lt;th&gt;HTML&lt;th&gt;CSS／JS／圖片／字型&lt;th&gt;建議&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;文章常更新，檔名沒有版本號&lt;td&gt;&lt;code&gt;no-cache&lt;/code&gt; 或短 TTL&lt;td&gt;短 TTL，例如 5–30 分鐘&lt;td&gt;先確保更新穩定，再逐步延長&lt;tr&gt;&lt;td&gt;CSS／JS 有內容指紋&lt;td&gt;&lt;code&gt;no-cache&lt;/code&gt; 或一天&lt;td&gt;一年＋&lt;code&gt;immutable&lt;/code&gt;&lt;td&gt;適合穩定的 production 靜態站&lt;tr&gt;&lt;td&gt;正在除錯快取問題&lt;td&gt;&lt;code&gt;no-cache&lt;/code&gt;&lt;td&gt;&lt;code&gt;no-cache&lt;/code&gt; 或短 TTL&lt;td&gt;驗收完成後再恢復長效快取&lt;tr&gt;&lt;td&gt;Pagefind 搜尋索引會更新&lt;td&gt;短 TTL&lt;td&gt;Pagefind 使用 &lt;code&gt;no-cache&lt;/code&gt;；其他資產依版本策略&lt;td&gt;避免搜尋索引長時間未更新&lt;tr&gt;&lt;td&gt;CDN 剛啟用或剛清除&lt;td&gt;短 TTL&lt;td&gt;依 CDN 規則&lt;td&gt;等待節點更新，再用不同網路測試&lt;/table&gt;&lt;p&gt;這張表主要根據兩個問題決定快取時間：檔案多久會更新，以及檔案的 URL 是否會隨內容改變。&lt;ul&gt;&lt;li&gt;&lt;strong&gt;HTML 通常使用短 TTL&lt;/strong&gt;：首頁和文章內容可能經常更新，因此使用短時間快取，讓瀏覽器較快向伺服器確認。本專案目前設定為三分鐘；&lt;code&gt;no-cache&lt;/code&gt; 則是另一種要求使用前重新驗證的策略。&lt;li&gt;&lt;strong&gt;有內容指紋的靜態資產可以快取一年&lt;/strong&gt;：例如 &lt;code&gt;app.8f31c.css&lt;/code&gt;。CSS 或 JavaScript 更新後，檔名也會改變，瀏覽器會把它視為全新的檔案，因此適合搭配 &lt;code&gt;immutable&lt;/code&gt; 使用一年 TTL。&lt;li&gt;&lt;strong&gt;固定檔名的資產應使用短 TTL&lt;/strong&gt;：例如 &lt;code&gt;style.css&lt;/code&gt; 或 &lt;code&gt;app.js&lt;/code&gt;。如果內容更新但 URL 不變，長效快取可能讓訪客持續取得舊檔案；在尚未完成版本化時，可先使用 5–30 分鐘。&lt;li&gt;&lt;strong&gt;Pagefind 使用 &lt;code&gt;no-cache&lt;/code&gt;&lt;/strong&gt;：搜尋索引會隨文章更新，但 URL 通常不一定包含內容指紋，因此每次使用前都應重新驗證。&lt;li&gt;&lt;strong&gt;正在除錯時全部縮短 TTL&lt;/strong&gt;：先讓 HTML、CSS 和 JavaScript 使用 &lt;code&gt;no-cache&lt;/code&gt; 或短 TTL，確認部署與 response headers 正常後，再恢復長效快取。&lt;/ul&gt;&lt;p&gt;要注意的是，這張表是判斷原則，不代表目前 &lt;code&gt;.htaccess&lt;/code&gt; 已經自動檢查檔名是否含有內容指紋。目前的 &lt;code&gt;&amp;lt;FilesMatch&amp;gt;&lt;/code&gt; 是按照副檔名設定一年快取，所以固定名稱的 &lt;code&gt;style.css&lt;/code&gt; 或 &lt;code&gt;app.js&lt;/code&gt; 也可能被快取一年。只有在部署流程確實會讓資產 URL 隨內容改變時，才適合使用一年 TTL 和 &lt;code&gt;immutable&lt;/code&gt;。&lt;h2 id=&quot;常見問題&quot;&gt;常見問題&lt;/h2&gt;&lt;h3 id=&quot;重新建置後,網站為什麼還是舊頁面?&quot;&gt;重新建置後，網站為什麼還是舊頁面？&lt;/h3&gt;&lt;p&gt;先確認 &lt;code&gt;_site&lt;/code&gt; 裡真的有新內容，再確認上傳的是 &lt;code&gt;_site&lt;/code&gt; 內容，而不是原始 Markdown。接著依序檢查 Hostinger 快取、CDN 和瀏覽器，不要只重跑 11ty。&lt;h3 id=&quot;可以把所有檔案都設定成快取一年嗎?&quot;&gt;可以把所有檔案都設定成快取一年嗎？&lt;/h3&gt;&lt;p&gt;不建議。沒有版本化的 HTML、&lt;code&gt;style.css&lt;/code&gt; 或 &lt;code&gt;app.js&lt;/code&gt; 可能長時間讓訪客拿到舊內容。只有 URL 會隨內容改變的資產，才適合搭配 &lt;code&gt;immutable&lt;/code&gt; 使用一年 TTL。&lt;h3 id=&quot;為什麼-Pagefind-搜尋不到剛發布的文章?&quot;&gt;為什麼 Pagefind 搜尋不到剛發布的文章？&lt;/h3&gt;&lt;p&gt;可能是 Pagefind 索引本身沒有重新建置，也可能是 &lt;code&gt;/pagefind/&lt;/code&gt; 資產仍被舊快取提供。先確認部署輸出有最新索引，再用 &lt;code&gt;curl -I&lt;/code&gt; 檢查 Pagefind URL 是否真的要求重新驗證，最後清除 Hostinger 或 CDN 快取。&lt;h3 id=&quot;改-.htaccess-後網站出現-500-怎麼辦?&quot;&gt;改 &lt;code&gt;.htaccess&lt;/code&gt; 後網站出現 500 怎麼辦？&lt;/h3&gt;&lt;p&gt;先還原最後一次修改，確認 Hostinger 支援 &lt;code&gt;mod_headers&lt;/code&gt;、&lt;code&gt;mod_expires&lt;/code&gt; 和相關語法，再逐段加入設定。不要在未驗證前，直接把同一份 &lt;code&gt;.htaccess&lt;/code&gt; 複製到其他網站。&lt;h2 id=&quot;部署驗收工具&quot;&gt;部署驗收工具&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Hostinger hPanel&lt;/strong&gt;：用來清除網站快取、管理 Cache Manager，以及依方案管理 CDN。&lt;li&gt;&lt;strong&gt;&lt;code&gt;curl&lt;/code&gt;&lt;/strong&gt;：直接查看公開網站的 response headers，先確認伺服器實際回傳什麼，再判斷是不是瀏覽器問題。&lt;li&gt;&lt;strong&gt;Chrome DevTools Network&lt;/strong&gt;：查看 &lt;code&gt;Age&lt;/code&gt;、&lt;code&gt;ETag&lt;/code&gt;、&lt;code&gt;Cache-Control&lt;/code&gt; 和 response body，交叉確認不同資產是否使用預期的 TTL。&lt;/ul&gt;&lt;h2 id=&quot;結語先分層,再決定-TTL&quot;&gt;結語：先分層，再決定 TTL&lt;/h2&gt;&lt;p&gt;11ty 快取問題的重點不是把數字設得越大越好，而是讓不同檔案採用適合自己的更新策略：&lt;ol&gt;&lt;li&gt;HTML 使用短 TTL，保持容易更新。&lt;li&gt;版本化 CSS、JavaScript、圖片和字型使用長效快取。&lt;li&gt;Pagefind 搜尋資產使用 &lt;code&gt;no-cache&lt;/code&gt;，避免索引更新被卡住。&lt;li&gt;每次部署後用 hPanel、&lt;code&gt;curl&lt;/code&gt; 和無痕視窗驗收。&lt;/ol&gt;&lt;p&gt;完成設定後，下一步是到 Hostinger hPanel 清除快取，再用 &lt;code&gt;curl -I&lt;/code&gt; 確認公開網站真的回傳預期的 headers。快取驗收完，再來一杯咖啡；這次咖啡和 HTML 都應該是熱的。&lt;h2 id=&quot;參考資料&quot;&gt;參考資料&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://httpd.apache.org/docs/2.4/mod/mod_expires.html&quot;&gt;Apache &lt;code&gt;mod_expires&lt;/code&gt; 官方文件&lt;/a&gt;：說明 &lt;code&gt;ExpiresActive&lt;/code&gt;、&lt;code&gt;ExpiresDefault&lt;/code&gt; 和 &lt;code&gt;ExpiresByType&lt;/code&gt; 如何產生快取標頭。&lt;li&gt;&lt;a href=&quot;https://httpd.apache.org/docs/2.4/mod/mod_headers.html&quot;&gt;Apache &lt;code&gt;mod_headers&lt;/code&gt; 官方文件&lt;/a&gt;：查閱 &lt;code&gt;.htaccess&lt;/code&gt; 中設定與修改 response headers 的 &lt;code&gt;Header&lt;/code&gt; 指令。&lt;li&gt;&lt;a href=&quot;https://pagefind.app/docs/running-pagefind/&quot;&gt;Pagefind Running Pagefind&lt;/a&gt;：說明在靜態網站建置後產生 &lt;code&gt;pagefind/&lt;/code&gt; 搜尋資產的流程。&lt;li&gt;&lt;a href=&quot;https://www.hostinger.com/tutorials/what-is-website-caching&quot;&gt;Hostinger：Website caching explained&lt;/a&gt;：說明 hPanel Cache Manager、瀏覽器快取、伺服器快取與 CDN 的差異。&lt;/ul&gt;</content>
  </entry>
  <entry>
    <title>字型子集化：2026 中文網站如何減少字型載入、改善首屏速度</title>
    <link href="https://kamadiam.com/font-subsetting-reduce-font-loading/" />
    <updated>2026-08-03T07:45:00Z</updated>
    <id>https://kamadiam.com/font-subsetting-reduce-font-loading/</id>
    <content type="html">&lt;p&gt;繁體中文網站最容易被忽略的效能成本，常常不是第三方腳本，而是字型。完整 CJK 字型可能很大；若每位訪客都先下載整包，手機網路就得先端上一杯「等待咖啡」，文字才慢慢穩定下來。&lt;p&gt;這篇會帶你判斷是否真的需要 Web Font、理解字型子集化與 &lt;code&gt;unicode-range&lt;/code&gt; 的差別，最後用自架 WOFF2 的流程把載入成本降下來。重點不是追求某個神奇檔案大小，而是讓字型策略符合網站內容與實際測量結果。&lt;div class=&quot;table-of-contents&quot;&gt;&lt;h2 id=&quot;目錄&quot;&gt;目錄&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/font-subsetting-reduce-font-loading/#%E5%85%88%E5%88%A4%E6%96%B7%E4%BD%A0%E7%9C%9F%E7%9A%84%E9%9C%80%E8%A6%81-Web-Font-%E5%97%8E?&quot;&gt;先判斷：你真的需要 Web Font 嗎？&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/font-subsetting-reduce-font-loading/#%E4%B8%89%E7%A8%AE%E5%AD%97%E5%9E%8B%E7%AD%96%E7%95%A5%E6%80%8E%E9%BA%BC%E9%81%B8?&quot;&gt;三種字型策略怎麼選？&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/font-subsetting-reduce-font-loading/#%E7%82%BA%E4%BB%80%E9%BA%BC%E7%B9%81%E4%B8%AD%E5%AD%97%E5%9E%8B%E7%89%B9%E5%88%A5%E5%AE%B9%E6%98%93%E6%8B%96%E6%85%A2%E9%A6%96%E5%B1%8F?&quot;&gt;為什麼繁中字型特別容易拖慢首屏？&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/font-subsetting-reduce-font-loading/#%E5%AD%97%E5%9E%8B%E5%AD%90%E9%9B%86%E5%8C%96%E5%88%B0%E5%BA%95%E5%9C%A8%E5%81%9A%E4%BB%80%E9%BA%BC?&quot;&gt;字型子集化到底在做什麼？&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/font-subsetting-reduce-font-loading/#%E8%87%AA%E6%9E%B6%E5%AD%90%E9%9B%86%E5%8C%96%E7%9A%84%E5%AF%A6%E4%BD%9C%E6%B5%81%E7%A8%8B&quot;&gt;自架子集化的實作流程&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/font-subsetting-reduce-font-loading/#%E5%B8%B8%E8%A6%8B%E9%8C%AF%E8%AA%A4%E8%88%87%E5%8F%96%E6%8D%A8&quot;&gt;常見錯誤與取捨&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/font-subsetting-reduce-font-loading/#%E7%B5%90%E8%AA%9E%E5%85%88%E5%B0%91%E8%BC%89%E5%85%A5,%E5%86%8D%E8%BF%BD%E6%B1%82%E4%B8%80%E8%87%B4&quot;&gt;結語：先少載入，再追求一致&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/font-subsetting-reduce-font-loading/#%E5%8F%83%E8%80%83%E8%B3%87%E6%96%99&quot;&gt;參考資料&lt;/a&gt;&lt;/ul&gt;&lt;/div&gt;&lt;h2 id=&quot;先判斷你真的需要-Web-Font-嗎?&quot;&gt;先判斷：你真的需要 Web Font 嗎？&lt;/h2&gt;&lt;p&gt;如果網站是內容型部落格、管理後台或文件站，最划算的起點通常是&lt;strong&gt;系統字體優先&lt;/strong&gt;。系統字體不需要額外下載，少了一趟網路請求，也少了一個可能延遲文字顯示的變數：&lt;pre class=&quot;language-css&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;body&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;font-family&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
		system-ui&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; -apple-system&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; BlinkMacSystemFont&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Segoe UI&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Roboto&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;token string&quot;&gt;&quot;PingFang TC&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Microsoft JhengHei&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Noto Sans CJK TC&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; sans-serif&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;token string&quot;&gt;&quot;Apple Color Emoji&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Segoe UI Emoji&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;macOS 與 iOS 常見的是 &lt;code&gt;PingFang TC&lt;/code&gt;，Windows 常見的是 &lt;code&gt;Microsoft JhengHei&lt;/code&gt;；實際結果仍會受作業系統、已安裝字型與瀏覽器影響。如果你不需要每台裝置都呈現完全相同的品牌字形，這一步可能比任何字型工具更有效。&lt;h2 id=&quot;三種字型策略怎麼選?&quot;&gt;三種字型策略怎麼選？&lt;/h2&gt;&lt;p&gt;可以先用下面的方式縮小選擇：&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;情境&lt;th&gt;建議做法&lt;th&gt;主要取捨&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;內容型部落格&lt;td&gt;內文使用系統字體&lt;td&gt;載入成本低，但不同平台字形不完全一致&lt;tr&gt;&lt;td&gt;品牌官網或 Landing Page&lt;td&gt;標題使用子集化 Web Font，內文使用系統字體&lt;td&gt;兼顧美觀與速度，但要維護字元清單&lt;tr&gt;&lt;td&gt;必須固定品牌字形的產品 UI&lt;td&gt;自架 WOFF2，搭配靜態或分片子集化&lt;td&gt;控制力最高，也需要加入 build 流程&lt;/table&gt;&lt;p&gt;不要只因為「看起來專業」，就讓整篇內文載入完整的 Noto Sans TC。先把 Web Font 限制在 H1、H2 或少量 UI 元件，通常更容易取得可接受的速度與一致性。字型不是越多越有文化，請求數才不會因此變成詩。&lt;h2 id=&quot;為什麼繁中字型特別容易拖慢首屏?&quot;&gt;為什麼繁中字型特別容易拖慢首屏？&lt;/h2&gt;&lt;p&gt;中文字型需要涵蓋大量漢字、標點與不同字重，因此完整檔案可能遠大於 Latin 字型。一個網站若同時載入多個 CJK 字型族和多個字重，下載量很快就會累積；若使用外部服務，還要考慮連線、CSS 發現時機與字型檔本身的請求。&lt;p&gt;外部 CDN 不一定比較慢，自架也不保證比較快。實際結果會受到 CDN 位置、HTTP 版本、快取策略、CSS 何時被瀏覽器發現，以及頁面到底匹配到哪些字型分片影響。跨網站共用快取也不能當成必然優勢，所以應該把「外部服務」和「自架」都放進同一套實測裡比較。&lt;h2 id=&quot;字型子集化到底在做什麼?&quot;&gt;字型子集化到底在做什麼？&lt;/h2&gt;&lt;p&gt;子集化就是從完整字型中只保留網站需要的字元，再輸出較小的字型檔。HarfBuzz 的 &lt;code&gt;hb-subset&lt;/code&gt; 文件也把它描述為縮減字型涵蓋的 codepoint，並移除不再需要的資料。&lt;p&gt;常見做法有兩種：&lt;ol&gt;&lt;li&gt;&lt;strong&gt;靜態子集化&lt;/strong&gt;：掃描指定頁面或整個網站，只保留目前用到的字元。檔案通常更小，適合內容固定的 Landing Page；但內容更新後必須重跑。&lt;li&gt;&lt;strong&gt;Unicode 分片&lt;/strong&gt;：把字型切成多個檔案，在 CSS 的每組 &lt;code&gt;@font-face&lt;/code&gt; 中用 &lt;code&gt;unicode-range&lt;/code&gt; 指定負責的字元範圍。瀏覽器會根據頁面文字判斷要不要載入某個分片，但匹配後下載的是整個分片，不是單獨一個字。&lt;/ol&gt;&lt;p&gt;這裡有個容易誤會的地方：&lt;strong&gt;WOFF2 格式和瀏覽器不會自動替你切檔&lt;/strong&gt;。分片需要多組 &lt;code&gt;@font-face&lt;/code&gt;、多個 WOFF2 網址，以及對應的 &lt;code&gt;unicode-range&lt;/code&gt;。只有一個完整 WOFF2 和一條 &lt;code&gt;@font-face&lt;/code&gt;，瀏覽器就只會把它當成一個檔案處理。&lt;p&gt;Google Fonts 會替你產生包含 &lt;code&gt;unicode-range&lt;/code&gt; 的 CSS，某些字型也會依字元或語系切成多個檔案；你只放入 stylesheet 連結，就能使用它預先準備好的分片。自架則要自行產生分片與 CSS，例如使用 &lt;a href=&quot;https://github.com/KonghaYao/cn-font-split/&quot;&gt;&lt;code&gt;cn-font-split&lt;/code&gt;&lt;/a&gt;。&lt;p&gt;本站曾以四種字型載入約 23 個 WOFF2 請求、總計約 1.78MB 作為改版前觀測值；後來改用自架子集，並讓內文回到系統字體，目前實際使用的 Web Font 減少到兩個檔案。這是本站某次測量，不是所有網站都會得到相同數字；分片數量減少，也不代表每個頁面一定少下載同樣多的位元組。&lt;p&gt;子集缺少的字也不會自動變成缺字方框。瀏覽器會沿著 &lt;code&gt;font-family&lt;/code&gt; 堆疊尋找下一個可用字型，代價是罕見字可能和周圍文字有些風格差異。因此，子集化完成後要測試特殊人名、專有名詞、全形標點與程式碼符號。&lt;h2 id=&quot;自架子集化的實作流程&quot;&gt;自架子集化的實作流程&lt;/h2&gt;&lt;h3 id=&quot;1.-收集真正會出現的字元&quot;&gt;1. 收集真正會出現的字元&lt;/h3&gt;&lt;p&gt;掃描文章、模板、導覽列、按鈕和錯誤訊息，收集漢字、全形標點、Latin 字母、數字與常用符號。不要只掃一篇文章，否則下一篇新增一個罕見字時，字型就會突然「失憶」。&lt;p&gt;Latin 部分可以參考字型服務提供的範圍，但仍要依網站實際內容確認，避免英文、貨幣符號或程式碼標點意外 fallback。若標題和內文使用不同字型，也應分別收集，避免把所有文章文字都塞進只套用於標題的字型。&lt;h3 id=&quot;2.-用工具產出-WOFF2&quot;&gt;2. 用工具產出 WOFF2&lt;/h3&gt;&lt;p&gt;&lt;code&gt;cn-font-split&lt;/code&gt; 適合把 CJK 字型切成可按範圍載入的分片；如果想依網站實際用字產出單一子集，可以使用 &lt;a href=&quot;https://github.com/nickbalestra/subset-font&quot;&gt;&lt;code&gt;subset-font&lt;/code&gt;&lt;/a&gt;。這個 Node.js 套件以 HarfBuzz 的 subset 功能為基礎，會輸出指定格式的字型檔。&lt;p&gt;概念流程如下：&lt;pre class=&quot;language-text&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;掃描 Markdown、模板與導覽文字
            ↓
收集 Latin、CJK 與符號
            ↓
subset-font 或 cn-font-split
            ↓
輸出 WOFF2 與必要的 CSS
            ↓
用瀏覽器與效能工具驗證&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;如果專案已經有腳本，讓它成為固定的 build 步驟會比手動操作可靠。例如本站可在新增內容後執行：&lt;pre class=&quot;language-shell&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;pnpm&lt;/span&gt; run subset-fonts
&lt;span class=&quot;token function&quot;&gt;pnpm&lt;/span&gt; run build&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;3.-用-@font-face-控制載入行為&quot;&gt;3. 用 &lt;code&gt;@font-face&lt;/code&gt; 控制載入行為&lt;/h3&gt;&lt;p&gt;最基本的自架設定可以長這樣：&lt;pre class=&quot;language-css&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@font-face&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;font-family&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Noto Sans TC&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token url&quot;&gt;&lt;span class=&quot;token function&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string url&quot;&gt;&quot;/fonts/noto-sans-tc.woff2&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;woff2&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;font-weight&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 400 700&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;font-style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; normal&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;font-display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; swap&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;接著在 HTML 中把它套用到真正需要的元素，而不是只宣告卻全站使用：&lt;pre class=&quot;language-html&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h1&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;hero-title&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;網站標題&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;font-display: swap&lt;/code&gt; 會先顯示 fallback 字型，字型載入完成後再替換，適合需要優先看見文字的標題。&lt;code&gt;optional&lt;/code&gt; 則讓瀏覽器在載入不及時時更傾向維持 fallback；它可以降低字型替換造成版面變化的機會，但不是「完全不會 CLS」的保證。實際顯示時間仍由瀏覽器實作與網路狀況決定。&lt;h3 id=&quot;4.-把子集化接進-build-流程&quot;&gt;4. 把子集化接進 build 流程&lt;/h3&gt;&lt;p&gt;新增文章或修改導覽列後，重新執行字型腳本，再進行 production build。原始字型可以放在本機快取，避免每次重跑都重新下載；真正重要的是讓「新增內容 → 重新產生子集 → 測量效能」成為固定流程。&lt;h3 id=&quot;5.-移除不再需要的-CDN-標籤&quot;&gt;5. 移除不再需要的 CDN 標籤&lt;/h3&gt;&lt;p&gt;改成自架後，檢查 &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; 裡的 Google Fonts &lt;code&gt;preconnect&lt;/code&gt;、&lt;code&gt;preload&lt;/code&gt;、stylesheet 和 &lt;code&gt;noscript&lt;/code&gt; 標籤。新舊兩套設定同時留下來，等於請瀏覽器喝兩杯咖啡再做同一件事——熱鬧，但沒有比較快。&lt;h2 id=&quot;常見錯誤與取捨&quot;&gt;常見錯誤與取捨&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;直接上傳 OTF 或 TTF&lt;/strong&gt;：Web Font 通常優先使用 WOFF2，因為壓縮效率較好且現代瀏覽器支援廣；仍要依你的瀏覽器支援範圍決定是否提供其他格式。&lt;li&gt;&lt;strong&gt;以為分片等於只下載一個字&lt;/strong&gt;：&lt;code&gt;unicode-range&lt;/code&gt; 只負責判斷某個分片是否匹配；匹配後通常會下載整個分片。&lt;li&gt;&lt;strong&gt;只留下漢字、忘了 Latin 和符號&lt;/strong&gt;：英文、數字、括號或程式碼符號可能改用不同字型，應把實際需要的字元一起納入。&lt;li&gt;&lt;strong&gt;把整個網站都套品牌字體&lt;/strong&gt;：先從標題或少量 UI 開始，否則字型下載量和替換風險都會快速增加。&lt;li&gt;&lt;strong&gt;忽略子集缺字&lt;/strong&gt;：fallback 能避免缺字方框，卻不會讓不同字型的字形風格自動一致；上線前要測試罕見字、專有名詞與標點。&lt;li&gt;&lt;strong&gt;只看檔案大小、不測實際體驗&lt;/strong&gt;：請用 &lt;a href=&quot;https://pagespeed.web.dev/&quot;&gt;PageSpeed Insights&lt;/a&gt; 或自己的效能驗收流程，觀察 LCP、CLS、文字是否及時出現，以及 Network 面板實際下載了哪些字型。&lt;/ul&gt;&lt;h2 id=&quot;結語先少載入,再追求一致&quot;&gt;結語：先少載入，再追求一致&lt;/h2&gt;&lt;p&gt;字型最佳化可以先記住三件事：&lt;ol&gt;&lt;li&gt;內容站先用系統字體，不要預設整包載入 CJK Web Font。&lt;li&gt;需要品牌字形時，只把標題或 UI 子集化，並依需求選擇單一子集或 Unicode 分片。&lt;li&gt;用 &lt;code&gt;font-display&lt;/code&gt;、fallback 和實際 Core Web Vitals 測量確認取捨，不要只看理論檔案大小。&lt;/ol&gt;&lt;p&gt;下一步很簡單：先記下改動前的數據，挑一個頁面做子集化，再比較 LCP、CLS、字型請求數和實際文字顯示。先測一頁就好，不必一口氣把整個網站的字型 pipeline 變成考古現場。測完再來一杯，這次是慶功。&lt;h2 id=&quot;參考資料&quot;&gt;參考資料&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/At-rules/%40font-face&quot;&gt;MDN：&lt;code&gt;@font-face&lt;/code&gt;&lt;/a&gt;：查閱 &lt;code&gt;src&lt;/code&gt;、&lt;code&gt;font-display&lt;/code&gt;、&lt;code&gt;unicode-range&lt;/code&gt; 與 Web Font 格式。&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/Text_styling/Web_fonts&quot;&gt;MDN：Web fonts&lt;/a&gt;：說明 WOFF2、fallback、子集化與字型實際載入時機。&lt;li&gt;&lt;a href=&quot;https://developers.google.com/fonts/docs/css2&quot;&gt;Google Fonts CSS API&lt;/a&gt;：說明 Google Fonts CSS、&lt;code&gt;text&lt;/code&gt; 參數與字型請求格式。&lt;li&gt;&lt;a href=&quot;https://harfbuzz.github.io/harfbuzz-hb-subset.html&quot;&gt;HarfBuzz：&lt;code&gt;hb-subset&lt;/code&gt;&lt;/a&gt;：說明字型子集化如何縮減 codepoint 涵蓋範圍與不必要資料。&lt;li&gt;&lt;a href=&quot;https://github.com/nickbalestra/subset-font&quot;&gt;&lt;code&gt;subset-font&lt;/code&gt; GitHub&lt;/a&gt;：Node.js 字型子集化套件的原始碼與使用資訊。&lt;li&gt;&lt;a href=&quot;https://github.com/KonghaYao/cn-font-split/&quot;&gt;&lt;code&gt;cn-font-split&lt;/code&gt; GitHub&lt;/a&gt;：CJK 字型分片工具的功能與文件入口。&lt;/ul&gt;</content>
  </entry>
  <entry>
    <title>LCP 優化實戰：首屏別 lazy-load，用 fetchpriority 把關鍵資源拉上來</title>
    <link href="https://kamadiam.com/lcp-optimization-no-lazy-fetchpriority/" />
    <updated>2026-08-03T06:45:00Z</updated>
    <id>https://kamadiam.com/lcp-optimization-no-lazy-fetchpriority/</id>
    <content type="html">&lt;p&gt;PageSpeed 分數掉下來，你第一個想到的通常是「圖太大了」。有時候沒錯，但也可能是你把主視覺圖設成 &lt;code&gt;loading=&amp;quot;lazy&amp;quot;&lt;/code&gt;，等於請瀏覽器「晚點再叫主角上台」——LCP 當然跟著延後，比咖啡涼掉還難受。&lt;p&gt;LCP（Largest Contentful Paint）衡量的是：初次載入時，視窗內最大的文字、圖片或其他內容元素，何時完成繪製。這篇會帶你找出真正的 LCP 元素，分清楚 &lt;code&gt;lazy&lt;/code&gt;、&lt;code&gt;fetchpriority&lt;/code&gt; 與 &lt;code&gt;preload&lt;/code&gt; 各自解決什麼問題，再用一套可以實際驗收的流程收尾。看完，你不必對著瀑布圖施法，也能知道下一杯咖啡該泡給誰。&lt;div class=&quot;table-of-contents&quot;&gt;&lt;h2 id=&quot;目錄&quot;&gt;目錄&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/lcp-optimization-no-lazy-fetchpriority/#%E5%85%88%E6%8A%93%E5%88%B0%E4%BD%A0%E7%9C%9F%E6%AD%A3%E7%9A%84-LCP-%E5%85%83%E7%B4%A0&quot;&gt;先抓到你真正的 LCP 元素&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/lcp-optimization-no-lazy-fetchpriority/#%E6%B1%BA%E7%AD%96%E8%A1%A8lazyfetchprioritypreload-%E6%80%8E%E9%BA%BC%E9%81%B8?&quot;&gt;決策表：lazy、fetchpriority、preload 怎麼選？&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/lcp-optimization-no-lazy-fetchpriority/#LCP-%E5%84%AA%E5%8C%96%E7%AD%96%E7%95%A5%E6%A0%B8%E5%BF%83%E5%84%AA%E5%85%88%E7%B4%9A-+-%E4%B8%8D%E8%A2%AB-lazy&quot;&gt;LCP 優化策略（核心：優先級 + 不被 lazy）&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/lcp-optimization-no-lazy-fetchpriority/#%E4%B8%80%E5%80%8B%E5%BF%AB%E9%80%9F%E9%A9%97%E6%94%B6%E5%BF%83%E6%B3%95%E5%85%88-CLS-%E7%A9%A9,%E5%86%8D%E8%AB%87-LCP&quot;&gt;一個快速驗收心法：先 CLS 穩，再談 LCP&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/lcp-optimization-no-lazy-fetchpriority/#%E6%A1%86%E6%9E%B6%E5%BF%AB%E9%80%9F%E5%B0%8D%E7%85%A7&quot;&gt;框架快速對照&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/lcp-optimization-no-lazy-fetchpriority/#%E5%BF%AB%E9%80%9F%E9%A9%97%E6%94%B6%E6%B8%85%E5%96%AE&quot;&gt;快速驗收清單&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/lcp-optimization-no-lazy-fetchpriority/#%E7%B5%90%E8%AA%9ELCP-%E5%84%AA%E5%8C%96%E6%98%AF%E3%80%8C%E4%B8%8D%E8%A6%81%E6%8B%96%E9%97%9C%E9%8D%B5%E8%B3%87%E6%BA%90%E3%80%8D%E7%9A%84%E5%B7%A5%E7%A8%8B%E7%B4%80%E5%BE%8B&quot;&gt;結語：LCP 優化是「不要拖關鍵資源」的工程紀律&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/lcp-optimization-no-lazy-fetchpriority/#%E5%8F%83%E8%80%83%E8%B3%87%E6%96%99&quot;&gt;參考資料&lt;/a&gt;&lt;/ul&gt;&lt;/div&gt;&lt;h2 id=&quot;先抓到你真正的-LCP-元素&quot;&gt;先抓到你真正的 LCP 元素&lt;/h2&gt;&lt;p&gt;先不要猜「應該是主視覺圖」。LCP 也可能是 H1、文章開頭的大段文字，或其他在初次視窗內佔面積最大的元素。流程如下：&lt;ol&gt;&lt;li&gt;開 &lt;a href=&quot;https://pagespeed.web.dev/&quot;&gt;PageSpeed Insights&lt;/a&gt; 或 Chrome DevTools → &lt;strong&gt;Performance&lt;/strong&gt; 錄一次載入&lt;li&gt;看報告標出的 &lt;strong&gt;LCP 元素&lt;/strong&gt; 是什麼（圖片、文字區塊或 H1）&lt;li&gt;記下它的 URL 或 DOM 節點，後面改碼才有對象&lt;/ol&gt;&lt;p&gt;在 DevTools 的 Performance 面板中，也可以從 LCP 標記查看元素與時間。先確認「誰是 LCP」，再決定要不要調圖片；不然很容易把不是主角的圖片打扮成 VIP。&lt;p&gt;靜態網站常見的 LCP 來源包括&lt;strong&gt;主視覺圖&lt;/strong&gt;、&lt;strong&gt;H1 標題&lt;/strong&gt;與&lt;strong&gt;產品截圖&lt;/strong&gt;。以真實使用者資料的評估方式來看，LCP 在第 75 百分位&lt;strong&gt;不超過 2.5 秒&lt;/strong&gt;才算「良好」；這和 Lighthouse 的單次實驗室測試不是同一件事。想先搞懂欄位資料與實驗室資料的差異，可看站內 &lt;a href=&quot;https://kamadiam.com/pagespeed-core-web-vitals/&quot;&gt;PageSpeed Insights 與 Core Web Vitals 2026&lt;/a&gt;。&lt;h2 id=&quot;決策表lazyfetchprioritypreload-怎麼選?&quot;&gt;決策表：lazy、fetchpriority、preload 怎麼選？&lt;/h2&gt;&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;情境&lt;th&gt;建議做法&lt;th&gt;為什麼&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;確認是首屏內的 LCP 圖片&lt;td&gt;不使用 &lt;code&gt;loading=&amp;quot;lazy&amp;quot;&lt;/code&gt;；必要時加 &lt;code&gt;fetchpriority=&amp;quot;high&amp;quot;&lt;/code&gt;&lt;td&gt;讓瀏覽器及早發現並下載關鍵圖片&lt;tr&gt;&lt;td&gt;LCP 是 CSS 背景圖，或資源要等 JS 才出現&lt;td&gt;&lt;code&gt;&amp;lt;link rel=&amp;quot;preload&amp;quot; as=&amp;quot;image&amp;quot; fetchpriority=&amp;quot;high&amp;quot;&amp;gt;&lt;/code&gt;&lt;td&gt;瀏覽器不容易從初始 HTML 直接發現它，需主動提示&lt;tr&gt;&lt;td&gt;內文圖、頁尾圖、側欄縮圖&lt;td&gt;&lt;code&gt;loading=&amp;quot;lazy&amp;quot;&lt;/code&gt;&lt;td&gt;讓頻寬先留給首屏內容&lt;tr&gt;&lt;td&gt;頁面有一、兩張真正關鍵的圖片&lt;td&gt;只對那些圖片使用 &lt;code&gt;fetchpriority=&amp;quot;high&amp;quot;&lt;/code&gt;&lt;td&gt;高優先級用太多，反而失去區分效果&lt;tr&gt;&lt;td&gt;本站的 Eleventy Transform 自動補上 &lt;code&gt;lazy&lt;/code&gt;&lt;td&gt;在主視覺圖上明確覆寫 &lt;code&gt;loading=&amp;quot;eager&amp;quot;&lt;/code&gt;&lt;td&gt;內文圖的預設值不一定適合 LCP 圖&lt;/table&gt;&lt;p&gt;&lt;strong&gt;一句話心法&lt;/strong&gt;：首屏 LCP 圖不要 lazy；確認是關鍵資源後再提高優先級；其餘圖片放心 lazy。&lt;h2 id=&quot;LCP-優化策略核心優先級-+-不被-lazy&quot;&gt;LCP 優化策略（核心：優先級 + 不被 lazy）&lt;/h2&gt;&lt;h3 id=&quot;1.-不要-lazy-load-LCP&quot;&gt;1. 不要 lazy-load LCP&lt;/h3&gt;&lt;p&gt;最常見的壞操作，就是把首屏 LCP 圖片加上 &lt;code&gt;loading=&amp;quot;lazy&amp;quot;&lt;/code&gt;。瀏覽器通常要先完成版面計算，確認圖片接近視窗後才請求它；原本可以從 HTML 先發現的資源，就被迫排隊。下載、解碼、繪製每一步都往後，LCP 只好一起等。&lt;pre class=&quot;language-html&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- ❌ 主視覺圖被延後載入 --&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/img/hero.webp&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;loading&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;lazy&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;主視覺&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- ✅ 首屏主角：不 lazy，並提示它很重要 --&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/img/hero.webp&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;主視覺&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;1200&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;630&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;loading&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;eager&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;fetchpriority&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;high&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;eager&lt;/code&gt; 是明確表達「不要額外延後」；它不等於魔法加速器，真正的下載順序仍由瀏覽器決定。&lt;code&gt;fetchpriority=&amp;quot;high&amp;quot;&lt;/code&gt; 也是提示，不是命令。最後，別忘了補 &lt;code&gt;width&lt;/code&gt;／&lt;code&gt;height&lt;/code&gt; 預留版面，LCP 和 CLS 常常是同一張圖一起修。細節見站內 &lt;a href=&quot;https://kamadiam.com/image-size-cls-preallocation/&quot;&gt;圖片最佳化與 CLS 解決方案&lt;/a&gt;。&lt;h3 id=&quot;2.-用-fetchpriority-把關鍵資源拉到前面&quot;&gt;2. 用 fetchpriority 把關鍵資源拉到前面&lt;/h3&gt;&lt;p&gt;當 LCP 是圖片，而且瀏覽器發現它太晚時，方向是：&lt;ul&gt;&lt;li&gt;主視覺圖：加 &lt;code&gt;fetchpriority=&amp;quot;high&amp;quot;&lt;/code&gt;&lt;li&gt;CSS 背景圖或 JS 稍後插入的圖片：在 &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; 視情況用 &lt;code&gt;preload&lt;/code&gt; 宣告&lt;/ul&gt;&lt;pre class=&quot;language-html&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- CSS 背景 LCP：在 head 主動預載 --&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;preload&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;fetchpriority&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;high&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;image&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/img/hero.webp&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;image/webp&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- HTML 直接宣告的 LCP 圖 --&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/img/hero.webp&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;fetchpriority&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;high&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;網站主視覺 Banner&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;1200&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;630&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;preload&lt;/code&gt; 解決的是「讓瀏覽器更早發現資源」；&lt;code&gt;fetchpriority&lt;/code&gt; 解決的是「在已發現的資源中，提示相對重要性」。兩者可以搭配，但不是每張圖片都要一起上。&lt;code&gt;fetchpriority=&amp;quot;high&amp;quot;&lt;/code&gt; 請只給真正可能成為 LCP、而且確實需要提早下載的資源；大家都當 VIP，最後就沒有 VIP。&lt;h3 id=&quot;3.-Eleventy-靜態站管道預設-lazy-的陷阱&quot;&gt;3. Eleventy 靜態站：管道預設 lazy 的陷阱&lt;/h3&gt;&lt;p&gt;在本站，HTML Transform 會替沒有明確設定的圖片補上 &lt;code&gt;loading=&amp;quot;lazy&amp;quot;&lt;/code&gt;。這對文章中段的圖片通常合理，卻不一定適合首屏 LCP 圖。遇到這種情況，要在那張圖片上明確覆寫：&lt;pre class=&quot;language-html&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/img/hero.png&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;產品主視覺&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;loading&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;eager&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;fetchpriority&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;high&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;sizes&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;(max-width: 800px) 100vw, 1200px&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;不要只看 HTML 屬性，也要在 build 後檢查實際輸出的 HTML，確認 &lt;code&gt;loading&lt;/code&gt;、&lt;code&gt;fetchpriority&lt;/code&gt;、&lt;code&gt;srcset&lt;/code&gt; 與 &lt;code&gt;sizes&lt;/code&gt; 都符合預期。接著檢查圖片的實際下載大小、尺寸與格式：手機只需要幾百像素寬，就別把幾千像素的原圖整包寄過去。WebP 或 AVIF 可以是選項，但最後仍要以實際檔案大小、畫質和瀏覽器支援情況驗收。&lt;p&gt;如果圖片是由 JavaScript 動態插入，瀏覽器會更晚才知道它存在。能直接寫進初始 HTML 就直接寫；真的必須動態產生時，至少確認它不是因為程式執行太晚才錯過下載時機：&lt;pre class=&quot;language-js&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; hero &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;.hero-image&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

hero&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;src &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/img/hero.webp&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
hero&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;fetchPriority &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;high&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;這段程式只是在 HTML 已經有圖片元素的前提下補上來源與提示；它不會讓 JavaScript 變成 preload。若資源本來就能在 &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; 預先宣告，優先考慮 HTML 的 &lt;code&gt;preload&lt;/code&gt;。&lt;h3 id=&quot;4.-避免-render-blocking-讓首屏後面卡住&quot;&gt;4. 避免 render-blocking 讓首屏後面卡住&lt;/h3&gt;&lt;p&gt;如果非關鍵 JS／CSS 形成很長的依賴鏈，LCP 圖片就算早早下載完成，也可能等不到繪製。快速檢查：&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;資源類型&lt;th&gt;建議&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;非關鍵 JavaScript&lt;td&gt;依載入需求考慮 &lt;code&gt;defer&lt;/code&gt; 或 &lt;code&gt;async&lt;/code&gt;；第三方標籤可在主要內容穩定後再載入&lt;tr&gt;&lt;td&gt;CSS&lt;td&gt;避免不必要的 &lt;code&gt;@import&lt;/code&gt; 串接；確認 CSS 沒有讓首屏繪製卡在長依賴鏈上&lt;tr&gt;&lt;td&gt;Web 字體&lt;td&gt;使用 &lt;code&gt;font-display: swap&lt;/code&gt;；只有確定是關鍵字體時才考慮 preload&lt;/table&gt;&lt;p&gt;GTM 延遲載入的具體寫法，可參考站內 &lt;a href=&quot;https://kamadiam.com/gtm-defer-requestidlecallback-psi-optimization/&quot;&gt;GTM 延遲載入：用 requestIdleCallback 降低 PSI 的 TBT&lt;/a&gt;。&lt;h2 id=&quot;一個快速驗收心法先-CLS-穩,再談-LCP&quot;&gt;一個快速驗收心法：先 CLS 穩，再談 LCP&lt;/h2&gt;&lt;p&gt;LCP 是「什麼時候畫出來」，CLS 是「版面有沒有抖」。兩者不是同一個指標，但同一張圖片可能同時影響兩者：沒有尺寸時會推動版面，有錯誤載入策略時又會晚到。&lt;p&gt;建議順序：&lt;ol&gt;&lt;li&gt;&lt;strong&gt;先確保圖片／嵌入不會讓版面在載入期抖動（CLS 預留）&lt;/strong&gt;&lt;li&gt;再回頭把 LCP 圖片的優先級拉到位&lt;li&gt;最後用 GSC → 體驗 → Core Web Vitals 對&lt;strong&gt;欄位資料&lt;/strong&gt;驗收；報告反映的是一段期間的真實使用者資料，不會改完立刻更新&lt;/ol&gt;&lt;h2 id=&quot;框架快速對照&quot;&gt;框架快速對照&lt;/h2&gt;&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;框架&lt;th&gt;圖片管道&lt;th&gt;LCP 注意事項&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Hugo&lt;/strong&gt;&lt;td&gt;&lt;code&gt;resources.Get&lt;/code&gt; + &lt;code&gt;Resize&lt;/code&gt;／&lt;code&gt;webp&lt;/code&gt;&lt;td&gt;主視覺圖模板別加 lazy&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Eleventy&lt;/strong&gt;&lt;td&gt;&lt;code&gt;@11ty/eleventy-img&lt;/code&gt;&lt;td&gt;Transform 預設 lazy，主視覺圖覆寫 eager + fetchpriority&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Astro&lt;/strong&gt;&lt;td&gt;&lt;code&gt;astro:assets&lt;/code&gt; 或 &lt;code&gt;&amp;lt;Image /&amp;gt;&lt;/code&gt;&lt;td&gt;依實際輸出與圖片位置確認 loading 設定，LCP 圖別盲目 lazy&lt;/table&gt;&lt;h2 id=&quot;快速驗收清單&quot;&gt;快速驗收清單&lt;/h2&gt;&lt;p&gt;每次修改後，按這個順序檢查：&lt;ol&gt;&lt;li&gt;PSI 或 DevTools 是否指出同一個 LCP 元素？&lt;li&gt;LCP 圖是否在初始 HTML 中可被發現？若是背景圖，是否真的需要 &lt;code&gt;preload&lt;/code&gt;？&lt;li&gt;是否誤用了 &lt;code&gt;loading=&amp;quot;lazy&amp;quot;&lt;/code&gt;？&lt;code&gt;fetchpriority=&amp;quot;high&amp;quot;&lt;/code&gt; 是否只用在少數關鍵資源？&lt;li&gt;build 後的 HTML、Network 瀑布圖與實際圖片大小是否符合預期？&lt;li&gt;CLS 的尺寸預留是否仍然存在？&lt;/ol&gt;&lt;h2 id=&quot;結語LCP-優化是「不要拖關鍵資源」的工程紀律&quot;&gt;結語：LCP 優化是「不要拖關鍵資源」的工程紀律&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;用 PSI／DevTools 找出真正的 LCP 元素，別憑感覺改&lt;li&gt;首屏 LCP 圖不要 &lt;code&gt;loading=&amp;quot;lazy&amp;quot;&lt;/code&gt;；確認需要時再加 &lt;code&gt;fetchpriority=&amp;quot;high&amp;quot;&lt;/code&gt;&lt;li&gt;CSS 背景圖等難以被初始 HTML 發現的資源，才考慮 &lt;code&gt;preload&lt;/code&gt;&lt;li&gt;Eleventy 管道若自動補上 lazy，主視覺圖要個別覆寫&lt;li&gt;圖片下載完成不代表已經繪製；也要排查 CSS、字體與 JavaScript 的依賴鏈&lt;/ul&gt;&lt;p&gt;先改一個真正的瓶頸，再重新測量；不要一次把整個 &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; 塞滿 preload，否則瀏覽器也會開始懷疑人生。等資料慢慢累積，再回 GSC 看真實使用者的趨勢。改完記得再來一杯，這次是慶祝主角準時登場的那杯。&lt;h2 id=&quot;參考資料&quot;&gt;參考資料&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://web.dev/articles/optimize-lcp&quot;&gt;web.dev：Optimize Largest Contentful Paint&lt;/a&gt; — LCP 元素、資源發現、lazy loading、fetch priority 與 preload 的整體優化方法。&lt;li&gt;&lt;a href=&quot;https://web.dev/articles/fetch-priority&quot;&gt;web.dev：Optimize resource loading with the Fetch Priority API&lt;/a&gt; — 說明 &lt;code&gt;fetchpriority&lt;/code&gt; 如何影響資源優先級，以及它和 preload 的搭配方式。&lt;li&gt;&lt;a href=&quot;https://web.dev/articles/browser-level-image-lazy-loading&quot;&gt;web.dev：Browser-level image lazy loading for the web&lt;/a&gt; — 說明 &lt;code&gt;loading=&amp;quot;lazy&amp;quot;&lt;/code&gt; 與 &lt;code&gt;loading=&amp;quot;eager&amp;quot;&lt;/code&gt; 的載入行為和適用情境。&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/img&quot;&gt;MDN：&lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; HTML image element&lt;/a&gt; — 查閱圖片元素的 &lt;code&gt;loading&lt;/code&gt;、&lt;code&gt;fetchpriority&lt;/code&gt;、尺寸與響應式圖片屬性。&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Attributes/fetchpriority&quot;&gt;MDN：fetchpriority HTML attribute&lt;/a&gt; — 查閱 &lt;code&gt;fetchpriority&lt;/code&gt; 的用途、限制與應該節制使用的原因。&lt;/ul&gt;</content>
  </entry>
  <entry>
    <title>GTM 延遲載入：用 requestIdleCallback 降低 PSI 的 TBT</title>
    <link href="https://kamadiam.com/gtm-defer-requestidlecallback-psi-optimization/" />
    <updated>2026-08-03T06:00:00Z</updated>
    <id>https://kamadiam.com/gtm-defer-requestidlecallback-psi-optimization/</id>
    <content type="html">&lt;p&gt;GTM 一掛上去，PageSpeed Insights（PSI）分數就往下掉？先別急著把它整個拔掉。很多時候，問題不是「有沒有 GTM」，而是它在首屏最忙的時候也跑進來搶頻寬、搶主執行緒——大家都還沒坐好，它已經開始開會了。&lt;p&gt;這篇會帶你把 GTM 從「頁面一開就啟動」改成「使用者互動或瀏覽器有空時才啟動」，再用同一套流程檢查 LCP、TBT、INP，以及轉換事件有沒有被延遲到漏掉。讀完你會知道這種延遲適合什麼情境、程式碼怎麼寫，以及為什麼「分數變綠」不等於資料一定完整。簡單說，就是先讓瀏覽器把早餐吃完，再請 GTM 進來開會。&lt;div class=&quot;table-of-contents&quot;&gt;&lt;h2 id=&quot;目錄&quot;&gt;目錄&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/gtm-defer-requestidlecallback-psi-optimization/#%E7%82%BA%E4%BB%80%E9%BA%BC-GTM-%E5%8F%AF%E8%83%BD%E6%8B%96%E6%85%A2-PSI?&quot;&gt;為什麼 GTM 可能拖慢 PSI？&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/gtm-defer-requestidlecallback-psi-optimization/#%E6%A0%B8%E5%BF%83%E5%81%9A%E6%B3%95%E4%BA%92%E5%8B%95%E8%A7%B8%E7%99%BC%E5%8A%A0-idle-fallback&quot;&gt;核心做法：互動觸發加 idle fallback&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/gtm-defer-requestidlecallback-psi-optimization/#%E5%8F%AF%E7%9B%B4%E6%8E%A5%E8%A4%87%E8%A3%BD%E7%9A%84%E6%9C%80%E5%B0%8F%E7%89%88%E6%9C%AC&quot;&gt;可直接複製的最小版本&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/gtm-defer-requestidlecallback-psi-optimization/#timeout-%E8%A6%81%E8%A8%AD%E5%A4%9A%E5%B0%91?&quot;&gt;timeout 要設多少？&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/gtm-defer-requestidlecallback-psi-optimization/#dataLayer-%E6%9C%83%E4%BF%9D%E7%95%99%E4%BA%8B%E4%BB%B6,%E4%BD%86%E4%B8%8D%E6%98%AF%E8%B3%87%E6%96%99%E4%BF%9D%E9%9A%AA%E7%AE%B1&quot;&gt;dataLayer 會保留事件，但不是資料保險箱&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/gtm-defer-requestidlecallback-psi-optimization/#%E5%BB%B6%E9%81%B2%E5%89%8D%E5%85%88%E5%81%9A%E7%9A%84%E5%85%A9%E4%BB%B6%E4%BA%8B&quot;&gt;延遲前先做的兩件事&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/gtm-defer-requestidlecallback-psi-optimization/#%E6%80%8E%E9%BA%BC%E9%A9%97%E6%94%B6%E4%B8%8D%E8%A6%81%E5%8F%AA%E7%9C%8B%E4%B8%80%E6%AC%A1-PSI-%E5%88%86%E6%95%B8&quot;&gt;怎麼驗收：不要只看一次 PSI 分數&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/gtm-defer-requestidlecallback-psi-optimization/#%E6%8E%A8%E8%96%A6%E5%B7%A5%E5%85%B7/%E8%B3%87%E6%BA%90&quot;&gt;推薦工具／資源&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/gtm-defer-requestidlecallback-psi-optimization/#%E7%B5%90%E8%AA%9E%E5%85%88%E8%AE%93%E4%B8%BB%E5%9F%B7%E8%A1%8C%E7%B7%92%E5%96%98%E5%8F%A3%E6%B0%A3&quot;&gt;結語：先讓主執行緒喘口氣&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/gtm-defer-requestidlecallback-psi-optimization/#%E5%8F%83%E8%80%83%E8%B3%87%E6%96%99&quot;&gt;參考資料&lt;/a&gt;&lt;/ul&gt;&lt;/div&gt;&lt;h2 id=&quot;為什麼-GTM-可能拖慢-PSI?&quot;&gt;為什麼 GTM 可能拖慢 PSI？&lt;/h2&gt;&lt;p&gt;GTM 常見的影響大致分兩條線：&lt;ol&gt;&lt;li&gt;&lt;strong&gt;網路頻寬競爭&lt;/strong&gt;：&lt;code&gt;gtm.js&lt;/code&gt; 下載時，可能和 hero 圖片、CSS 等首屏資源競爭，讓 LCP 延後。實際影響仍取決於網路、資源優先順序與容器內容，不能只看檔名就宣稱一定變慢。&lt;li&gt;&lt;strong&gt;主執行緒工作增加&lt;/strong&gt;：容器啟動後，GA4、Pixel、Hotjar 等標籤可能執行 JavaScript。標籤太多或太重，就可能製造長任務，推高 TBT，也讓互動回饋變慢。&lt;/ol&gt;&lt;p&gt;延遲載入可以保護首屏，但它不是免費午餐：越晚啟動，越可能漏掉頁面剛開啟時的分析或轉換事件。因此要一起看效能和資料完整度。&lt;h2 id=&quot;核心做法互動觸發加-idle-fallback&quot;&gt;核心做法：互動觸發加 idle fallback&lt;/h2&gt;&lt;p&gt;可以讓以下兩條路都呼叫同一個 &lt;code&gt;boot()&lt;/code&gt;：&lt;ul&gt;&lt;li&gt;使用者真的開始使用頁面，例如滾動、按鍵或觸控。&lt;li&gt;使用者沒有互動時，等主執行緒進入 idle；如果瀏覽器不支援，就退回 &lt;code&gt;setTimeout&lt;/code&gt;。&lt;/ul&gt;&lt;p&gt;重點是用 &lt;code&gt;started&lt;/code&gt; flag 去重。&lt;code&gt;once: true&lt;/code&gt; 只會移除某一個事件的 listener，不能取消已排程的 idle callback，也不能保證兩條路不會同時進入函式。所以要在 &lt;code&gt;boot()&lt;/code&gt; 裡設防，並在互動搶先發生時取消排程。&lt;h2 id=&quot;可直接複製的最小版本&quot;&gt;可直接複製的最小版本&lt;/h2&gt;&lt;p&gt;把 &lt;code&gt;GTM-XXXXXXX&lt;/code&gt; 換成自己的 container ID。這段程式會先建立 &lt;code&gt;dataLayer&lt;/code&gt;，再在互動或 idle 時只載入一次 GTM。它是「延遲載入的起點」，不是可以直接取代所有 consent、轉換與 noscript 設計的萬用片段。&lt;pre class=&quot;language-javascript&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;w&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; d&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; started &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; idleId &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; timeoutId &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; events &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;pointerdown&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;keydown&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;touchstart&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;scroll&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  w&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dataLayer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; w&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dataLayer &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;boot&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;started&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    started &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;idleId &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; w&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cancelIdleCallback&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      w&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;cancelIdleCallback&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;idleId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;timeoutId &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      w&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;clearTimeout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;timeoutId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    w&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dataLayer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token string-property property&quot;&gt;&#39;gtm.start&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Date&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;gtm.js&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; script &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; d&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createElement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;script&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    script&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;async &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    script&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;src &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;https://www.googletagmanager.com/gtm.js?id=GTM-XXXXXXX&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    d&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;head&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;appendChild&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;script&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  events&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    d&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;type&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; boot&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;once&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;passive&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;requestIdleCallback&#39;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; w&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    idleId &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; w&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;requestIdleCallback&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;boot&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;timeout&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2000&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    timeoutId &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; w&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setTimeout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;boot&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;window&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;requestIdleCallback&lt;/code&gt; 的 &lt;code&gt;timeout&lt;/code&gt; 不是精準鬧鐘，也不是「GTM 完成載入」的期限。瀏覽器會先等 idle period；若期限到了還沒執行，才把 callback 排進 event loop。callback 開始執行後，還要下載、解析並執行 GTM 與容器裡的 tag。&lt;p&gt;另外，&lt;code&gt;requestIdleCallback()&lt;/code&gt; 目前不是所有瀏覽器都一致支援，所以用 feature detection 比寫死「某某瀏覽器不支援」安全。上面的 fallback 只處理 API 不存在的情況；若網站還有 CSP、consent mode 或特殊 WebView 限制，仍要在目標環境測試。&lt;h2 id=&quot;timeout-要設多少?&quot;&gt;timeout 要設多少？&lt;/h2&gt;&lt;p&gt;真正要平衡的是「首屏保護」和「資料多早開始收集」。下面的數字只是第一輪 A/B 實驗的起點，不是 Google 或 GTM 規定的標準；沒有一個數字適合所有網站，先用實測建立基準比較可靠。&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;timeout&lt;th&gt;優點&lt;th&gt;風險&lt;th&gt;適合情境&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;1500–2500ms&lt;td&gt;適合先做第一輪實驗&lt;td&gt;早期 pageview 或轉換可能延後&lt;td&gt;一般內容站、非關鍵分析&lt;tr&gt;&lt;td&gt;1000ms 以下&lt;td&gt;較早啟動，資料風險較小&lt;td&gt;首屏和 TBT 的保護變弱&lt;td&gt;頁面很輕、轉換追蹤較重要&lt;tr&gt;&lt;td&gt;3000ms 以上&lt;td&gt;首屏保護較久&lt;td&gt;秒退與早期轉換漏算更多&lt;td&gt;只有實測支持時才用&lt;/table&gt;&lt;p&gt;如果登入、註冊或購買事件不能漏，不要把所有 tag 都綁在延遲初始化上。可以保留關鍵事件的即時路徑，只延後非必要的分析和行銷 tag。若有同意管理平台，也要依使用者同意狀態決定哪些 tag 能啟動；效能優化不能偷偷繞過隱私規則。&lt;h2 id=&quot;dataLayer-會保留事件,但不是資料保險箱&quot;&gt;dataLayer 會保留事件，但不是資料保險箱&lt;/h2&gt;&lt;p&gt;GTM 載入前先建立陣列，並把資料與 &lt;code&gt;event&lt;/code&gt; 放在同一個 object：&lt;pre class=&quot;language-javascript&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dataLayer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dataLayer &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dataLayer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;signup_complete&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;plan&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;free&#39;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;這能保留「目前頁面、GTM 載入前已成功 push」的事件，GTM 載入後會依序處理 queue。但要記得三個界線：&lt;ul&gt;&lt;li&gt;&lt;code&gt;dataLayer&lt;/code&gt; 不會自動跨頁保存；下一頁要重新初始化和 push。&lt;li&gt;queue 被處理，不代表 tag 的網路請求已經送達。&lt;li&gt;使用者秒退或立即導向時，下載、執行或 beacon 仍可能被中斷。&lt;/ul&gt;&lt;p&gt;因此，GTM Preview 或 Tag Assistant 顯示「有 push」只是第一關，還要到分析或廣告平台的測試工具確認事件真的送出。&lt;h2 id=&quot;延遲前先做的兩件事&quot;&gt;延遲前先做的兩件事&lt;/h2&gt;&lt;h3 id=&quot;先精簡-GTM-container&quot;&gt;先精簡 GTM container&lt;/h3&gt;&lt;p&gt;每隔一段時間檢查 container：&lt;ul&gt;&lt;li&gt;刪掉過期活動和不再使用的第三方 tag。&lt;li&gt;把不需要每頁執行的 tag 改成 Window Loaded 或自訂事件。&lt;li&gt;減少會反覆掃描 DOM 的變數和觸發條件。&lt;/ul&gt;&lt;p&gt;如果只是把一堆不必要的 tag 延後，主執行緒只是晚點忙，並沒有真正變輕。&lt;h3 id=&quot;高階方案再考慮-Worker&quot;&gt;高階方案再考慮 Worker&lt;/h3&gt;&lt;p&gt;Partytown 等方案可以把部分第三方腳本移到 Web Worker，但需要額外整合，且依賴 DOM 或同步行為的 tag 可能不相容。它比較適合已經完成 tag 審計、仍有明確長任務問題的技術型網站，不是第一個該按的按鈕。先把不用的 tag 刪掉，通常比替整個倉庫搬家更划算。&lt;h2 id=&quot;怎麼驗收不要只看一次-PSI-分數&quot;&gt;怎麼驗收：不要只看一次 PSI 分數&lt;/h2&gt;&lt;p&gt;用下面流程比較立即載入版和延遲版：&lt;ol&gt;&lt;li&gt;固定同一個 URL、GTM container、裝置策略和 cookie／consent 狀態。&lt;li&gt;PSI 兩個版本都跑多次，記錄 LCP、TBT、CLS、FCP，不要用單次分數宣稱因果。PSI 的 lab 結果會受測試條件影響，分數不是精密秤。&lt;li&gt;Lighthouse Navigation mode 看首屏；Timespan mode 則用來觀察互動後載入 GTM 的影響。&lt;li&gt;在 Performance trace 和 Network 面板確認 &lt;code&gt;gtm.js&lt;/code&gt;、tag 長任務與請求發生時間。&lt;li&gt;用 GTM Preview／Tag Assistant 檢查 queue、Custom Event trigger、consent 和轉換 tag。&lt;li&gt;以真實流量的 CrUX 或 PSI field data 觀察 INP。TBT 是 lab 指標，可作為載入期間主執行緒壅塞的線索，但不能直接當成真實 INP。&lt;/ol&gt;&lt;p&gt;如果首屏改善了，但註冊或購買事件明顯少掉，這不是成功的優化；它只是把問題從 PSI 搬到分析資料裡。&lt;h2 id=&quot;推薦工具/資源&quot;&gt;推薦工具／資源&lt;/h2&gt;&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;需求&lt;th&gt;工具／做法&lt;th&gt;適合誰&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;首屏與 lab 指標比較&lt;td&gt;PageSpeed Insights、Chrome Lighthouse&lt;td&gt;想確認 LCP、TBT 是否改善的站長&lt;tr&gt;&lt;td&gt;真實互動體驗&lt;td&gt;CrUX／PSI field data&lt;td&gt;有足夠流量、要看 INP 的網站&lt;tr&gt;&lt;td&gt;Tag 行為驗證&lt;td&gt;GTM Preview、Tag Assistant&lt;td&gt;不能接受事件悄悄漏掉的網站&lt;tr&gt;&lt;td&gt;整理效能與 SEO 驗收&lt;td&gt;&lt;a href=&quot;https://kamadiam.com/pagespeed-core-web-vitals/&quot;&gt;SEO 與 Core Web Vitals 檢查流程&lt;/a&gt;&lt;td&gt;想把單次修正變成固定流程的站長&lt;/table&gt;&lt;h2 id=&quot;結語先讓主執行緒喘口氣&quot;&gt;結語：先讓主執行緒喘口氣&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;GTM 不一定要刪，先把它改成互動觸發加 idle fallback。&lt;li&gt;&lt;code&gt;timeout&lt;/code&gt; 越小，資料越早開始；越大，首屏保護越強，但早期事件風險也越高。&lt;li&gt;&lt;code&gt;dataLayer&lt;/code&gt; 能排隊目前頁面的事件，不能保證跨頁保存或請求送達。&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;下一步&lt;/strong&gt;：先用 2000ms 做基準，將立即載入版與延遲版各跑多次，同時核對轉換事件，再依 LCP、TBT、INP 和資料完整度調整。修完再喝一口咖啡，這次讓瀏覽器先喝；它如果還是忙，就代表該審的是 tag，不是咖啡師。&lt;h2 id=&quot;參考資料&quot;&gt;參考資料&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://support.google.com/tagmanager/answer/6164391&quot;&gt;Google Tag Manager：The data layer&lt;/a&gt;：說明 &lt;code&gt;dataLayer&lt;/code&gt; 的建立位置、&lt;code&gt;push()&lt;/code&gt; 與容器程式碼的關係。&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback&quot;&gt;MDN：Window.requestIdleCallback()&lt;/a&gt;：說明 idle callback、&lt;code&gt;timeout&lt;/code&gt; 與瀏覽器支援限制。&lt;li&gt;&lt;a href=&quot;https://web.dev/articles/tbt&quot;&gt;web.dev：Total Blocking Time（TBT）&lt;/a&gt;：說明 TBT 的計算方式，以及它和 INP 的差異。&lt;li&gt;&lt;a href=&quot;https://web.dev/articles/inp&quot;&gt;web.dev：Interaction to Next Paint（INP）&lt;/a&gt;：說明 INP 應以實地資料觀察，及 lab 指標的限制。&lt;li&gt;&lt;a href=&quot;https://developer.chrome.com/docs/devtools/lighthouse&quot;&gt;Chrome for Developers：Lighthouse&lt;/a&gt;：說明 Navigation、Timespan 與 Snapshot 模式的使用情境。&lt;/ul&gt;</content>
  </entry>
  <entry>
    <title>圖片最佳化與 CLS 解決方案：用 width、height 預防網頁版面跳動</title>
    <link href="https://kamadiam.com/image-size-cls-preallocation/" />
    <updated>2026-08-03T03:15:00Z</updated>
    <id>https://kamadiam.com/image-size-cls-preallocation/</id>
    <content type="html">&lt;p&gt;你有沒有遇過這種情況：正準備點連結，圖片突然載入，整段文字往下一跳。手指點到旁邊的空白，只能默念：「很好，咖啡也跟著位移了。」&lt;p&gt;這不是單純的「圖片太大」問題。圖片還沒載入時，如果瀏覽器不知道它要佔多少空間，後面的內容就可能被推開，形成 &lt;strong&gt;CLS（Cumulative Layout Shift，累積版面配置位移）&lt;/strong&gt;。另一方面，圖片若大到離譜，又會拖慢下載與解碼，影響 LCP。&lt;p&gt;這篇會用白話整理三件事：如何替圖片預留空間、如何讓下載尺寸符合實際顯示寬度，以及 Eleventy Image Transform 該怎麼接上工作流程。讀完後，你應該能判斷什麼時候寫 &lt;code&gt;width&lt;/code&gt;／&lt;code&gt;height&lt;/code&gt;，什麼時候使用 &lt;code&gt;aspect-ratio&lt;/code&gt;，也能避開幾個很常見的圖片效能坑。&lt;div class=&quot;table-of-contents&quot;&gt;&lt;h2 id=&quot;目錄&quot;&gt;目錄&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/image-size-cls-preallocation/#CLS-%E5%88%B0%E5%BA%95%E5%9C%A8%E9%87%8F%E4%BB%80%E9%BA%BC?&quot;&gt;CLS 到底在量什麼？&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/image-size-cls-preallocation/#%E7%AC%AC%E4%B8%80%E6%8B%9B%E5%9C%A8-HTML-%E5%AF%AB%E4%B8%8A%E7%9C%9F%E5%AF%A6%E5%AF%AC%E9%AB%98&quot;&gt;第一招：在 HTML 寫上真實寬高&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/image-size-cls-preallocation/#%E7%AC%AC%E4%BA%8C%E6%8B%9B%E9%9C%80%E8%A6%81%E5%9B%BA%E5%AE%9A%E6%A1%86%E6%9E%B6%E6%99%82%E4%BD%BF%E7%94%A8-aspect-ratio&quot;&gt;第二招：需要固定框架時使用 aspect-ratio&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/image-size-cls-preallocation/#%E7%AC%AC%E4%B8%89%E6%8B%9B%E5%9C%96%E7%89%87%E5%B0%BA%E5%AF%B8%E8%A6%81%E9%85%8D%E5%90%88%E9%A1%AF%E7%A4%BA%E5%AF%AC%E5%BA%A6&quot;&gt;第三招：圖片尺寸要配合顯示寬度&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/image-size-cls-preallocation/#Eleventy-Image-Transform%E6%8A%8A%E9%87%8D%E8%A4%87%E5%B7%A5%E4%BD%9C%E4%BA%A4%E7%B5%A6%E5%BB%BA%E7%BD%AE%E6%B5%81%E7%A8%8B&quot;&gt;Eleventy Image Transform：把重複工作交給建置流程&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/image-size-cls-preallocation/#%E4%B8%80%E6%AC%A1%E5%AE%8C%E6%88%90%E7%9A%84%E6%AA%A2%E6%9F%A5%E6%B8%85%E5%96%AE&quot;&gt;一次完成的檢查清單&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/image-size-cls-preallocation/#%E7%B5%90%E8%AA%9E%E5%85%88%E9%A0%90%E7%95%99,%E5%86%8D%E7%98%A6%E8%BA%AB&quot;&gt;結語：先預留，再瘦身&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/image-size-cls-preallocation/#%E5%8F%83%E8%80%83%E8%B3%87%E6%96%99&quot;&gt;參考資料&lt;/a&gt;&lt;/ul&gt;&lt;/div&gt;&lt;h2 id=&quot;CLS-到底在量什麼?&quot;&gt;CLS 到底在量什麼？&lt;/h2&gt;&lt;p&gt;CLS 衡量的是頁面生命週期中，&lt;strong&gt;非預期的版面位移&lt;/strong&gt;。例如文章文字先排好，圖片載入後才把文字往下推；這種跳動會讓讀者誤點按鈕，也會讓人覺得網站在偷偷喝醉。&lt;p&gt;Google 的 Core Web Vitals 建議值是：CLS 不高於 0.1、LCP 不超過 2.5 秒，都是以實際使用者資料的第 75 百分位作為判斷基準。這不代表每一台裝置都會得到同樣結果，所以本機 Lighthouse 分數很漂亮，也不等於真實訪客完全沒有問題。&lt;p&gt;先把範圍切清楚：本文專注圖片造成的位移。字型交換、廣告、Cookie 橫幅與第三方嵌入，也可能造成 CLS，但應該分開檢查，否則修到最後會像在咖啡渣裡找 bug。&lt;h2 id=&quot;第一招在-HTML-寫上真實寬高&quot;&gt;第一招：在 HTML 寫上真實寬高&lt;/h2&gt;&lt;p&gt;對一般 &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt;，最穩妥的做法是提供圖片檔案的實際像素尺寸：&lt;pre class=&quot;language-html&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt;
	&lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/img/product-dashboard.webp&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;token attr-name&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;1200&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;token attr-name&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;675&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;產品控制台介面截圖&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;token attr-name&quot;&gt;loading&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;lazy&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;token attr-name&quot;&gt;decoding&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;async&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;這裡的 &lt;code&gt;width&lt;/code&gt; 與 &lt;code&gt;height&lt;/code&gt; 是圖片的內在尺寸，不是你希望它在畫面上永遠顯示的 CSS 寬度。瀏覽器可以先用這兩個數值算出比例，在圖片下載完成前預留大致正確的高度。&lt;p&gt;接著用 CSS 讓它能縮小，但不要被拉伸：&lt;pre class=&quot;language-css&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;img&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;max-width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 100%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; auto&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;max-width: 100%&lt;/code&gt; 只限制圖片不要超出容器；&lt;code&gt;height: auto&lt;/code&gt; 則依照原本比例計算高度。這兩行解決的是響應式顯示，HTML 寬高屬性解決的是載入前的尺寸提示，兩者是隊友，不是二選一。&lt;h3 id=&quot;寬高比例填錯,仍然會跳&quot;&gt;寬高比例填錯，仍然會跳&lt;/h3&gt;&lt;p&gt;如果圖片實際是 1600 × 900，卻寫成 1600 × 1200，瀏覽器會先預留錯誤的高度，圖片載入後再校正。這仍可能產生位移，因此請從原檔或產圖工具確認比例，不要靠目測——螢幕上的圖片很會偽裝。&lt;p&gt;Lazy loading 圖片也一樣需要寬高。它本來就可能等到接近視窗才下載；若此時才突然取得高度，讀者正在看的內容就會被推走。&lt;h2 id=&quot;第二招需要固定框架時使用-aspect-ratio&quot;&gt;第二招：需要固定框架時使用 &lt;code&gt;aspect-ratio&lt;/code&gt;&lt;/h2&gt;&lt;p&gt;&lt;code&gt;aspect-ratio&lt;/code&gt; 適合用在圖片比例不固定、但設計需要統一視覺框的情境，也很適合 iframe、影片或其他沒有圖片內在比例的嵌入內容。&lt;pre class=&quot;language-html&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;figure&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;screenshot&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt;
		&lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/img/compare-a.webp&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
		&lt;span class=&quot;token attr-name&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;1600&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
		&lt;span class=&quot;token attr-name&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;900&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
		&lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;方案 A 設定畫面&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
		&lt;span class=&quot;token attr-name&quot;&gt;loading&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;lazy&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
		&lt;span class=&quot;token attr-name&quot;&gt;decoding&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;async&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;figure&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class=&quot;language-css&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.screenshot&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;aspect-ratio&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 16 / 9&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 100%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;overflow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; hidden&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #f2f2f2&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;.screenshot img&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 100%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 100%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;object-fit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; cover&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;object-fit: cover&lt;/code&gt; 會讓圖片填滿框架，必要時裁掉邊緣。如果內容不能被裁切，就改用 &lt;code&gt;contain&lt;/code&gt;，或保留 &lt;code&gt;height: auto&lt;/code&gt; 讓圖片完整顯示。&lt;p&gt;一般內容圖片仍建議保留正確的 HTML 寬高；&lt;code&gt;aspect-ratio&lt;/code&gt; 是補充工具，不是把每張圖的實際比例都忘掉的通行證。&lt;h2 id=&quot;第三招圖片尺寸要配合顯示寬度&quot;&gt;第三招：圖片尺寸要配合顯示寬度&lt;/h2&gt;&lt;p&gt;一張在手機上只顯示 360px 寬的圖片，如果每次都下載 4000px 原圖，瀏覽器不只要多下載，也要多解碼。做法不是無限降低品質，而是先產生符合版面需求的尺寸，再用 &lt;code&gt;srcset&lt;/code&gt; 和 &lt;code&gt;sizes&lt;/code&gt; 讓瀏覽器選擇候選檔案。&lt;pre class=&quot;language-html&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt;
	&lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/img/guide-800.webp&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;token attr-name&quot;&gt;srcset&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;
		/img/guide-400.webp 400w,
		/img/guide-800.webp 800w,
		/img/guide-1200.webp 1200w
	&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;token attr-name&quot;&gt;sizes&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;(max-width: 768px) 100vw, 720px&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;token attr-name&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;1200&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;token attr-name&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;675&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;設定步驟示意&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;token attr-name&quot;&gt;loading&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;lazy&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;token attr-name&quot;&gt;decoding&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;async&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;srcset&lt;/code&gt; 列出可用的圖片候選；&lt;code&gt;sizes&lt;/code&gt; 描述圖片在目前版面大約會顯示多寬。瀏覽器會綜合視窗寬度與裝置像素比例，選出合適的檔案。&lt;code&gt;sizes&lt;/code&gt; 寫成 &lt;code&gt;100vw&lt;/code&gt; 但圖片其實只佔內容欄，可能就會下載過大的檔案。&lt;p&gt;首屏真正的主圖不要一律設定 &lt;code&gt;loading=&amp;quot;lazy&amp;quot;&lt;/code&gt;。它若是 LCP 候選，應讓瀏覽器正常優先取得；是否加上 &lt;code&gt;fetchpriority=&amp;quot;high&amp;quot;&lt;/code&gt;，則要看實際載入瀑布流與頁面結構，別把「高優先」當成圖片界的萬用咖啡因。&lt;h3 id=&quot;格式與壓縮怎麼選?&quot;&gt;格式與壓縮怎麼選？&lt;/h3&gt;&lt;p&gt;沒有一個適用所有圖片的固定 KB 門檻。照片、介面截圖、透明 Logo 的可接受品質不同，應該在實際裝置與網路條件下比較畫質、檔案大小和 LCP。&lt;p&gt;可以遵循這個順序：&lt;ol&gt;&lt;li&gt;先裁掉不需要的畫面，並縮到合理的輸出寬度。&lt;li&gt;依瀏覽器支援與網站流程選擇 AVIF、WebP 或原格式作為 fallback。&lt;li&gt;UI 截圖放大檢查文字；壓縮不是越狠越專業。&lt;li&gt;用 PageSpeed Insights 或瀏覽器 Performance 面板確認實際結果。&lt;/ol&gt;&lt;p&gt;像 &lt;a href=&quot;https://squoosh.app/&quot;&gt;Squoosh&lt;/a&gt; 這類工具適合手動比較不同格式與品質，但工具只負責產檔，&lt;code&gt;width&lt;/code&gt; 和 &lt;code&gt;height&lt;/code&gt; 仍要回到 HTML 補好。&lt;h2 id=&quot;Eleventy-Image-Transform把重複工作交給建置流程&quot;&gt;Eleventy Image Transform：把重複工作交給建置流程&lt;/h2&gt;&lt;p&gt;圖片不多時，手動處理沒有問題；部落格持續更新後，漏掉寬高或忘記產縮圖就很容易發生。Eleventy 官方的 &lt;code&gt;@11ty/eleventy-img&lt;/code&gt; Image HTML Transform 可以在建置時產生多種尺寸與格式，並輸出含有寬高和 &lt;code&gt;srcset&lt;/code&gt; 的標記。&lt;p&gt;概念設定如下：&lt;pre class=&quot;language-js&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; eleventyImageTransformPlugin &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;@11ty/eleventy-img&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;eleventyConfig&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	eleventyConfig&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addPlugin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;eleventyImageTransformPlugin&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token literal-property property&quot;&gt;formats&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;webp&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;auto&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;token literal-property property&quot;&gt;widths&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;400&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;800&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1200&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;實際使用多個寬度時，請為輸出的 &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; 提供符合版面的 &lt;code&gt;sizes&lt;/code&gt;。沒有 &lt;code&gt;sizes&lt;/code&gt;，瀏覽器可能無法正確判斷候選圖片適合的顯示寬度，最佳化就只完成一半。&lt;p&gt;Eleventy 也可能替圖片加上 &lt;code&gt;loading=&amp;quot;lazy&amp;quot;&lt;/code&gt;。這對文章下方的圖片通常合理，但首屏主圖要個別覆寫，並在建置後檢查產出的 HTML，而不是只相信設定檔看起來很漂亮。&lt;p&gt;建議導入順序很簡單：先讓所有圖片有正確比例，再讓建置流程產生合適尺寸，最後用真實頁面檢查 CLS 與 LCP。先把地板畫好，再搬沙發；不然每次圖片載入，都像重新裝潢一次。&lt;h2 id=&quot;一次完成的檢查清單&quot;&gt;一次完成的檢查清單&lt;/h2&gt;&lt;p&gt;改完圖片後，可以照這個順序驗收：&lt;ul&gt;&lt;li&gt;搜尋所有 &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt;，確認有 &lt;code&gt;alt&lt;/code&gt;，並檢查 &lt;code&gt;width&lt;/code&gt;／&lt;code&gt;height&lt;/code&gt; 是否符合原圖比例。&lt;li&gt;確認響應式 CSS 沒有用兩個固定尺寸把圖片拉伸；一般圖片通常搭配 &lt;code&gt;max-width: 100%&lt;/code&gt; 與 &lt;code&gt;height: auto&lt;/code&gt;。&lt;li&gt;圖片若需要統一框架，確認容器的 &lt;code&gt;aspect-ratio&lt;/code&gt; 和 &lt;code&gt;object-fit&lt;/code&gt; 符合內容需求。&lt;li&gt;檢查 &lt;code&gt;srcset&lt;/code&gt; 的候選寬度與 &lt;code&gt;sizes&lt;/code&gt; 是否符合實際版面。&lt;li&gt;確認首屏主圖沒有被不必要的 lazy loading 延後。&lt;li&gt;用 PageSpeed Insights、Chrome DevTools Performance 或 Search Console 的 Core Web Vitals 資料觀察結果。&lt;/ul&gt;&lt;p&gt;CLS 修好不代表整站就會飛起來；伺服器回應、字型、JavaScript 和第三方服務仍可能影響體驗。不過圖片先把尺寸說清楚，至少不會一邊載入、一邊把讀者的手指送去錯的按鈕。&lt;h2 id=&quot;結語先預留,再瘦身&quot;&gt;結語：先預留，再瘦身&lt;/h2&gt;&lt;p&gt;圖片最佳化可以記成兩句話：&lt;strong&gt;先用 HTML 寬高或 CSS 比例預留空間，再讓檔案尺寸符合實際顯示寬度。&lt;/strong&gt; 前者守住 CLS，後者幫助下載與解碼；兩邊一起做，才不是只把問題從「會跳」換成「等很久」。&lt;p&gt;先挑一篇文章，補齊圖片寬高並檢查產出的 &lt;code&gt;srcset&lt;/code&gt;。做完再跑一次效能檢查，然後放心喝咖啡——這次咖啡杯的位置，應該不會因為圖片載入而改變。&lt;h2 id=&quot;參考資料&quot;&gt;參考資料&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://web.dev/articles/optimize-cls&quot;&gt;web.dev：Optimize Cumulative Layout Shift&lt;/a&gt;：說明圖片尺寸、&lt;code&gt;aspect-ratio&lt;/code&gt; 與預留版面空間如何降低 CLS。&lt;li&gt;&lt;a href=&quot;https://web.dev/articles/vitals&quot;&gt;web.dev：Web Vitals&lt;/a&gt;：Core Web Vitals 與 CLS、LCP 建議值及評估方式。&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/img&quot;&gt;MDN：&lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; HTML image element&lt;/a&gt;：查閱 &lt;code&gt;width&lt;/code&gt;、&lt;code&gt;height&lt;/code&gt;、&lt;code&gt;loading&lt;/code&gt;、&lt;code&gt;srcset&lt;/code&gt; 與 &lt;code&gt;sizes&lt;/code&gt; 的行為。&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Guides/Responsive_images&quot;&gt;MDN：Using responsive images in HTML&lt;/a&gt;：說明解析度切換、&lt;code&gt;srcset&lt;/code&gt; 和 &lt;code&gt;sizes&lt;/code&gt; 的使用方式。&lt;li&gt;&lt;a href=&quot;https://www.11ty.dev/docs/plugins/image/&quot;&gt;Eleventy：Image&lt;/a&gt;：官方 Image Transform 設定、輸出格式、尺寸與 &lt;code&gt;sizes&lt;/code&gt; 注意事項。&lt;/ul&gt;</content>
  </entry>
  <entry>
    <title>Proton VPN 值得買嗎？免費版與付費版功能完整解析</title>
    <link href="https://kamadiam.com/proton-vpn-free-vs-paid-review/" />
    <updated>2026-08-02T13:25:00Z</updated>
    <id>https://kamadiam.com/proton-vpn-free-vs-paid-review/</id>
    <content type="html">&lt;p&gt;你是不是也曾經在 App Store 搜尋「免費 VPN」，下載一個評分不錯的 App，結果隔沒多久就看到新聞說某款免費 VPN 洩露使用者資料？免費不一定有問題，但「免費」兩個字也不會自動替 App 做資安審查。這就像免費咖啡：先問清楚誰付了豆子的錢，心裡比較踏實。&lt;p&gt;Proton VPN 是少數提供免費方案、而且把隱私保護當成產品核心的 VPN 服務。這篇文章會用實際使用情境比較 Free 與 VPN Plus：你會知道免費版能做什麼、付費版到底多了哪些功能，以及價格為什麼不適合用一個固定數字概括。讀完後，你應該能依自己的需求做選擇，而不是被「買最貴才最安全」的焦慮推著走。&lt;div class=&quot;table-of-contents&quot;&gt;&lt;h2 id=&quot;目錄&quot;&gt;目錄&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/proton-vpn-free-vs-paid-review/#%E7%82%BA%E4%BB%80%E9%BA%BC%E5%A4%A7%E5%A4%9A%E6%95%B8%E3%80%8C%E5%85%8D%E8%B2%BB-VPN%E3%80%8D%E5%BE%88%E5%8D%B1%E9%9A%AA?&quot;&gt;為什麼大多數「免費 VPN」很危險？&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/proton-vpn-free-vs-paid-review/#Proton-VPN-%E5%85%8D%E8%B2%BB%E7%89%88%E5%B8%82%E5%A0%B4%E4%B8%8A%E7%9A%84%E7%95%B0%E9%A1%9E&quot;&gt;Proton VPN 免費版：市場上的異類&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/proton-vpn-free-vs-paid-review/#%E5%85%8D%E8%B2%BB%E7%89%88-vs-%E4%BB%98%E8%B2%BB%E7%89%88Plus%E5%AE%8C%E6%95%B4%E5%8A%9F%E8%83%BD%E5%B0%8D%E6%AF%94&quot;&gt;免費版 vs 付費版（Plus）：完整功能對比&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/proton-vpn-free-vs-paid-review/#%E8%AE%93-Proton-VPN-%E8%88%87%E7%9C%BE%E4%B8%8D%E5%90%8C%E7%9A%84%E5%9B%9B%E5%A4%A7%E6%8A%80%E8%A1%93%E7%89%B9%E8%89%B2&quot;&gt;讓 Proton VPN 與眾不同的四大技術特色&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/proton-vpn-free-vs-paid-review/#%E3%80%8C%E7%84%A1%E6%97%A5%E8%AA%8C%E3%80%8D%E4%B8%8D%E5%8F%AA%E6%98%AF%E5%BB%A3%E5%91%8A%E8%A9%9E%E2%80%94%E2%80%94%E5%A6%82%E4%BD%95%E9%A9%97%E8%AD%89-Proton-VPN-%E7%9A%84%E8%AA%A0%E4%BF%A1?&quot;&gt;「無日誌」不只是廣告詞——如何驗證 Proton VPN 的誠信？&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/proton-vpn-free-vs-paid-review/#Proton-%E4%B8%8D%E5%8F%AA%E6%98%AF-VPN%E5%AE%8C%E6%95%B4%E9%9A%B1%E7%A7%81%E7%94%9F%E6%85%8B%E7%B3%BB&quot;&gt;Proton 不只是 VPN：完整隱私生態系&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/proton-vpn-free-vs-paid-review/#%E6%88%91%E6%8E%A8%E8%96%A6%E8%AA%B0%E6%87%89%E8%A9%B2%E9%81%B8%E6%93%87%E5%93%AA%E5%80%8B%E6%96%B9%E6%A1%88?&quot;&gt;我推薦誰應該選擇哪個方案？&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/proton-vpn-free-vs-paid-review/#%E7%B5%90%E8%AB%96%E5%85%8D%E8%B2%BB%E7%89%88%E5%80%BC%E5%BE%97%E7%94%A8%E5%97%8E?&quot;&gt;結論：免費版值得用嗎？&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/proton-vpn-free-vs-paid-review/#%E5%8F%83%E8%80%83%E8%B3%87%E6%96%99&quot;&gt;參考資料&lt;/a&gt;&lt;/ul&gt;&lt;/div&gt;&lt;hr&gt;&lt;p&gt;先說明我的使用背景：我自己使用 Proton VPN、Proton Drive 和 Proton Mail 已經超過兩年。選擇它們不是為了切換國家觀看 Netflix，也不是為了玩遊戲或追求特殊用途，主要是想在日常上網時多加一層保護。到國外住飯店、使用公共 Wi-Fi 時，我通常會先開啟 VPN；但 VPN 不是防毒軟體，也不是「開了就無敵」的護身符，這點要先講在前面。&lt;p&gt;Proton Mail 的別名功能可以減少公開主要電子郵件地址，Proton Pass 也能作為密碼管理工具。不過本文主角是 Proton VPN，其他 Proton 服務只會在討論 Proton Unlimited 時順帶提到，免得一篇 VPN 評測突然變成全家福產品目錄。&lt;h2 id=&quot;為什麼大多數「免費-VPN」很危險?&quot;&gt;為什麼大多數「免費 VPN」很危險？&lt;/h2&gt;&lt;p&gt;在討論 Proton VPN 之前，先理解免費 VPN 的商業模式會比較容易判斷風險。不是每一個免費 VPN 都會把使用者當商品，但服務總要有收入來源；如果業者沒有清楚說明資料如何處理，就值得提高警覺。&lt;p&gt;常見的免費 VPN 營利手法包括：&lt;ul&gt;&lt;li&gt;以廣告或資料分析支撐服務，卻沒有清楚交代收集哪些資料&lt;li&gt;記錄 DNS 查詢、連線時間或其他使用 metadata，並用於分析或行銷&lt;li&gt;出租使用者的頻寬，讓其他流量從你的 IP 位址通過&lt;li&gt;使用過度的權限或不透明的第三方元件&lt;/ul&gt;&lt;p&gt;因此，選 VPN 時不能只看「免費」或下載量。隱私政策、公司背景、App 是否公開原始碼、是否有獨立審計，才是比較有用的檢查項目。否則你以為自己買了保護傘，結果傘面寫著「使用者行為分析中」。&lt;hr&gt;&lt;h2 id=&quot;Proton-VPN-免費版市場上的異類&quot;&gt;Proton VPN 免費版：市場上的異類&lt;/h2&gt;&lt;p&gt;Proton VPN Free 的定位，是讓使用者不用付費也能取得基本 VPN 保護。依 Proton 官方方案說明，免費方案提供：&lt;ul&gt;&lt;li&gt;✅ &lt;strong&gt;不限資料量&lt;/strong&gt;&lt;li&gt;✅ &lt;strong&gt;無廣告，且官方承諾不記錄活動紀錄、不出售個人資料&lt;/strong&gt;&lt;li&gt;✅ &lt;strong&gt;Kill Switch 與基本的 VPN 防洩漏保護&lt;/strong&gt;&lt;li&gt;✅ &lt;strong&gt;官方 App 原始碼公開，並接受獨立安全審計&lt;/strong&gt;&lt;/ul&gt;&lt;p&gt;這些條件讓它成為值得考慮的免費入門方案，但「免費」仍然有產品限制。&lt;h3 id=&quot;免費版的三個主要限制&quot;&gt;免費版的三個主要限制&lt;/h3&gt;&lt;ol&gt;&lt;li&gt;&lt;strong&gt;可用地點有限&lt;/strong&gt;：免費伺服器的可用國家與分配方式，應以官方方案頁面和 App 當下顯示為準。如果你需要指定國家，免費版可能不符合需求。&lt;li&gt;&lt;strong&gt;同時只能連線一台裝置&lt;/strong&gt;：家中電腦、手機和平板不能全部一起掛著。這不是 VPN 在鬧脾氣，而是方案限制。&lt;li&gt;&lt;strong&gt;官方速度等級較低&lt;/strong&gt;：Proton 將 Free 標示為 Medium VPN speed；實際速度仍會受距離、伺服器負載、裝置和原本網路品質影響。&lt;/ol&gt;&lt;h3 id=&quot;免費版-vs-付費版速度差異&quot;&gt;免費版 vs 付費版速度差異&lt;/h3&gt;&lt;p&gt;Proton 官方沒有保證 Free 與 Plus 固定的下載、上傳或延遲數字，所以不要把某一次測速結果當成所有人的成績單。官方將 Free 標示為 Medium VPN speed，Plus 則標示為 Highest VPN speed；兩者都沒有資料量上限。&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th style=&quot;text-align:left&quot;&gt;評測指標&lt;th style=&quot;text-align:center&quot;&gt;免費方案 (Free)&lt;th style=&quot;text-align:center&quot;&gt;Plus 付費方案&lt;tbody&gt;&lt;tr&gt;&lt;td style=&quot;text-align:left&quot;&gt;&lt;strong&gt;官方速度描述&lt;/strong&gt;&lt;td style=&quot;text-align:center&quot;&gt;Medium VPN speed&lt;td style=&quot;text-align:center&quot;&gt;Highest VPN speed&lt;tr&gt;&lt;td style=&quot;text-align:left&quot;&gt;&lt;strong&gt;資料量&lt;/strong&gt;&lt;td style=&quot;text-align:center&quot;&gt;無限&lt;td style=&quot;text-align:center&quot;&gt;無限&lt;tr&gt;&lt;td style=&quot;text-align:left&quot;&gt;&lt;strong&gt;實際表現&lt;/strong&gt;&lt;td style=&quot;text-align:center&quot;&gt;受可用地點、距離、伺服器負載與網路環境影響&lt;td style=&quot;text-align:center&quot;&gt;可使用較多地點與 Plus 伺服器，實際表現仍因環境而異&lt;/table&gt;&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;💡 免費版提速小提醒&lt;/strong&gt;：若網頁載入緩慢，可以在 App 中嘗試其他支援的 VPN 協定或 Smart Protocol。協定不是速度保證，距離和伺服器負載通常也很有存在感。&lt;/blockquote&gt;&lt;p&gt;Proton 官方方案頁面目前以「20,000 多台伺服器、140 多個國家」以及最高 10 Gbps 的伺服器能力介紹 Plus。官方也表示 VPN Accelerator 在某些情況下最多可提升約 400% 的速度；這是特定條件下的最高宣稱，不是每次連線都會得到的保證。&lt;hr&gt;&lt;h2 id=&quot;免費版-vs-付費版Plus完整功能對比&quot;&gt;免費版 vs 付費版（Plus）：完整功能對比&lt;/h2&gt;&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;功能&lt;th style=&quot;text-align:center&quot;&gt;免費版&lt;th style=&quot;text-align:center&quot;&gt;Plus 付費版&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;流量限制&lt;td style=&quot;text-align:center&quot;&gt;無限 ✅&lt;td style=&quot;text-align:center&quot;&gt;無限 ✅&lt;tr&gt;&lt;td&gt;可用伺服器國家&lt;td style=&quot;text-align:center&quot;&gt;依官方頁面與 App 當下顯示，地點有限且可能隨機分配&lt;td style=&quot;text-align:center&quot;&gt;&lt;strong&gt;140+ 個（以官方當下資料為準）&lt;/strong&gt;&lt;tr&gt;&lt;td&gt;伺服器數量&lt;td style=&quot;text-align:center&quot;&gt;免費節點&lt;td style=&quot;text-align:center&quot;&gt;&lt;strong&gt;20,000+ 個（以官方當下資料為準）&lt;/strong&gt;&lt;tr&gt;&lt;td&gt;官方速度描述&lt;td style=&quot;text-align:center&quot;&gt;Medium VPN speed&lt;td style=&quot;text-align:center&quot;&gt;Highest VPN speed，最高可達 10 Gbps&lt;tr&gt;&lt;td&gt;同時連線裝置數&lt;td style=&quot;text-align:center&quot;&gt;1 台&lt;td style=&quot;text-align:center&quot;&gt;&lt;strong&gt;10 台&lt;/strong&gt;&lt;tr&gt;&lt;td&gt;支援的串流服務&lt;td style=&quot;text-align:center&quot;&gt;不保證支援&lt;td style=&quot;text-align:center&quot;&gt;付費方案支援部分串流服務&lt;tr&gt;&lt;td&gt;P2P / BT 下載&lt;td style=&quot;text-align:center&quot;&gt;不支援&lt;td style=&quot;text-align:center&quot;&gt;✅&lt;tr&gt;&lt;td&gt;Secure Core（雙重跳板）&lt;td style=&quot;text-align:center&quot;&gt;❌&lt;td style=&quot;text-align:center&quot;&gt;✅&lt;tr&gt;&lt;td&gt;NetShield（廣告與惡意軟體攔截）&lt;td style=&quot;text-align:center&quot;&gt;❌&lt;td style=&quot;text-align:center&quot;&gt;✅&lt;tr&gt;&lt;td&gt;Stealth 混淆協定&lt;td style=&quot;text-align:center&quot;&gt;✅&lt;td style=&quot;text-align:center&quot;&gt;✅&lt;/table&gt;&lt;p&gt;&lt;strong&gt;關鍵升級理由&lt;/strong&gt;：如果你需要指定更多國家、部分串流服務、P2P、NetShield 或 Secure Core，Plus 才有升級的實際理由。Stealth 則不是 Plus 專屬功能，Proton 官方也將它列為 Free 可用的反審查功能。至於串流服務能否連線，仍可能受到服務商封鎖策略、伺服器和所在地區影響，不該寫成「永遠解鎖」。&lt;hr&gt;&lt;h2 id=&quot;讓-Proton-VPN-與眾不同的四大技術特色&quot;&gt;讓 Proton VPN 與眾不同的四大技術特色&lt;/h2&gt;&lt;h3 id=&quot;1.-Secure-Core雙重跳板防禦&quot;&gt;1. Secure Core：雙重跳板防禦&lt;/h3&gt;&lt;p&gt;一般 VPN 的流量路徑是：你的裝置 → VPN 伺服器 → 目標網站。&lt;p&gt;如果出口伺服器遭到監控，理論上可能透過流量關聯分析輸入與輸出，增加追蹤來源的機會。&lt;p&gt;Proton VPN 的 Secure Core 會讓流量先經過 Proton 自有、位於高安全性資料中心的伺服器，再轉接到目標國家的出口伺服器。官方表示 Secure Core 伺服器位於瑞士、冰島與瑞典，並使用自有硬體配置。它能增加追蹤來源的難度，但不代表絕對匿名，也不能消除所有端點或使用習慣造成的風險。&lt;h3 id=&quot;2.-Stealth-協定突破深度封包檢測&quot;&gt;2. Stealth 協定：突破深度封包檢測&lt;/h3&gt;&lt;p&gt;標準的 OpenVPN 和 WireGuard 流量可能有可被識別的特徵，某些網路環境會針對這些特徵進行阻斷。&lt;p&gt;Proton 的 Stealth 協定會：&lt;ol&gt;&lt;li&gt;將 VPN 流量包裝在混淆的 TLS over TCP 通道中。&lt;li&gt;讓流量更像一般 HTTPS，增加深度封包檢測系統辨識與封鎖的難度。&lt;/ol&gt;&lt;p&gt;這對身處高審查網路環境的人可能有幫助，但 Proton 也提醒，封鎖措施會持續變化，沒有任何 VPN 能保證在特定國家或網路永遠連線成功。網路封鎖不是固定考卷，今天能連不代表明天也會給你及格。&lt;h3 id=&quot;3.-NetShieldDNS-層廣告與惡意軟體防護&quot;&gt;3. NetShield：DNS 層廣告與惡意軟體防護&lt;/h3&gt;&lt;p&gt;NetShield 是基於 DNS 的網路層過濾器。啟用後，DNS 請求會在 Proton 伺服器端與已知的廣告、追蹤器和惡意網域清單比對，在資源載入前封鎖符合條件的請求。&lt;p&gt;實際效益：&lt;ul&gt;&lt;li&gt;加速網頁載入（跳過廣告載入時間）&lt;li&gt;節省行動數據流量&lt;li&gt;降低連到已知惡意網域的風險&lt;/ul&gt;&lt;p&gt;這能減少部分廣告、追蹤器和已知惡意網域帶來的干擾，也可能省下一些載入時間與行動數據。不過 NetShield 不是萬能防毒軟體，不能保證攔截所有木馬或病毒；遇到陌生網站和自動下載的檔案，還是要保持警覺。&lt;h3 id=&quot;4.-瑞士法律管轄權法律層面的最強隱私盾牌&quot;&gt;4. 瑞士法律管轄權：法律層面的最強隱私盾牌&lt;/h3&gt;&lt;p&gt;Proton VPN 受瑞士法律管轄，這是評估服務時可以納入的因素，但不能直接等同於「絕對隱私」。&lt;p&gt;Proton 官方表示，在目前的瑞士法律框架下，Proton VPN 沒有強制保存 VPN connection logs 的義務；如果服務本身沒有保存相關 VPN 活動紀錄，也就無法提供不存在的資料。實際權利義務仍應以官方隱私政策、透明度報告和適用法律為準。&lt;p&gt;不過，法律管轄權不是「絕對隱私」的保證。使用者仍應閱讀 Proton 的隱私政策與透明度報告，按照自己的威脅模型選擇服務。&lt;hr&gt;&lt;h2 id=&quot;「無日誌」不只是廣告詞——如何驗證-Proton-VPN-的誠信?&quot;&gt;「無日誌」不只是廣告詞——如何驗證 Proton VPN 的誠信？&lt;/h2&gt;&lt;p&gt;市面上不少 VPN 都宣稱自己是「無日誌（No-Logs）」，但口號不能單獨當證據。可以從以下三個方向檢查：&lt;h3 id=&quot;驗證指標-1第三方獨立安全審計&quot;&gt;驗證指標 1：第三方獨立安全審計&lt;/h3&gt;&lt;p&gt;Proton 會委託獨立資安機構審查 App 或 no-logs 架構，並公開相關報告。Proton 官方的 no-logs 審計文章提到由 &lt;strong&gt;Securitum&lt;/strong&gt; 執行，內容涵蓋 VPN 伺服器是否記錄瀏覽活動、DNS 查詢、連線 metadata 和流量內容。閱讀時仍要注意報告的範圍與日期，因為一次審計不是永久通行證。&lt;h3 id=&quot;驗證指標-2真實法庭案例的考驗最強鐵證&quot;&gt;驗證指標 2：真實法庭案例的考驗（最強鐵證）&lt;/h3&gt;&lt;p&gt;再嚴謹的審計，也可以搭配透明度報告和實際法律案件一起看：&lt;blockquote&gt;&lt;p&gt;Proton 官方 no-logs 說明指出，2019 年曾收到要求提供特定使用者 VPN connection logs 的法律命令，但因為相關紀錄不存在，Proton 無法提供。官方也表示，這項 no-logs 政策在多個法律案件中受到檢驗。這些是 Proton 的官方說法，讀者仍可搭配透明度報告自行判讀。&lt;/blockquote&gt;&lt;h3 id=&quot;驗證指標-3開源程式碼任何人都可以審計&quot;&gt;驗證指標 3：開源程式碼（任何人都可以審計）&lt;/h3&gt;&lt;p&gt;Proton 官方表示，Proton VPN App 的程式碼公開，並接受獨立安全審計。開源讓研究人員可以檢查 App 的實作方式，但 App 開源本身不等於能單獨證明所有伺服器端行為；這也是為什麼審計、法律和透明度資料要一起看。&lt;hr&gt;&lt;h2 id=&quot;Proton-不只是-VPN完整隱私生態系&quot;&gt;Proton 不只是 VPN：完整隱私生態系&lt;/h2&gt;&lt;p&gt;如果你開始關注數位隱私，Proton VPN 只是其中一項工具。Proton 也提供其他隱私導向服務：&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Proton 產品&lt;th&gt;替代方案&lt;th&gt;核心優勢&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Proton Mail&lt;/strong&gt;&lt;td&gt;Gmail&lt;td&gt;提供加密與別名功能；不同郵件情境的加密方式與 metadata 保護程度有所不同&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Proton Drive&lt;/strong&gt;&lt;td&gt;Google Drive&lt;td&gt;以端對端加密和零知識設計保護檔案&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Proton Pass&lt;/strong&gt;&lt;td&gt;瀏覽器密碼管理器&lt;td&gt;加密金庫，並整合 2FA 與別名功能&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Proton Calendar&lt;/strong&gt;&lt;td&gt;Google Calendar&lt;td&gt;以端對端加密設計保護行事曆資料&lt;/table&gt;&lt;p&gt;Proton Unlimited 可一次取得多項 Proton 服務的付費功能。官方方案頁面目前列出 Proton Mail 與 Proton Drive 共用的 500 GB 儲存空間；Calendar 也包含在方案服務中，但不代表共用這項儲存空間。是否划算，要看你是否真的會使用這些服務，不是看到「全家桶」就要把整個廚房搬回家。&lt;h3 id=&quot;💰-Proton-方案價格怎麼看&quot;&gt;💰 Proton 方案價格怎麼看&lt;/h3&gt;&lt;p&gt;Proton 的價格會因計費週期、貨幣、地區、促銷活動與續訂條件變動。本文不硬塞一個台幣換算或促銷價，避免你看到文章時，價格已經像咖啡店的季節限定一樣下架。實際金額請以 &lt;a href=&quot;https://protonvpn.com/pricing&quot;&gt;Proton 官方價格頁面&lt;/a&gt; 和結帳頁為準；台灣信用卡的匯率與海外刷卡手續費，也可能影響最後支出。&lt;hr&gt;&lt;h2 id=&quot;我推薦誰應該選擇哪個方案?&quot;&gt;我推薦誰應該選擇哪個方案？&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;✅ 選免費版，如果你：&lt;/strong&gt;&lt;ul&gt;&lt;li&gt;只是偶爾需要 VPN 保護公共 Wi-Fi 安全&lt;li&gt;不需要解鎖串流平台或 P2P 下載&lt;li&gt;想先試用後再決定是否付費&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;✅ 選 Plus 付費版，如果你：&lt;/strong&gt;&lt;ul&gt;&lt;li&gt;需要官方列出的部分串流服務支援，例如 Netflix、Disney+、Prime Video 等&lt;li&gt;需要 P2P / BT 下載高速伺服器&lt;li&gt;需要更多國家與伺服器選擇&lt;li&gt;需要多裝置同時連線（最多 10 台）&lt;li&gt;需要 Secure Core、NetShield 等進階功能&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;✅ 考慮 Proton Unlimited，如果你：&lt;/strong&gt;&lt;ul&gt;&lt;li&gt;想從 Google / Microsoft 生態系遷移到隱私導向的替代服務&lt;li&gt;會同時使用郵件、雲端、密碼管理和行事曆&lt;li&gt;需要一個涵蓋郵件、雲端、密碼管理的一站式隱私解決方案&lt;/ul&gt;&lt;hr&gt;&lt;h2 id=&quot;結論免費版值得用嗎?&quot;&gt;結論：免費版值得用嗎？&lt;/h2&gt;&lt;p&gt;我的結論是：&lt;strong&gt;Proton VPN Free 值得當作免費入門方案，但不一定適合所有人。&lt;/strong&gt; 它的優點是不限資料量、功能透明，且不必一開始就付款；如果你只是偶爾保護公共 Wi-Fi，通常已經足夠。&lt;p&gt;如果你需要指定更多地點、部分串流服務、P2P、NetShield、Secure Core 或多台裝置同時連線，VPN Plus 才有明確的升級理由。若你還會使用 Proton Mail、Drive、Pass 和 Calendar，再比較 Proton Unlimited；價格請以官方當下頁面為準。&lt;p&gt;最後提醒：VPN 能降低部分網路風險，卻不能替你辨識釣魚網站、修補裝置漏洞，或讓你在網路上自動隱形。選好方案後，更新系統、使用多因素驗證和管理密碼，這些基本功仍然不能外包給 VPN。隱私保護像泡咖啡，磨豆、沖煮、清潔都要做，不能只買一個漂亮濾杯。&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;聯盟揭露：&lt;/strong&gt; 本文包含 Proton VPN 推薦連結。如果你透過連結註冊或購買，我可能獲得推薦回饋；這不會增加你的費用。本文仍應依實際功能、限制與使用需求做選擇。&lt;/blockquote&gt;&lt;p&gt;👉 &lt;strong&gt;&lt;a href=&quot;https://pr.tn/ref/SZX2CCB0&quot;&gt;免費申請 Proton 帳號 →&lt;/a&gt;&lt;/strong&gt;&lt;h2 id=&quot;參考資料&quot;&gt;參考資料&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://protonvpn.com/free-vpn&quot;&gt;Proton VPN 免費方案&lt;/a&gt; — 查閱 Free 方案的功能、限制與申請方式。&lt;li&gt;&lt;a href=&quot;https://protonvpn.com/support/proton-vpn-plans&quot;&gt;Proton VPN 方案說明&lt;/a&gt; — 比較 Free、VPN Plus 與 Proton Unlimited 的功能。&lt;li&gt;&lt;a href=&quot;https://protonvpn.com/pricing&quot;&gt;Proton VPN 價格頁面&lt;/a&gt; — 查閱依地區、計費週期與當期活動顯示的實際價格。&lt;li&gt;&lt;a href=&quot;https://protonvpn.com/secure-core&quot;&gt;Proton VPN Secure Core&lt;/a&gt; — 了解 Secure Core 的流量路徑與伺服器配置。&lt;li&gt;&lt;a href=&quot;https://protonvpn.com/support/netshield&quot;&gt;Proton VPN NetShield&lt;/a&gt; — 了解 DNS 層廣告、追蹤器與惡意網域過濾。&lt;li&gt;&lt;a href=&quot;https://protonvpn.com/blog/no-logs-audit&quot;&gt;Proton VPN no-logs 審計報告&lt;/a&gt; — 查閱 Securitum 審計及 Proton 對 no-logs 政策的說明。&lt;li&gt;&lt;a href=&quot;https://protonvpn.com/transparency-report&quot;&gt;Proton VPN Transparency Report&lt;/a&gt; — 查閱 Proton 公開的法律要求與資料提供紀錄。&lt;/ul&gt;</content>
  </entry>
  <entry>
    <title>AI 時代還需要學習 CSS 嗎？從「語法背誦」到「架構審查」的思維轉向</title>
    <link href="https://kamadiam.com/ai-age-css-learning-strategy/" />
    <updated>2026-07-31T03:17:00Z</updated>
    <id>https://kamadiam.com/ai-age-css-learning-strategy/</id>
    <content type="html">&lt;p&gt;沒有一杯咖啡解決不了的 CSS；如果有，就交給 AI——然後你可能會發現，它把整頁都寫成 &lt;code&gt;position: absolute&lt;/code&gt;。版面跑到懷疑人生，只好再沖一杯。&lt;p&gt;你是不是也遇過這種情境：打開 Cursor 或 Claude，輸入「幫我做一個響應式產品頁」，幾秒後就得到一大串看起來很漂亮的 CSS。你心想：「太好了，以後不用學了！」結果換個螢幕寬度，版面像被貓踩過一樣。&lt;p&gt;結論先講：&lt;strong&gt;CSS 還是要學，但學習重點已經改變。&lt;/strong&gt; 你不必把每個屬性和值背成咖啡店菜單；你需要建立版面與效能的心智模型，能看懂 AI 的決策、驗證結果，並在必要時把它拉回正軌。這篇會帶你整理這套學法，還會示範怎麼寫出比較可靠的 CSS Prompt。&lt;div class=&quot;table-of-contents&quot;&gt;&lt;h2 id=&quot;目錄&quot;&gt;目錄&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/ai-age-css-learning-strategy/#%E4%BD%A0%E4%B8%8D%E5%8F%AA%E8%A6%81%E5%AF%AB-CSS,%E4%B9%9F%E8%A6%81%E5%AF%A9-CSS&quot;&gt;你不只要寫 CSS，也要審 CSS&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/ai-age-css-learning-strategy/#AI-%E8%B6%8A%E5%BC%B7,%E4%BD%A0%E8%B6%8A%E9%9C%80%E8%A6%81-CSS-%E5%BF%83%E6%99%BA%E6%A8%A1%E5%9E%8B&quot;&gt;AI 越強，你越需要 CSS 心智模型&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/ai-age-css-learning-strategy/#CSS-%E6%9C%83%E5%BD%B1%E9%9F%BF%E9%AB%94%E9%A9%97,%E4%B9%9F%E5%8F%AF%E8%83%BD%E7%89%BD%E5%8B%95%E6%90%9C%E5%B0%8B%E8%A1%A8%E7%8F%BE&quot;&gt;CSS 會影響體驗，也可能牽動搜尋表現&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/ai-age-css-learning-strategy/#%E5%9B%9B%E9%9A%8E%E6%AE%B5%E5%AD%B8%E7%BF%92%E8%B7%AF%E7%B7%9A%E9%82%8A%E5%81%9A%E9%82%8A%E8%B7%9F-AI-%E7%B7%B4,%E5%88%A5%E4%B9%BE%E8%AE%80&quot;&gt;四階段學習路線（邊做邊跟 AI 練，別乾讀）&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/ai-age-css-learning-strategy/#%E5%88%A5%E5%8F%AA%E8%AA%AA%E3%80%8C%E5%B9%AB%E6%88%91%E5%81%9A%E4%B8%80%E5%80%8B%E5%8D%A1%E7%89%87%E3%80%8D%E2%80%94%E2%80%94%E7%B5%A6%E5%AE%83%E4%B8%80%E5%80%8B%E5%A5%BD-Prompt&quot;&gt;別只說「幫我做一個卡片」——給它一個好 Prompt&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/ai-age-css-learning-strategy/#%E7%B5%90%E8%AA%9E%E6%9C%83%E5%AF%AB-CSS-%E7%9A%84%E4%BA%BA%E8%AE%8A%E5%A4%9A%E4%BA%86,%E6%9C%83%E5%AF%A9-CSS-%E7%9A%84%E4%BA%BA%E6%9B%B4%E5%80%BC%E9%8C%A2&quot;&gt;結語：會寫 CSS 的人變多了，會審 CSS 的人更值錢&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/ai-age-css-learning-strategy/#%E5%8F%83%E8%80%83%E8%B3%87%E6%96%99&quot;&gt;參考資料&lt;/a&gt;&lt;/ul&gt;&lt;/div&gt;&lt;h2 id=&quot;你不只要寫-CSS,也要審-CSS&quot;&gt;你不只要寫 CSS，也要審 CSS&lt;/h2&gt;&lt;p&gt;以前學 CSS，很大一部分時間花在記屬性與語法。現在查文件很方便，像 &lt;code&gt;align-content&lt;/code&gt; 的值或 &lt;code&gt;flex-shrink&lt;/code&gt; 的預設行為，都可以在需要時查清楚。這不代表基礎不重要，而是學習時間應該從「背答案」移到「理解問題」。&lt;p&gt;真正重要的是，你能不能看出 AI 寫的版面為什麼不穩，並用清楚的條件請它修正。否則就會陷入經典循環：和 AI 來回對話十輪，Bug 還在，咖啡倒是喝了三杯。這不是咖啡因不足，是驗收條件不足。&lt;h3 id=&quot;AI-是乘數底子越厚,幫助越大&quot;&gt;AI 是乘數：底子越厚，幫助越大&lt;/h3&gt;&lt;p&gt;把 AI 想成乘數會比較貼近實際：它可以加快產生草稿、改寫重複程式碼，但不會替你承擔需求不清、驗收錯誤或維護困難的後果。&lt;ul&gt;&lt;li&gt;&lt;strong&gt;概念不清 × AI&lt;/strong&gt;：很快得到一個能展示的版本，但遇到不同內容、不同寬度或下一次修改，就不知道該從哪裡修。&lt;li&gt;&lt;strong&gt;概念清楚 × AI&lt;/strong&gt;：你可以把重複工作交給 AI，再用版面、可及性、效能與可維護性的原則檢查成果。&lt;/ul&gt;&lt;p&gt;白話說，AI 會放大你的能力，也會放大你的盲點。底子不夠，它幫你「快」；底子夠，它才比較有機會幫你「又快又穩」。&lt;h2 id=&quot;AI-越強,你越需要-CSS-心智模型&quot;&gt;AI 越強，你越需要 CSS 心智模型&lt;/h2&gt;&lt;p&gt;AI 能產生程式碼，但它不會替你確認需求，也不會自動知道這個元件在真實內容與各種螢幕下是否合理。因此，它可能交出&lt;strong&gt;看起來對、實際很脆弱&lt;/strong&gt;的解法——像自動咖啡機做出一杯外表像拿鐵、喝起來像苦水的飲料。&lt;p&gt;常見警訊包括：&lt;ol&gt;&lt;li&gt;&lt;strong&gt;亂用 &lt;code&gt;position: absolute&lt;/code&gt;&lt;/strong&gt;：為了對齊就硬拉座標，內容一變或視窗變窄，版面便可能重疊。絕對定位不是禁忌，但應該用在確實需要脫離正常流的情境。&lt;li&gt;&lt;strong&gt;無止境堆 &lt;code&gt;@media&lt;/code&gt; 斷點&lt;/strong&gt;：若每個元件都只依賴視窗寬度，元件放到不同容器時就難以重用。Container Queries 可以在適合的情況下，讓樣式依祖先容器的尺寸調整；前提是先正確設定查詢容器。&lt;li&gt;&lt;strong&gt;把 &lt;code&gt;z-index&lt;/code&gt; 加到 9999 還是沒用&lt;/strong&gt;：&lt;code&gt;z-index&lt;/code&gt; 受層疊上下文影響，數字大不等於一定顯示在最上層。這時候再來一杯咖啡也沒用，得先找出是哪個元素建立了新的層疊上下文。&lt;/ol&gt;&lt;h3 id=&quot;這幾件事,AI-真的替不了你&quot;&gt;這幾件事，AI 真的替不了你&lt;/h3&gt;&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th style=&quot;text-align:left&quot;&gt;概念&lt;th style=&quot;text-align:left&quot;&gt;AI 常犯的錯&lt;th style=&quot;text-align:left&quot;&gt;你要會的&lt;tbody&gt;&lt;tr&gt;&lt;td style=&quot;text-align:left&quot;&gt;&lt;strong&gt;正常流（Normal Flow）&lt;/strong&gt;&lt;td style=&quot;text-align:left&quot;&gt;版面一亂就加定位硬撐&lt;td style=&quot;text-align:left&quot;&gt;知道元素預設如何排列，再判斷是否真的要脫離正常流&lt;tr&gt;&lt;td style=&quot;text-align:left&quot;&gt;&lt;strong&gt;佈局選型&lt;/strong&gt;&lt;td style=&quot;text-align:left&quot;&gt;不分情境套用 Grid 或 Flex&lt;td style=&quot;text-align:left&quot;&gt;Flexbox 適合一維排列，Grid 適合同時處理列與欄；實際選擇仍要看內容&lt;tr&gt;&lt;td style=&quot;text-align:left&quot;&gt;&lt;strong&gt;Intrinsic Sizing&lt;/strong&gt;&lt;td style=&quot;text-align:left&quot;&gt;直接把 &lt;code&gt;width&lt;/code&gt;、&lt;code&gt;height&lt;/code&gt; 寫死&lt;td style=&quot;text-align:left&quot;&gt;理解內容如何影響尺寸，再用 &lt;code&gt;min()&lt;/code&gt;、&lt;code&gt;max()&lt;/code&gt; 或 &lt;code&gt;clamp()&lt;/code&gt; 等方式設計彈性&lt;tr&gt;&lt;td style=&quot;text-align:left&quot;&gt;&lt;strong&gt;設計系統（Tokens）&lt;/strong&gt;&lt;td style=&quot;text-align:left&quot;&gt;到處硬編碼顏色與間距&lt;td style=&quot;text-align:left&quot;&gt;使用像 &lt;code&gt;--color-primary&lt;/code&gt; 這類變數，讓修改有一致的入口&lt;/table&gt;&lt;h2 id=&quot;CSS-會影響體驗,也可能牽動搜尋表現&quot;&gt;CSS 會影響體驗，也可能牽動搜尋表現&lt;/h2&gt;&lt;p&gt;CSS 不只影響外觀，也會參與版面配置與瀏覽器渲染。Core Web Vitals（CWV）是用來觀察載入、互動與視覺穩定性的使用者體驗指標；Google 說明這些指標會在相關工具中呈現，也可能作為搜尋排名系統考量的一部分，但它們不是「CSS 分數」，更不是分數一掉就必然失去流量。真正的目標，是讓使用者感覺網站穩定、快速、好操作。&lt;p&gt;AI 產出的 CSS 可以從這些角度檢查：&lt;ul&gt;&lt;li&gt;&lt;strong&gt;CLS（累積版面位移）&lt;/strong&gt;：圖片或嵌入內容若沒有預留空間，載入後可能把既有內容推開。可以確認媒體尺寸、&lt;code&gt;aspect-ratio&lt;/code&gt; 或其他空間預留方式是否符合實際版面。&lt;li&gt;&lt;strong&gt;INP（互動至下一次繪製）&lt;/strong&gt;：它反映使用者互動後，頁面回應並完成下一次繪製的延遲。動畫若頻繁改動會影響版面配置的屬性，可能增加瀏覽器工作量；在適合的情況下，可優先評估 &lt;code&gt;transform&lt;/code&gt; 或 &lt;code&gt;opacity&lt;/code&gt;，但仍要用實際測量驗證，不能保證換了屬性就一定改善。&lt;li&gt;&lt;strong&gt;LCP（最大內容繪製）&lt;/strong&gt;：主要內容的載入與呈現也會影響體驗。不要只看 CSS，還要一起檢查圖片、字型、HTML 與 JavaScript 的載入路徑。&lt;/ul&gt;&lt;p&gt;想搞懂門檻跟優化清單，可以看這篇：&lt;a href=&quot;https://kamadiam.com/pagespeed-core-web-vitals/&quot;&gt;PageSpeed Insights 與 Core Web Vitals 2026 完全指南&lt;/a&gt;。&lt;h2 id=&quot;四階段學習路線邊做邊跟-AI-練,別乾讀&quot;&gt;四階段學習路線（邊做邊跟 AI 練，別乾讀）&lt;/h2&gt;&lt;p&gt;如果你現在要重新學 CSS，我會建議照這張地圖走。每完成一階段，可以獎勵自己一杯咖啡；只是別邊喝邊讓 AI 無限制自由發揮：&lt;ol&gt;&lt;li&gt;&lt;strong&gt;階段 1：先建立心智模型&lt;/strong&gt;——先用正常流做出可閱讀的 HTML，再觀察什麼時候需要 Flexbox、Grid 或定位。你會逐漸理解「流」不是玄學，而是瀏覽器的預設排版方式。&lt;li&gt;&lt;strong&gt;階段 2：練習 Flexbox 與 Grid 的分工&lt;/strong&gt;——先問自己是在處理一個方向，還是同時管理列與欄，再選擇工具。可以要求 AI：「請說明為什麼這個區塊適合使用 Flexbox 或 Grid。」&lt;li&gt;&lt;strong&gt;階段 3：練習元件級響應式&lt;/strong&gt;——學會 Container Queries 的基本條件，理解它和 Media Queries 解決的是不同層級的問題，再請 AI 產生小範例並逐行審查。&lt;li&gt;&lt;strong&gt;階段 4：加入效能與可及性驗收&lt;/strong&gt;——用 PageSpeed Insights、瀏覽器開發者工具或其他合適工具觀察 LCP、CLS、INP 與可及性問題。&lt;code&gt;content-visibility&lt;/code&gt; 可以是進階研究題目，但要先理解它的適用情境與限制，不要把它當成長頁面的萬用咖啡粉。&lt;/ol&gt;&lt;h2 id=&quot;別只說「幫我做一個卡片」——給它一個好-Prompt&quot;&gt;別只說「幫我做一個卡片」——給它一個好 Prompt&lt;/h2&gt;&lt;p&gt;模糊指令 = 模糊產出。就像你走進咖啡店只說「給我一杯好喝的」，店員也只能猜。你可以複製下面這個結構，再依專案改寫：&lt;pre class=&quot;language-markdown&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-markdown&quot;&gt;請為我實作一個響應式文章卡片元件：
&lt;span class=&quot;token list punctuation&quot;&gt;1.&lt;/span&gt; &lt;span class=&quot;token bold&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;**&lt;/span&gt;&lt;span class=&quot;token content&quot;&gt;佈局&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;**&lt;/span&gt;&lt;/span&gt;：使用語意化 HTML；圖片在上、標題在下、按鈕靠底對齊。請說明為什麼選 Flexbox 或 Grid。
&lt;span class=&quot;token list punctuation&quot;&gt;2.&lt;/span&gt; &lt;span class=&quot;token bold&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;**&lt;/span&gt;&lt;span class=&quot;token content&quot;&gt;設計系統&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;**&lt;/span&gt;&lt;/span&gt;：優先使用專案既有的 CSS 變數；如果沒有，先列出需要的變數，再提供暫時值，禁止散落難以維護的硬編碼色碼。
&lt;span class=&quot;token list punctuation&quot;&gt;3.&lt;/span&gt; &lt;span class=&quot;token bold&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;**&lt;/span&gt;&lt;span class=&quot;token content&quot;&gt;響應式&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;**&lt;/span&gt;&lt;/span&gt;：若使用 Container Queries，請補上必要的容器設定，並說明查詢條件；不要只複製視窗斷點。
&lt;span class=&quot;token list punctuation&quot;&gt;4.&lt;/span&gt; &lt;span class=&quot;token bold&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;**&lt;/span&gt;&lt;span class=&quot;token content&quot;&gt;效能&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;**&lt;/span&gt;&lt;/span&gt;：只在適合的情況使用 &lt;span class=&quot;token code-snippet code keyword&quot;&gt;`transform`&lt;/span&gt;、&lt;span class=&quot;token code-snippet code keyword&quot;&gt;`opacity`&lt;/span&gt; 等動畫方式，並說明仍需如何測量驗證。
&lt;span class=&quot;token list punctuation&quot;&gt;5.&lt;/span&gt; &lt;span class=&quot;token bold&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;**&lt;/span&gt;&lt;span class=&quot;token content&quot;&gt;a11y&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;**&lt;/span&gt;&lt;/span&gt;：確保鍵盤 focus 狀態可見、互動元素有清楚名稱，並檢查文字與背景對比是否符合 WCAG 2.2 的要求。
&lt;span class=&quot;token list punctuation&quot;&gt;6.&lt;/span&gt; &lt;span class=&quot;token bold&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;**&lt;/span&gt;&lt;span class=&quot;token content&quot;&gt;要求&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;**&lt;/span&gt;&lt;/span&gt;：請先解釋佈局與響應式策略，再提供 HTML 與 CSS 程式碼，最後列出需要人工驗收的項目。&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;重點不是 Prompt 越長越好，而是&lt;strong&gt;把決策權留給你&lt;/strong&gt;。先讓 AI 解釋為什麼這樣排、有哪些假設，再決定要不要採用。AI 可以當外骨骼，但方向盤還是你的；不然它很可能一路開向 &lt;code&gt;!important&lt;/code&gt; 的荒野。&lt;h2 id=&quot;結語會寫-CSS-的人變多了,會審-CSS-的人更值錢&quot;&gt;結語：會寫 CSS 的人變多了，會審 CSS 的人更值錢&lt;/h2&gt;&lt;p&gt;「會寫 CSS」這件事本身，不代表價值消失了；只是單純產生一段能跑的樣式，已經不是完整能力。真正有差的是：你能不能交付一個&lt;strong&gt;能上線、好維護、在真實情境下穩定&lt;/strong&gt;的成品。&lt;p&gt;下次請 AI 寫 CSS 時，先給它清楚的限制，再逐項驗收。沒有一杯咖啡解決不了的 CSS，如果有，就交給 AI——但記得，審完再來一杯。&lt;h2 id=&quot;參考資料&quot;&gt;參考資料&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/CSS_layout/Introduction&quot;&gt;MDN：CSS 版面配置入門&lt;/a&gt;：正常流、Flexbox 與 Grid 的基本概念。&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/Guides/Flexible_box_layout/Relationship_with_other_layout_methods&quot;&gt;MDN：Flexbox 與其他版面方法的關係&lt;/a&gt;：Flexbox 一維佈局的特性與選用思路。&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/Guides/Grid_layout/Relationship_with_other_layout_methods&quot;&gt;MDN：Grid 與其他版面方法的關係&lt;/a&gt;：Grid 二維佈局的特性與和 Flexbox 的差異。&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/Guides/Containment/Container_queries&quot;&gt;MDN：CSS Container Queries&lt;/a&gt;：依容器尺寸套用樣式的語法與必要設定。&lt;li&gt;&lt;a href=&quot;https://web.dev/articles/vitals&quot;&gt;web.dev：Web Vitals&lt;/a&gt;：LCP、INP、CLS 的定義、門檻與測量方式。&lt;li&gt;&lt;a href=&quot;https://www.w3.org/TR/WCAG22/&quot;&gt;W3C：Web Content Accessibility Guidelines (WCAG) 2.2&lt;/a&gt;：可及性成功準則，包括鍵盤操作與色彩對比。&lt;/ul&gt;</content>
  </entry>
</feed>