<?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-29T11:38:00Z</updated>
  <id>https://kamadiam.com/</id>
  <author>
    <name>huihere</name>
  </author>
  <entry>
    <title>Eleventy 怎麼顯示流程圖，Mermaid 的顯示和優化</title>
    <link href="https://kamadiam.com/eleventy-mermaid-static-images/" />
    <updated>2026-08-29T11:38:00Z</updated>
    <id>https://kamadiam.com/eleventy-mermaid-static-images/</id>
    <content type="html">&lt;p&gt;Mermaid 很方便：寫幾行文字，就能變成流程圖。問題是，如果每頁都把 Mermaid library 一起送到瀏覽器，讀者可能只是來看一篇文章，卻先搬一箱 JavaScript 回家。這篇的做法是：固定不變的圖在 build 時先匯出成 SVG 或 PNG，網頁只顯示普通圖片。&lt;/p&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/eleventy-mermaid-static-images/#%E5%85%88%E8%AC%9B%E7%B5%90%E8%AB%96%E5%9B%BA%E5%AE%9A%E5%9C%96%E5%9C%A8-build-%E6%99%82%E5%8C%AF%E5%87%BA&quot;&gt;先講結論：固定圖在 build 時匯出&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/eleventy-mermaid-static-images/#SVGPNG,%E9%82%84%E6%98%AF%E7%80%8F%E8%A6%BD%E5%99%A8%E7%AB%AF-Mermaid?&quot;&gt;SVG、PNG，還是瀏覽器端 Mermaid？&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/eleventy-mermaid-static-images/#%E7%AC%AC%E4%B8%80%E6%AD%A5%E6%BA%96%E5%82%99-Mermaid-%E5%8E%9F%E5%A7%8B%E6%AA%94&quot;&gt;第一步：準備 Mermaid 原始檔&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/eleventy-mermaid-static-images/#%E7%AC%AC%E4%BA%8C%E6%AD%A5%E7%94%A8-Mermaid-CLI-%E5%8C%AF%E5%87%BA-SVG-%E6%88%96-PNG&quot;&gt;第二步：用 Mermaid CLI 匯出 SVG 或 PNG&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/eleventy-mermaid-static-images/#%E7%AC%AC%E4%B8%89%E6%AD%A5%E8%AE%93-Eleventy-%E8%A4%87%E8%A3%BD%E5%9C%96%E6%AA%94&quot;&gt;第三步：讓 Eleventy 複製圖檔&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/eleventy-mermaid-static-images/#%E7%AC%AC%E5%9B%9B%E6%AD%A5%E8%AE%93%E5%9C%96%E7%89%87%E5%9C%A8%E6%89%8B%E6%A9%9F%E4%B8%8A%E4%B8%8D%E6%8B%86%E5%8F%B0&quot;&gt;第四步：讓圖片在手機上不拆台&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/eleventy-mermaid-static-images/#alt-%E4%B8%8D%E8%A6%81%E5%8F%AA%E5%AF%AB%E3%80%8CMermaid-%E5%9C%96%E3%80%8D&quot;&gt;alt 不要只寫「Mermaid 圖」&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/eleventy-mermaid-static-images/#Vite-%E9%A1%AF%E7%A4%BA-chunk-%E9%81%8E%E5%A4%A7,%E6%87%89%E8%A9%B2%E6%80%8E%E9%BA%BC%E5%88%A4%E6%96%B7?&quot;&gt;Vite 顯示 chunk 過大，應該怎麼判斷？&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/eleventy-mermaid-static-images/#%E4%B8%8A%E7%B7%9A%E5%89%8D%E6%AA%A2%E6%9F%A5%E6%B8%85%E5%96%AE&quot;&gt;上線前檢查清單&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/eleventy-mermaid-static-images/#%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;li&gt;&lt;a href=&quot;https://kamadiam.com/eleventy-mermaid-static-images/#%E7%B5%90%E8%AA%9E%E5%85%88%E6%8A%8A%E5%9B%BA%E5%AE%9A%E5%9C%96%E6%90%AC%E5%88%B0-build-%E6%99%82%E9%96%93&quot;&gt;結語：先把固定圖搬到 build 時間&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/eleventy-mermaid-static-images/#%E5%8F%83%E8%80%83%E8%B3%87%E6%96%99&quot;&gt;參考資料&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;h2 id=&quot;先講結論固定圖在-build-時匯出&quot;&gt;先講結論：固定圖在 build 時匯出&lt;/h2&gt;&lt;p&gt;文章中的流程圖、架構圖通常不需要訪客現場編輯或互動。這類圖適合用 Mermaid CLI 在建置前轉成靜態檔，再用 &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; 顯示。&lt;/p&gt;&lt;p&gt;這樣做有三個好處：&lt;/p&gt;&lt;ul&gt;&lt;li&gt;不用每頁載入 Mermaid JavaScript。&lt;/li&gt;&lt;li&gt;Vite bundle 不會因為固定圖而一起變胖。&lt;/li&gt;&lt;li&gt;可以用圖片的 &lt;code&gt;alt&lt;/code&gt;、&lt;code&gt;width&lt;/code&gt;、&lt;code&gt;height&lt;/code&gt; 和 &lt;code&gt;&amp;lt;figcaption&amp;gt;&lt;/code&gt; 照顧可讀性與版面穩定。&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;如果圖真的會跟著訪客操作即時變化，才保留瀏覽器端 Mermaid。互動不是免費的，通常會用 JavaScript 預算付款。&lt;/p&gt;&lt;h2 id=&quot;SVGPNG,還是瀏覽器端-Mermaid?&quot;&gt;SVG、PNG，還是瀏覽器端 Mermaid？&lt;/h2&gt;&lt;p&gt;先看圖的用途，不要先看工具名稱。選錯格式，就像拿海報字體去印名片：不是不能用，只是會有點委屈。&lt;/p&gt;&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;情境&lt;/th&gt;&lt;th&gt;建議&lt;/th&gt;&lt;th&gt;取捨&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;一般流程圖、架構圖&lt;/td&gt;&lt;td&gt;build-time SVG&lt;/td&gt;&lt;td&gt;線條與文字縮放清楚；仍要檢查文字大小與對比&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;固定像素圖片、社群分享、不想處理 SVG&lt;/td&gt;&lt;td&gt;build-time PNG&lt;/td&gt;&lt;td&gt;瀏覽器處理單純；放大可能變糊，檔案也可能較大&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;圖會因操作即時改變&lt;/td&gt;&lt;td&gt;瀏覽器端 Mermaid&lt;/td&gt;&lt;td&gt;保留互動，但要負擔下載、解析與執行 JavaScript&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;想把圖直接放進 HTML&lt;/td&gt;&lt;td&gt;build-time inline SVG&lt;/td&gt;&lt;td&gt;少一次圖片請求；HTML 變大，也要審查 SVG 內容&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;多數教學文章先選 SVG。若你的分享流程、圖片處理或內容系統更適合 PNG，也可以在同一次 build 產出 PNG。重點不是「PNG 永遠比較快」，而是不要讓每個頁面都執行整套繪圖 library。&lt;/p&gt;&lt;h2 id=&quot;第一步準備-Mermaid-原始檔&quot;&gt;第一步：準備 Mermaid 原始檔&lt;/h2&gt;&lt;p&gt;例如建立 &lt;code&gt;src/diagrams/publish-flow.mmd&lt;/code&gt;：&lt;/p&gt;&lt;pre class=&quot;language-mermaid&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-mermaid&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;flowchart&lt;/span&gt; TD
  A&lt;span class=&quot;token text string&quot;&gt;[寫文章]&lt;/span&gt; &lt;span class=&quot;token arrow operator&quot;&gt;--&gt;&lt;/span&gt; B&lt;span class=&quot;token text string&quot;&gt;{檢查}&lt;/span&gt;
  B &lt;span class=&quot;token arrow operator&quot;&gt;--&gt;&lt;/span&gt;&lt;span class=&quot;token label property&quot;&gt;|通過|&lt;/span&gt; C&lt;span class=&quot;token text string&quot;&gt;[Eleventy build]&lt;/span&gt;
  B &lt;span class=&quot;token arrow operator&quot;&gt;--&gt;&lt;/span&gt;&lt;span class=&quot;token label property&quot;&gt;|未通過|&lt;/span&gt; A
  C &lt;span class=&quot;token arrow operator&quot;&gt;--&gt;&lt;/span&gt; D&lt;span class=&quot;token text string&quot;&gt;[輸出靜態網站]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;.mmd&lt;/code&gt; 是圖表的原始碼。它像食譜；SVG 或 PNG 才是端上桌的蛋糕。讀者通常只需要蛋糕，不需要一起下載烤箱。&lt;/p&gt;&lt;h2 id=&quot;第二步用-Mermaid-CLI-匯出-SVG-或-PNG&quot;&gt;第二步：用 Mermaid CLI 匯出 SVG 或 PNG&lt;/h2&gt;&lt;p&gt;Mermaid 官方 CLI 的套件名稱是 &lt;code&gt;@mermaid-js/mermaid-cli&lt;/code&gt;，命令名稱是 &lt;code&gt;mmdc&lt;/code&gt;。可以全域安裝：&lt;/p&gt;&lt;pre class=&quot;language-sh&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-g&lt;/span&gt; @mermaid-js/mermaid-cli
mmdc &lt;span class=&quot;token parameter variable&quot;&gt;-i&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;src/diagrams/publish-flow.mmd&quot;&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-o&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;src/assets/diagrams/publish-flow.svg&quot;&lt;/span&gt;
mmdc &lt;span class=&quot;token parameter variable&quot;&gt;-i&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;src/diagrams/publish-flow.mmd&quot;&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-o&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;src/assets/diagrams/publish-flow.png&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;或者裝在專案內，讓本機和 CI 使用同一套依賴：&lt;/p&gt;&lt;pre class=&quot;language-sh&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; --save-dev @mermaid-js/mermaid-cli
./node_modules/.bin/mmdc &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;-i&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;src/diagrams/publish-flow.mmd&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;-o&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;src/assets/diagrams/publish-flow.svg&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;mmdc&lt;/code&gt; 會依輸出副檔名產生 SVG、PNG 或 PDF。也可以在輸出時指定主題與透明背景：&lt;/p&gt;&lt;pre class=&quot;language-sh&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;mmdc &lt;span class=&quot;token parameter variable&quot;&gt;-i&lt;/span&gt; input.mmd &lt;span class=&quot;token parameter variable&quot;&gt;-o&lt;/span&gt; output.png &lt;span class=&quot;token parameter variable&quot;&gt;-t&lt;/span&gt; dark &lt;span class=&quot;token parameter variable&quot;&gt;-b&lt;/span&gt; transparent&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;網站有深色模式時要多留意：SVG 能縮放，不代表它會自動換成深色配色。需要時在 build 時產生 light／dark 兩份，或固定一套在兩種背景都看得清楚的主題。&lt;/p&gt;&lt;h2 id=&quot;第三步讓-Eleventy-複製圖檔&quot;&gt;第三步：讓 Eleventy 複製圖檔&lt;/h2&gt;&lt;p&gt;如果圖片放在 &lt;code&gt;src/assets/diagrams/&lt;/code&gt;，在 &lt;code&gt;eleventy.config.js&lt;/code&gt; 加入 passthrough copy：&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;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;addPassthroughCopy&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;&quot;src/assets/diagrams&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;assets/diagrams&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 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;assets/diagrams&lt;/code&gt;。接著，文章或模板可以用普通 HTML 顯示圖片：&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;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;diagram&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;/assets/diagrams/publish-flow.svg&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;發布流程：寫文章後先檢查；通過才執行 Eleventy build，失敗則回到修改文章。&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;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;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;520&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;figcaption&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;圖一：文章從檢查到 Eleventy build 的流程。&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;figcaption&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;p&gt;如果 Markdown 設定允許原始 HTML，直接放上面的 &lt;code&gt;&amp;lt;figure&amp;gt;&lt;/code&gt; 即可。這裡要放的是 &lt;code&gt;&amp;lt;img src=&amp;quot;...&amp;quot;&amp;gt;&lt;/code&gt;，不是 &lt;code&gt;&amp;lt;div class=&amp;quot;mermaid&amp;quot;&amp;gt;&lt;/code&gt;。後者只是標記，仍需要 Mermaid JavaScript 在瀏覽器把它畫出來。&lt;/p&gt;&lt;p&gt;若 Markdown 會過濾 HTML，可改用 Nunjucks／WebC layout 或 shortcode 輸出同一段 HTML。不要為了繞過過濾器，就把所有內容無條件標成 &lt;code&gt;safe&lt;/code&gt;。&lt;/p&gt;&lt;h2 id=&quot;第四步讓圖片在手機上不拆台&quot;&gt;第四步：讓圖片在手機上不拆台&lt;/h2&gt;&lt;p&gt;圖片 CSS 可以先從這個版本開始：&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 selector&quot;&gt;.diagram&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;margin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 2rem 0&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;.diagram img&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; block&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; auto&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 punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;.diagram figcaption&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;margin-top&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0.5rem&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--muted&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; #666&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;width&lt;/code&gt; 和 &lt;code&gt;height&lt;/code&gt; 要填輸出圖片的實際像素尺寸。瀏覽器可以先依比例保留空間，降低圖片載入時內容跳動；如果比例填錯，反而可能失真或預留錯誤空間。&lt;/p&gt;&lt;p&gt;寬流程圖在手機上常見兩種結果：&lt;/p&gt;&lt;ul&gt;&lt;li&gt;圖縮到看不清楚：改成較短的縱向流程。&lt;/li&gt;&lt;li&gt;圖不能再縮：提供原尺寸圖片連結，讓讀者自行放大查看。&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;如果有不同寬度或解析度版本，再考慮 &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; 和 &lt;code&gt;srcset&lt;/code&gt;。不要把一張超大 PNG 無條件送給所有裝置。&lt;/p&gt;&lt;h2 id=&quot;alt-不要只寫「Mermaid-圖」&quot;&gt;&lt;code&gt;alt&lt;/code&gt; 不要只寫「Mermaid 圖」&lt;/h2&gt;&lt;p&gt;&lt;code&gt;alt&lt;/code&gt; 應該描述讀者從圖中需要知道的結論或流程，例如「通過才執行 Eleventy build，失敗則回到修改文章」，而不是只寫「流程圖」。&lt;/p&gt;&lt;p&gt;如果正文已經完整說明同一個流程，圖片可以使用 &lt;code&gt;alt=&amp;quot;&amp;quot;&lt;/code&gt;，避免螢幕閱讀器重複朗讀。但正文不能因此消失。圖表不是把文字藏起來的魔法斗篷。&lt;/p&gt;&lt;p&gt;&lt;code&gt;&amp;lt;figure&amp;gt;&lt;/code&gt; 讓圖片成為一個有語意的內容單位，&lt;code&gt;&amp;lt;figcaption&amp;gt;&lt;/code&gt; 則提供看得見的圖說，也能成為父層 &lt;code&gt;&amp;lt;figure&amp;gt;&lt;/code&gt; 的 accessible name。不要只靠顏色區分成功與失敗；把差異寫在節點文字、箭頭標籤或正文裡。&lt;/p&gt;&lt;h2 id=&quot;Vite-顯示-chunk-過大,應該怎麼判斷?&quot;&gt;Vite 顯示 chunk 過大，應該怎麼判斷？&lt;/h2&gt;&lt;p&gt;Vite 的 &lt;code&gt;build.chunkSizeWarningLimit&lt;/code&gt; 預設是 500 kB。這是警告門檻，不是超過就會壞掉的硬限制；把數字調大，只會讓終端機比較安靜，不會讓下載速度變快。&lt;/p&gt;&lt;p&gt;可以用這張表判斷：&lt;/p&gt;&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;你遇到的情況&lt;/th&gt;&lt;th&gt;合理處理&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;固定 Mermaid 圖，文章不需要互動&lt;/td&gt;&lt;td&gt;build-time 匯出 SVG／PNG，頁面只載入圖片&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;只有少數頁面需要互動圖&lt;/td&gt;&lt;td&gt;用 &lt;code&gt;import(&#39;mermaid&#39;)&lt;/code&gt; 延遲載入，並限制在需要的頁面&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;chunk 已經量測過，大小可接受，只是不想看到警告&lt;/td&gt;&lt;td&gt;調高 warning 門檻，但不要把它當效能修復&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;想改善快取與分組&lt;/td&gt;&lt;td&gt;使用手動切 chunk；它不會消除 Mermaid 的下載與執行成本&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;最後要看實際結果：檢查 build 後不看圖的頁面是否還載入 Mermaid chunk，再用瀏覽器 Network、Lighthouse 和行動裝置測試。警告是煙霧警報器，不是火災報告；先找煙從哪裡來。&lt;/p&gt;&lt;h2 id=&quot;上線前檢查清單&quot;&gt;上線前檢查清單&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;[ ] &lt;code&gt;.mmd&lt;/code&gt; 與輸出的圖片都有納入版本控制，或 build 能穩定重建。&lt;/li&gt;&lt;li&gt;[ ] &lt;code&gt;mmdc&lt;/code&gt; 固定在專案 dev dependency，避免 CI 和本機產出不同。&lt;/li&gt;&lt;li&gt;[ ] 輸出路徑與 &lt;code&gt;&amp;lt;img src&amp;gt;&lt;/code&gt; 在開發伺服器、正式站與子路徑部署都正確。&lt;/li&gt;&lt;li&gt;[ ] 圖片有描述性 &lt;code&gt;alt&lt;/code&gt;，需要時補上 &lt;code&gt;&amp;lt;figcaption&amp;gt;&lt;/code&gt; 和正文文字版。&lt;/li&gt;&lt;li&gt;[ ] &lt;code&gt;width&lt;/code&gt;／&lt;code&gt;height&lt;/code&gt; 與 responsive CSS 已設定。&lt;/li&gt;&lt;li&gt;[ ] Light／dark mode 下文字與背景都有足夠對比。&lt;/li&gt;&lt;li&gt;[ ] 手機實機看得懂；太寬時提供原圖連結或拆成多張圖。&lt;/li&gt;&lt;li&gt;[ ] build 後確認不看圖的頁面沒有載入 Mermaid chunk。&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;推薦工具/資源&quot;&gt;推薦工具／資源&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Mermaid CLI&lt;/strong&gt;：適合固定流程圖、架構圖，想在 build 時產生 SVG／PNG 的站長。&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Eleventy passthrough copy&lt;/strong&gt;：適合把已產生的圖片穩定複製到輸出目錄。&lt;/li&gt;&lt;li&gt;&lt;strong&gt;瀏覽器 Network 與 Lighthouse&lt;/strong&gt;：適合確認「真的變快了」，而不是只讓 Vite 警告消失。&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;結語先把固定圖搬到-build-時間&quot;&gt;結語：先把固定圖搬到 build 時間&lt;/h2&gt;&lt;p&gt;固定圖先匯出，互動圖才在需要時載入 Mermaid。這個分工通常比「每頁都準備一套繪圖工具」更省事，也更符合靜態網站的個性。&lt;/p&gt;&lt;p&gt;下一次新增 Mermaid 圖時，可以先做一件事：用 &lt;code&gt;mmdc&lt;/code&gt; 產出 SVG 或 PNG，放進 Eleventy 的 passthrough asset，再用帶有 &lt;code&gt;alt&lt;/code&gt;、尺寸和圖說的 &lt;code&gt;&amp;lt;figure&amp;gt;&lt;/code&gt; 顯示。先改一張圖，再看 build 與 Network 數據；咖啡可以慢慢喝，bundle 不必慢慢胖。&lt;/p&gt;&lt;h2 id=&quot;參考資料&quot;&gt;參考資料&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/mermaid-js/mermaid-cli/blob/master/README.md&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Mermaid CLI 官方 README&lt;/a&gt; — 官方 CLI 安裝、&lt;code&gt;mmdc&lt;/code&gt; 參數，以及 SVG／PNG／PDF 輸出。&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://mermaid.js.org/config/mermaidCLI.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Mermaid CLI 官方文件入口&lt;/a&gt; — Mermaid CLI 文件入口。&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://www.11ty.dev/docs/copy/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Eleventy Passthrough File Copy&lt;/a&gt; — &lt;code&gt;addPassthroughCopy&lt;/code&gt; 的來源與輸出路徑。&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://vite.dev/config/build-options&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Vite Build Options&lt;/a&gt; — &lt;code&gt;chunkSizeWarningLimit&lt;/code&gt; 的預設值與用途。&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/figure&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;MDN：&lt;code&gt;&amp;lt;figure&amp;gt;&lt;/code&gt;&lt;/a&gt; — &lt;code&gt;figure&lt;/code&gt;、&lt;code&gt;figcaption&lt;/code&gt; 與語意結構。&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/Structuring_content/HTML_images&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;MDN：HTML images&lt;/a&gt; — &lt;code&gt;img&lt;/code&gt;、&lt;code&gt;alt&lt;/code&gt; 與圖片可及性。&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/Guides/Box_sizing/Aspect_ratios&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;MDN：Understanding and setting aspect ratios&lt;/a&gt; — 圖片尺寸與版面空間預留。&lt;/li&gt;&lt;/ul&gt;</content>
  </entry>
  <entry>
    <title>PHP 架站全指南：開發者必知的 5 個核心概念</title>
    <link href="https://kamadiam.com/what-is-php/" />
    <updated>2026-08-29T00:00:00Z</updated>
    <id>https://kamadiam.com/what-is-php/</id>
    <content type="html">&lt;p&gt;「PHP 已死」這句話聽了十多年，但 &lt;a href=&quot;https://w3techs.com/technologies/details/pl-php&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;W3Techs 的每日統計&lt;/a&gt; 在 2026 年 8 月仍顯示，&lt;strong&gt;約 70.5%&lt;/strong&gt; 的「已知伺服器端語言」網站使用 PHP；&lt;a href=&quot;https://w3techs.com/technologies/comparison/cm-wordpress&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;WordPress&lt;/a&gt; 則約佔全站 &lt;strong&gt;41.2%&lt;/strong&gt;。數字不會替語言辯護，卻說明一件事：架站與內容系統這條路上，PHP 仍然很有存在感。畢竟，真正退休的程式語言通常不會一直被拿出來宣布死亡。&lt;/p&gt;&lt;p&gt;本文用五個核心概念，幫你快速判斷「該不該學 PHP、怎麼開始、跟 Node.js／Python 怎麼選」。&lt;/p&gt;&lt;h2 id=&quot;5-個核心概念TL;DR&quot;&gt;5 個核心概念（TL;DR）&lt;/h2&gt;&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;#&lt;/th&gt;&lt;th&gt;概念&lt;/th&gt;&lt;th&gt;一句話&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;&lt;strong&gt;PHP 是什麼&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;伺服器端腳本語言，在伺服器產生 HTML 再送給瀏覽器&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;&lt;strong&gt;為什麼還活著&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;WordPress 等 CMS 生態系仍佔網站 40%+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;&lt;strong&gt;vs Node／Python&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;架站/CMS 選 PHP；即時 API／ML 選其他&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;4&lt;/td&gt;&lt;td&gt;&lt;strong&gt;PHP 8 新特性&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;JIT、Union types、Attributes 等現代語法&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;5&lt;/td&gt;&lt;td&gt;&lt;strong&gt;怎麼開始&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;本機 Herd／Docker + &lt;a href=&quot;https://kamadiam.com/php-basic-tutorial/&quot;&gt;PHP 新手入門&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&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/what-is-php/#5-%E5%80%8B%E6%A0%B8%E5%BF%83%E6%A6%82%E5%BF%B5TL;DR&quot;&gt;5 個核心概念（TL;DR）&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/what-is-php/#1.-PHP-%E6%98%AF%E4%BB%80%E9%BA%BC%E4%BC%BA%E6%9C%8D%E5%99%A8%E7%AB%AF%E8%85%B3%E6%9C%AC%E8%AA%9E%E8%A8%80&quot;&gt;1. PHP 是什麼：伺服器端腳本語言&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/what-is-php/#2.-%E7%82%BA%E4%BB%80%E9%BA%BC%E9%82%84%E6%9C%89%E4%BA%BA%E7%94%A8-PHP&quot;&gt;2. 為什麼還有人用 PHP&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/what-is-php/#3.-PHP-vs-Node.js-vs-Python%E6%80%8E%E9%BA%BC%E9%81%B8&quot;&gt;3. PHP vs Node.js vs Python：怎麼選&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/what-is-php/#4.-PHP-8.x-%E4%BD%A0%E8%A9%B2%E7%9F%A5%E9%81%93%E7%9A%84%E6%96%B0%E7%89%B9%E6%80%A7%E5%90%AB-8.5&quot;&gt;4. PHP 8.x 你該知道的新特性（含 8.5）&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/what-is-php/#5.-%E6%80%8E%E9%BA%BC%E9%96%8B%E5%A7%8B%E7%92%B0%E5%A2%83%E7%AC%AC%E4%B8%80%E6%94%AF%E7%A8%8B%E5%BC%8F%E5%AD%B8%E7%BF%92%E8%B7%AF%E5%BE%91&quot;&gt;5. 怎麼開始：環境、第一支程式、學習路徑&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/what-is-php/#PHP-%E5%B7%A5%E7%A8%8B%E5%B8%AB%E5%9C%A8%E5%81%9A%E4%BB%80%E9%BA%BC?%E5%B0%B1%E6%A5%AD%E6%80%8E%E9%BA%BC%E7%9C%8B&quot;&gt;PHP 工程師在做什麼？就業怎麼看&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/what-is-php/#%E5%B8%B8%E8%A6%8B%E5%95%8F%E9%A1%8C&quot;&gt;常見問題&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/what-is-php/#%E7%B5%90%E8%AA%9E&quot;&gt;結語&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/what-is-php/#%E5%8F%83%E8%80%83%E8%B3%87%E6%96%99&quot;&gt;參考資料&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;h2 id=&quot;1.-PHP-是什麼伺服器端腳本語言&quot;&gt;1. PHP 是什麼：伺服器端腳本語言&lt;/h2&gt;&lt;p&gt;PHP 早期名稱是 Personal Home Page，現在正式解釋為遞迴縮寫 &lt;strong&gt;PHP: Hypertext Preprocessor&lt;/strong&gt;。它是特別適合網頁開發的開源通用腳本語言，通常&lt;strong&gt;在伺服器上執行&lt;/strong&gt;。瀏覽器收到的通常是已經產生好的 HTML；資料庫查詢、登入驗證、表單處理，多半在伺服器端完成。&lt;/p&gt;&lt;pre class=&quot;language-php&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token php language-php&quot;&gt;&lt;span class=&quot;token delimiter important&quot;&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;&amp;lt;h1&gt;Hello PHP&amp;lt;/h1&gt;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;把上面存成 &lt;code&gt;index.php&lt;/code&gt;，用內建伺服器跑起來：&lt;/p&gt;&lt;pre class=&quot;language-sh&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;php &lt;span class=&quot;token parameter variable&quot;&gt;-S&lt;/span&gt; localhost:8000&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;開啟 &lt;code&gt;http://localhost:8000&lt;/code&gt;，就是最小的動態網頁。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;和前端 JavaScript 的差別（簡化）：&lt;/strong&gt;&lt;/p&gt;&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;&lt;/th&gt;&lt;th&gt;PHP&lt;/th&gt;&lt;th&gt;瀏覽器端 JavaScript&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;執行位置&lt;/td&gt;&lt;td&gt;伺服器&lt;/td&gt;&lt;td&gt;使用者裝置／瀏覽器&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;典型工作&lt;/td&gt;&lt;td&gt;資料庫、權限、API、產生 HTML&lt;/td&gt;&lt;td&gt;互動、動畫、SPA&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;部署&lt;/td&gt;&lt;td&gt;虛擬主機／VPS／容器皆常見&lt;/td&gt;&lt;td&gt;隨頁面下載&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;補充：JavaScript 也能在後端跑（Node.js），所以「語言名稱 ≠ 前後端」；PHP 則主要以網站後端與伺服器端腳本為人熟知。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;常見用途：&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;動態網站與 CMS（WordPress、Drupal、Joomla）&lt;/li&gt;&lt;li&gt;電商與會員系統&lt;/li&gt;&lt;li&gt;REST API、Webhook&lt;/li&gt;&lt;li&gt;檔案上傳、郵件、排程腳本&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;2.-為什麼還有人用-PHP&quot;&gt;2. 為什麼還有人用 PHP&lt;/h2&gt;&lt;h3 id=&quot;市占與生態&quot;&gt;市占與生態&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;伺服器端語言&lt;/strong&gt;：PHP 約 &lt;strong&gt;70.5%&lt;/strong&gt;（W3Techs，2026-08）&lt;/li&gt;&lt;li&gt;&lt;strong&gt;CMS&lt;/strong&gt;：WordPress 約佔全站 &lt;strong&gt;41.2%&lt;/strong&gt;（W3Techs，2026-08）&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;這些是會每日變動的調查數字，而且分母是「被 W3Techs 辨識出來的網站」，不是整個網路的戶口普查。市占高不代表「最適合每個專案」，但代表：主機支援、外掛、教學、接案需求都還在。&lt;/p&gt;&lt;h3 id=&quot;主機與上線成本&quot;&gt;主機與上線成本&lt;/h3&gt;&lt;p&gt;許多虛擬主機提供 PHP 與資料庫服務，部署傳統網站時不必先組一整套應用程式平台。Node.js 與 Python 當然也能部署到託管平台；只是不同供應商的支援方式、設定與成本不同，選之前要看清楚方案。延伸：&lt;a href=&quot;https://kamadiam.com/free-ssl-certificate/&quot;&gt;免費 SSL&lt;/a&gt;、&lt;a href=&quot;https://kamadiam.com/what-is-herd/&quot;&gt;Herd 本機環境&lt;/a&gt;。&lt;/p&gt;&lt;h3 id=&quot;現代-PHP-已不是「把-HTML-跟邏輯攪在一起」&quot;&gt;現代 PHP 已不是「把 HTML 跟邏輯攪在一起」&lt;/h3&gt;&lt;p&gt;PHP 8 系列加入許多現代語法與型別功能，也持續改善錯誤處理。主流框架（Laravel、Symfony）搭配 &lt;a href=&quot;https://kamadiam.com/php-composer-intro/&quot;&gt;Composer&lt;/a&gt; 依賴管理，讓大型專案比較容易維持結構；「比較容易」很重要，因為再好的工具也擋不住把所有程式塞進同一個檔案的熱情。&lt;/p&gt;&lt;h3 id=&quot;什麼時候不該硬選-PHP&quot;&gt;什麼時候不該硬選 PHP&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;即時協作、WebSocket 重度應用 → 可優先評估 Node.js 等事件驅動堆疊&lt;/li&gt;&lt;li&gt;資料科學、機器學習主力 → 通常會優先評估 Python 生態&lt;/li&gt;&lt;li&gt;純靜態內容、極致效能與維護單純 → 可考慮 &lt;a href=&quot;https://kamadiam.com/static-site-generators/&quot;&gt;靜態網站生成器&lt;/a&gt;／&lt;a href=&quot;https://kamadiam.com/11ty-generator/&quot;&gt;11ty&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;3.-PHP-vs-Node.js-vs-Python怎麼選&quot;&gt;3. PHP vs Node.js vs Python：怎麼選&lt;/h2&gt;&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;面向&lt;/th&gt;&lt;th&gt;PHP&lt;/th&gt;&lt;th&gt;Node.js&lt;/th&gt;&lt;th&gt;Python（Django／Flask）&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;架站／CMS&lt;/td&gt;&lt;td&gt;WordPress 生態成熟&lt;/td&gt;&lt;td&gt;常需自組或採 Headless&lt;/td&gt;&lt;td&gt;可，常見於 Django 等框架&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;共享主機支援&lt;/td&gt;&lt;td&gt;常見&lt;/td&gt;&lt;td&gt;取決於主機商&lt;/td&gt;&lt;td&gt;取決於主機商&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;即時應用&lt;/td&gt;&lt;td&gt;可做，非傳統強項&lt;/td&gt;&lt;td&gt;強&lt;/td&gt;&lt;td&gt;中&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;AI／資料分析&lt;/td&gt;&lt;td&gt;弱&lt;/td&gt;&lt;td&gt;中&lt;/td&gt;&lt;td&gt;強&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;學習曲線（網站）&lt;/td&gt;&lt;td&gt;低～中&lt;/td&gt;&lt;td&gt;中（非同步思維）&lt;/td&gt;&lt;td&gt;中&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;常見使用情境&lt;/td&gt;&lt;td&gt;CMS、傳統網站、後端&lt;/td&gt;&lt;td&gt;JavaScript 全端、即時服務&lt;/td&gt;&lt;td&gt;資料處理、後端、AI 原型&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;&lt;strong&gt;實務建議：&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;要維護或客製 WordPress、接中小企業站 → &lt;strong&gt;學 PHP&lt;/strong&gt;&lt;/li&gt;&lt;li&gt;要做 SPA + API、即時功能 → &lt;strong&gt;Node.js&lt;/strong&gt;（或 PHP API + 前端框架）&lt;/li&gt;&lt;li&gt;要做資料管線、AI 原型 → &lt;strong&gt;Python&lt;/strong&gt;，網站可另選&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;語言可以並存：很多團隊用 PHP 管內容站、用 Node／Python 管其他服務。&lt;/p&gt;&lt;h2 id=&quot;4.-PHP-8.x-你該知道的新特性含-8.5&quot;&gt;4. PHP 8.x 你該知道的新特性（含 8.5）&lt;/h2&gt;&lt;p&gt;不必一次背完；先知道「現代 PHP 長什麼樣」。&lt;/p&gt;&lt;h3 id=&quot;PHP-8.0–8.3-的代表性功能&quot;&gt;PHP 8.0–8.3 的代表性功能&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Named arguments&lt;/strong&gt;、&lt;strong&gt;union types&lt;/strong&gt;、&lt;strong&gt;match&lt;/strong&gt;、&lt;strong&gt;attributes&lt;/strong&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Constructor property promotion&lt;/strong&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Readonly properties／classes&lt;/strong&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;&lt;code&gt;#[&#92;Override]&lt;/code&gt;&lt;/strong&gt;（8.3）：覆寫父類方法時更安全&lt;/li&gt;&lt;li&gt;型別化 class constants（8.3）&lt;/li&gt;&lt;/ul&gt;&lt;h3 id=&quot;PHP-8.4–8.5-值得關注&quot;&gt;PHP 8.4–8.5 值得關注&lt;/h3&gt;&lt;p&gt;依 &lt;a href=&quot;https://www.php.net/releases/8_5_0.php&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;PHP 8.5 官方發布公告&lt;/a&gt;：&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Pipe operator &lt;code&gt;|&amp;gt;&lt;/code&gt;&lt;/strong&gt;：把巢狀呼叫改成由左到右的資料流，可讀性更好&lt;/li&gt;&lt;li&gt;&lt;strong&gt;內建 URI extension&lt;/strong&gt;：依 RFC 3986／WHATWG 解析與正規化 URL，減少脆弱的 &lt;code&gt;parse_url()&lt;/code&gt; 用法&lt;/li&gt;&lt;li&gt;&lt;strong&gt;&lt;code&gt;clone()&lt;/code&gt; 搭配屬性更新&lt;/strong&gt;：方便 readonly／value object 的 with-er 模式&lt;/li&gt;&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://www.php.net/manual/en/class.nodiscard.php&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;#[&#92;NoDiscard]&lt;/a&gt;&lt;/strong&gt;：提醒呼叫端不要忽略重要回傳值&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;範例（PHP 8.5）：&lt;/p&gt;&lt;pre class=&quot;language-php&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token php language-php&quot;&gt;&lt;span class=&quot;token delimiter important&quot;&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$result&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$input&lt;/span&gt;
	&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;trim&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&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 operator&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;strtolower&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&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 operator&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;htmlspecialchars&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&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&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;正式環境升級前，請對照框架支援（例如 Laravel／Symfony 版本說明）與測試套件。也要查看 &lt;a href=&quot;https://www.php.net/supported-versions.php&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;PHP 官方支援版本表&lt;/a&gt;，不要只因為版本號看起來很新就直接升級；程式碼不是牛奶，卻同樣有保存期限。&lt;/p&gt;&lt;h2 id=&quot;5.-怎麼開始環境第一支程式學習路徑&quot;&gt;5. 怎麼開始：環境、第一支程式、學習路徑&lt;/h2&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;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;macOS&lt;/td&gt;&lt;td&gt;&lt;a href=&quot;https://kamadiam.com/what-is-herd/&quot;&gt;Herd&lt;/a&gt;（最省事）或 &lt;a href=&quot;https://kamadiam.com/homebrew-tutorial/&quot;&gt;Homebrew&lt;/a&gt; &lt;code&gt;brew install php&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Windows&lt;/td&gt;&lt;td&gt;&lt;a href=&quot;https://herd.laravel.com/windows&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Herd for Windows&lt;/a&gt; 或 Chocolatey&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;只要試玩&lt;/td&gt;&lt;td&gt;官方二進位 + &lt;code&gt;php -S&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;Herd 教學：&lt;a href=&quot;https://kamadiam.com/herd-wordpress/&quot;&gt;用 Herd 架 WordPress&lt;/a&gt;。&lt;/p&gt;&lt;h3 id=&quot;五分鐘最小範例&quot;&gt;五分鐘最小範例&lt;/h3&gt;&lt;pre class=&quot;language-php&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token php language-php&quot;&gt;&lt;span class=&quot;token delimiter important&quot;&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;declare&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;strict_types&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&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 variable&quot;&gt;$name&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$_GET&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;name&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;訪客&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token delimiter important&quot;&gt;?&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token doctype&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;!&lt;/span&gt;&lt;span class=&quot;token doctype-tag&quot;&gt;DOCTYPE&lt;/span&gt; &lt;span class=&quot;token name&quot;&gt;html&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;html&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;lang&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;zh-Hant&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;head&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;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;charset&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;UTF-8&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;title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Hello&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;title&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;head&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;body&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;span class=&quot;token php language-php&quot;&gt;&lt;span class=&quot;token delimiter important&quot;&gt;&amp;lt;?=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;htmlspecialchars&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;ENT_QUOTES&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;UTF-8&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token delimiter important&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;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;body&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;html&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;/p&gt;&lt;ol&gt;&lt;li&gt;開啟 &lt;code&gt;declare(strict_types=1);&lt;/code&gt;&lt;/li&gt;&lt;li&gt;輸出到 HTML 前做跳脫（防 XSS）&lt;/li&gt;&lt;li&gt;密碼用 &lt;code&gt;password_hash()&lt;/code&gt;／&lt;code&gt;password_verify()&lt;/code&gt;，不要自己發明雜湊&lt;/li&gt;&lt;/ol&gt;&lt;h3 id=&quot;建議學習順序&quot;&gt;建議學習順序&lt;/h3&gt;&lt;ol&gt;&lt;li&gt;語法與陣列 → &lt;a href=&quot;https://kamadiam.com/php-basic-tutorial/&quot;&gt;PHP 新手入門練習&lt;/a&gt;&lt;/li&gt;&lt;li&gt;類別與物件 → &lt;a href=&quot;https://kamadiam.com/php-object-and-class/&quot;&gt;Class／Object&lt;/a&gt;、&lt;a href=&quot;https://kamadiam.com/php-access-modifiers/&quot;&gt;存取修飾符&lt;/a&gt;&lt;/li&gt;&lt;li&gt;例外處理 → &lt;a href=&quot;https://kamadiam.com/php-exception-handling/&quot;&gt;Exception&lt;/a&gt;&lt;/li&gt;&lt;li&gt;依賴管理 → &lt;a href=&quot;https://kamadiam.com/php-composer-intro/&quot;&gt;Composer&lt;/a&gt;&lt;/li&gt;&lt;li&gt;設計原則 → &lt;a href=&quot;https://kamadiam.com/solid-principles-in-php/&quot;&gt;SOLID&lt;/a&gt;&lt;/li&gt;&lt;li&gt;選一條產品路：WordPress 外掛／主題，或 Laravel 專案&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;編輯器推薦 &lt;a href=&quot;https://code.visualstudio.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;VS Code&lt;/a&gt;；字型可搭配 &lt;a href=&quot;https://kamadiam.com/firacode-tutorial/&quot;&gt;Fira Code&lt;/a&gt;。&lt;/p&gt;&lt;h2 id=&quot;PHP-工程師在做什麼?就業怎麼看&quot;&gt;PHP 工程師在做什麼？就業怎麼看&lt;/h2&gt;&lt;p&gt;典型工作內容：後端邏輯、資料庫、API、效能與安全、與前端協作、維護 CMS／電商。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;技能組合（實務向）：&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;PHP 8 + Composer&lt;/li&gt;&lt;li&gt;SQL（MySQL／MariaDB／PostgreSQL）&lt;/li&gt;&lt;li&gt;HTTP、REST、基本資安（XSS、CSRF、SQL Injection）&lt;/li&gt;&lt;li&gt;Git&lt;/li&gt;&lt;li&gt;至少一種框架或 CMS（Laravel 或 WordPress）&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;職涯方向：&lt;/strong&gt; 資深後端、全端、技術主管、接案／維護站、或轉架構。薪資取決於能力與產業，不取決於「會不會喊某語言已死」。台灣職缺可在 104 等平台搜「PHP」「Laravel」「WordPress」自行比對當下行情；本文不引用易過期的截圖數字。&lt;/p&gt;&lt;p&gt;知名產品與站點生態中仍可見 PHP（例如 WordPress.org、Wikipedia 等）；企業內部是否採用，要以職缺與專案為準，而非迷思。&lt;/p&gt;&lt;h2 id=&quot;常見問題&quot;&gt;常見問題&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Q：現在才學 PHP 會不會過時？&lt;/strong&gt;&lt;br&gt;A：若目標是架站、WordPress、中小企業維護或 Laravel，PHP 仍是合理選項。若目標是 AI 研究，通常應以 Python 為主、PHP 為輔；最後仍要看你要解決的問題，而不是語言排行榜。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Q：一定要先學完 OOP 才能碰 WordPress 嗎？&lt;/strong&gt;&lt;br&gt;A：不必。可先做主題／小外掛建立回饋，再回頭補 &lt;a href=&quot;https://kamadiam.com/php-object-and-class/&quot;&gt;物件導向&lt;/a&gt; 與安全觀念。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Q：PHP 7 還能用嗎？&lt;/strong&gt;&lt;br&gt;A：PHP 7 已不在 PHP 官方支援版本清單內，正式環境不應把它當成仍受支援的版本。請先確認相依套件與主機相容性，再規劃升級到仍受支援的 PHP 8.x；主機若卡在舊版，優先處理升級路徑，而不是繼續累積技術債。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Q：和靜態站怎麼選？&lt;/strong&gt;&lt;br&gt;A：內容少、更新不頻繁、要極致速度與低維護 → 靜態站（本站即 &lt;a href=&quot;https://kamadiam.com/wordpress-vs-11ty/&quot;&gt;11ty&lt;/a&gt;）。要後台、會員、大量編輯者 → PHP／CMS 仍合理。&lt;/p&gt;&lt;h2 id=&quot;結語&quot;&gt;結語&lt;/h2&gt;&lt;p&gt;學 PHP 的理由很務實：&lt;strong&gt;生態還在、主機選擇多、WordPress／Laravel 仍有大量使用情境，語言本身也持續更新到 8.5&lt;/strong&gt;。先掌握「伺服器端動態網頁」這個心智模型，再依目標選 CMS 或框架，比糾結「哪種語言最潮」有用。&lt;/p&gt;&lt;p&gt;下一步：&lt;/p&gt;&lt;ol&gt;&lt;li&gt;裝好環境（Herd 或 Homebrew）&lt;/li&gt;&lt;li&gt;跑完 &lt;a href=&quot;https://kamadiam.com/php-basic-tutorial/&quot;&gt;10 個 PHP 練習&lt;/a&gt;&lt;/li&gt;&lt;li&gt;決定路線：WordPress 實戰，或 Laravel 入門&lt;/li&gt;&lt;/ol&gt;&lt;blockquote&gt;&lt;p&gt;這世界只有兩種程式語言，一種是大家都在抱怨的，一種是沒人在用的。&lt;/p&gt;&lt;p&gt;&lt;cite&gt;— Bjarne Stroustrup&lt;/cite&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;有人抱怨，通常代表還有人在用——重點是你能不能用它把問題解決好。&lt;/p&gt;&lt;h2 id=&quot;參考資料&quot;&gt;參考資料&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://www.php.net/manual/en/introduction.php&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;PHP 官方手冊：Introduction&lt;/a&gt;：PHP 的定位、伺服器端執行方式與常見用途。&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://www.php.net/releases/8_5_0.php&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;PHP 8.5.0 官方發布公告&lt;/a&gt;：PHP 8.5 的新功能總覽。&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://www.php.net/manual/en/migration85.new-features.php&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;PHP 官方手冊：PHP 8.5 新功能&lt;/a&gt;：Pipe operator、URI extension、&lt;code&gt;clone()&lt;/code&gt; 與 &lt;code&gt;#[&#92;NoDiscard]&lt;/code&gt; 的細節。&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://www.php.net/supported-versions.php&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;PHP 官方支援版本&lt;/a&gt;：目前仍接受錯誤修正與資安更新的版本分支。&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://w3techs.com/technologies/details/pl-php&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;W3Techs：PHP 使用統計&lt;/a&gt;：PHP 在可辨識伺服器端語言網站中的每日使用比例。&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://w3techs.com/technologies/comparison/cm-wordpress&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;W3Techs：WordPress 使用統計&lt;/a&gt;：WordPress 在全站與已知 CMS 網站中的使用比例。&lt;/li&gt;&lt;/ul&gt;</content>
  </entry>
  <entry>
    <title>TypeScript 入門教學：5 分鐘零基礎快速上手</title>
    <link href="https://kamadiam.com/typescript-getting-started/" />
    <updated>2026-08-29T00:00:00Z</updated>
    <id>https://kamadiam.com/typescript-getting-started/</id>
    <content type="html">&lt;p&gt;歡迎來到 TypeScript 的世界。它不是把 JavaScript 重新煮一遍，而是在你寫程式時加上型別檢查，讓錯誤更早被發現。這篇文章會帶你在 macOS 建立基本環境、用 &lt;code&gt;tsc&lt;/code&gt; 編譯第一支程式，也會簡單比較幾種常見的執行方式。準備好咖啡——型別錯誤通常比咖啡更早提醒你醒來。&lt;/p&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/typescript-getting-started/#TypeScript-%E6%98%AF%E4%BB%80%E9%BA%BC?&quot;&gt;TypeScript 是什麼？&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/typescript-getting-started/#%E5%BB%BA%E7%AB%8B-TypeScript-%E9%96%8B%E7%99%BC%E7%92%B0%E5%A2%83&quot;&gt;建立 TypeScript 開發環境&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/typescript-getting-started/#%E5%AF%AB%E5%87%BA%E4%B8%A6%E7%B7%A8%E8%AD%AF%E7%AC%AC%E4%B8%80%E6%94%AF-TypeScript-%E7%A8%8B%E5%BC%8F&quot;&gt;寫出並編譯第一支 TypeScript 程式&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/typescript-getting-started/#tsc-%E5%B8%B8%E7%94%A8%E5%8A%9F%E8%83%BD&quot;&gt;tsc 常用功能&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/typescript-getting-started/#%E5%9F%B7%E8%A1%8C-TypeScript-%E7%9A%84%E5%B9%BE%E7%A8%AE%E6%96%B9%E5%BC%8F&quot;&gt;執行 TypeScript 的幾種方式&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/typescript-getting-started/#%E6%8E%A5%E4%B8%8B%E4%BE%86%E5%8F%AF%E4%BB%A5%E5%AD%B8%E4%BB%80%E9%BA%BC?&quot;&gt;接下來可以學什麼？&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/typescript-getting-started/#%E5%8F%83%E8%80%83%E8%B3%87%E6%96%99&quot;&gt;參考資料&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;h2 id=&quot;TypeScript-是什麼?&quot;&gt;TypeScript 是什麼？&lt;/h2&gt;&lt;p&gt;TypeScript 是 JavaScript 的語法超集。換句話說，合法的 JavaScript 通常也能放進 TypeScript 專案；而 TypeScript 額外提供型別系統與編譯期檢查。&lt;/p&gt;&lt;p&gt;瀏覽器和多數 JavaScript 執行環境最終仍然需要 JavaScript。因此，TypeScript 原始碼通常會先經過 TypeScript 編譯器（&lt;code&gt;tsc&lt;/code&gt;）或其他工具處理，再交給 JavaScript 執行環境。型別主要服務於開發階段，不會原封不動地留在輸出的 JavaScript 裡。&lt;/p&gt;&lt;h2 id=&quot;建立-TypeScript-開發環境&quot;&gt;建立 TypeScript 開發環境&lt;/h2&gt;&lt;p&gt;先準備 Node.js，再在專案裡安裝 TypeScript。把依賴裝在專案中，比全域安裝更容易固定版本，也比較不會出現「在我電腦明明可以」的經典劇情。&lt;/p&gt;&lt;h3 id=&quot;安裝-Node.js&quot;&gt;安裝 Node.js&lt;/h3&gt;&lt;p&gt;你可以從 &lt;a href=&quot;https://nodejs.org/en/download&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Node.js 官方網站&lt;/a&gt; 下載適合 macOS 的版本，也可以使用 Homebrew：&lt;/p&gt;&lt;pre class=&quot;language-sh&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;brew &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;node&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;確認 Node.js 與 npm 已經可以使用：&lt;/p&gt;&lt;pre class=&quot;language-sh&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token function&quot;&gt;node&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--version&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--version&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;建立專案並安裝-TypeScript&quot;&gt;建立專案並安裝 TypeScript&lt;/h3&gt;&lt;p&gt;先建立一個專案目錄，接著初始化 &lt;code&gt;package.json&lt;/code&gt;：&lt;/p&gt;&lt;pre class=&quot;language-sh&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token function&quot;&gt;mkdir&lt;/span&gt; typescript-demo
&lt;span class=&quot;token builtin class-name&quot;&gt;cd&lt;/span&gt; typescript-demo
&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; init &lt;span class=&quot;token parameter variable&quot;&gt;-y&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;把 TypeScript 安裝成開發依賴：&lt;/p&gt;&lt;pre class=&quot;language-sh&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; --save-dev typescript&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;如果你習慣使用 pnpm，也可以把最後一步改成：&lt;/p&gt;&lt;pre class=&quot;language-sh&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token function&quot;&gt;pnpm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt; --save-dev typescript&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;安裝在專案裡的 &lt;code&gt;tsc&lt;/code&gt; 不會直接成為全域命令。執行它時，可以透過 &lt;code&gt;npx&lt;/code&gt;（或 pnpm 的 &lt;code&gt;pnpm exec&lt;/code&gt;）使用目前專案的版本：&lt;/p&gt;&lt;pre class=&quot;language-sh&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;npx tsc &lt;span class=&quot;token parameter variable&quot;&gt;--version&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;初始化-tsconfig.json&quot;&gt;初始化 &lt;code&gt;tsconfig.json&lt;/code&gt;&lt;/h3&gt;&lt;p&gt;&lt;code&gt;tsconfig.json&lt;/code&gt; 是 TypeScript 專案的設定檔，會告訴編譯器要處理哪些檔案，以及要輸出什麼形式的 JavaScript。執行以下命令建立初始設定：&lt;/p&gt;&lt;pre class=&quot;language-sh&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;npx tsc &lt;span class=&quot;token parameter variable&quot;&gt;--init&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;你可以先使用一份精簡設定：&lt;/p&gt;&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;compilerOptions&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;target&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;ES2022&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;token property&quot;&gt;&quot;module&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;NodeNext&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;token property&quot;&gt;&quot;moduleResolution&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;NodeNext&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;token property&quot;&gt;&quot;strict&quot;&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 property&quot;&gt;&quot;outDir&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;dist&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;&quot;include&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 string&quot;&gt;&quot;src/**/*.ts&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;這裡的重點有三個：&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;target&lt;/code&gt; 指定輸出的 JavaScript 目標語法。&lt;/li&gt;&lt;li&gt;&lt;code&gt;module&lt;/code&gt; 與 &lt;code&gt;moduleResolution&lt;/code&gt; 讓 TypeScript 依照 Node.js 的現代模組規則解析程式。&lt;/li&gt;&lt;li&gt;&lt;code&gt;strict: true&lt;/code&gt; 開啟一組較嚴格的型別檢查選項，能在編譯階段抓出更多問題。&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;實際專案的設定會依 Node.js 版本、模組格式與建置工具而不同；上面這份設定適合作為使用現代 Node.js 的入門起點，不是所有專案的萬用答案。&lt;/p&gt;&lt;h2 id=&quot;寫出並編譯第一支-TypeScript-程式&quot;&gt;寫出並編譯第一支 TypeScript 程式&lt;/h2&gt;&lt;p&gt;建立 &lt;code&gt;src/hello.ts&lt;/code&gt;：&lt;/p&gt;&lt;pre class=&quot;language-typescript&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; message&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Hello, TypeScript!&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token builtin&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;message&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;/p&gt;&lt;pre class=&quot;language-sh&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;npx tsc&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;編譯器會依照 &lt;code&gt;tsconfig.json&lt;/code&gt; 的設定，把輸出放到 &lt;code&gt;dist&lt;/code&gt; 目錄。你可以執行產生的 JavaScript：&lt;/p&gt;&lt;pre class=&quot;language-sh&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token function&quot;&gt;node&lt;/span&gt; dist/hello.js&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;tsc&lt;/code&gt; 不只是把副檔名換掉。它會根據設定檢查型別，並把 TypeScript 語法轉換成目標環境能理解的 JavaScript。假設你把字串傳給需要數字的函式，編譯時就可能收到錯誤，而不是等到使用者按下按鈕後才一起尖叫。&lt;/p&gt;&lt;h2 id=&quot;tsc-常用功能&quot;&gt;&lt;code&gt;tsc&lt;/code&gt; 常用功能&lt;/h2&gt;&lt;h3 id=&quot;編譯並檢查&quot;&gt;編譯並檢查&lt;/h3&gt;&lt;p&gt;直接執行 &lt;code&gt;npx tsc&lt;/code&gt; 會依照專案的 &lt;code&gt;tsconfig.json&lt;/code&gt; 編譯。若只想檢查型別、不產生輸出，可以使用：&lt;/p&gt;&lt;pre class=&quot;language-sh&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;npx tsc &lt;span class=&quot;token parameter variable&quot;&gt;--noEmit&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;這很適合放在提交程式碼前或持續整合流程中。&lt;/p&gt;&lt;h3 id=&quot;指定單一檔案&quot;&gt;指定單一檔案&lt;/h3&gt;&lt;p&gt;你也可以直接指定檔案：&lt;/p&gt;&lt;pre class=&quot;language-sh&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;npx tsc src/hello.ts&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;不過要注意，命令列直接指定輸入檔案時，可能不會按照你預期使用專案完整的檔案清單與設定。正式專案通常讓 &lt;code&gt;tsconfig.json&lt;/code&gt; 管理編譯範圍，再執行不帶檔名的 &lt;code&gt;npx tsc&lt;/code&gt;，行為會比較一致。&lt;/p&gt;&lt;h2 id=&quot;執行-TypeScript-的幾種方式&quot;&gt;執行 TypeScript 的幾種方式&lt;/h2&gt;&lt;p&gt;完成編譯後，最單純的方法是用 Node.js 執行輸出的 JavaScript。除此之外，也有適合不同情境的工具。它們各有個性，像咖啡豆一樣；重點不是哪一種最潮，而是哪一種適合你的專案。&lt;/p&gt;&lt;h3 id=&quot;使用-Node.js&quot;&gt;使用 Node.js&lt;/h3&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://kamadiam.com/img/built/lBC9QFhBEe-240.webp 240w&quot; sizes=&quot;(max-width: 768px) 100vw, 65ch&quot;&gt;&lt;img src=&quot;https://kamadiam.com/img/built/lBC9QFhBEe-240.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;240&quot; height=&quot;70&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;Node.js 執行的是 JavaScript。為了讓行為跨版本、跨設定更容易理解，入門時建議先用 &lt;code&gt;tsc&lt;/code&gt; 編譯，再執行輸出的檔案：&lt;/p&gt;&lt;pre class=&quot;language-sh&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;npx tsc
&lt;span class=&quot;token function&quot;&gt;node&lt;/span&gt; dist/hello.js&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;使用-ts-node&quot;&gt;使用 ts-node&lt;/h3&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://kamadiam.com/img/built/39m0sj94DQ-240.webp 240w, https://kamadiam.com/img/built/39m0sj94DQ-320.webp 320w, https://kamadiam.com/img/built/39m0sj94DQ-480.webp 480w, https://kamadiam.com/img/built/39m0sj94DQ-640.webp 640w, https://kamadiam.com/img/built/39m0sj94DQ-672.webp 672w, https://kamadiam.com/img/built/39m0sj94DQ-800.webp 800w, https://kamadiam.com/img/built/39m0sj94DQ-1200.webp 1200w&quot; sizes=&quot;(max-width: 768px) 100vw, 65ch&quot;&gt;&lt;img src=&quot;https://kamadiam.com/img/built/39m0sj94DQ-240.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;455&quot; srcset=&quot;https://kamadiam.com/img/built/39m0sj94DQ-240.png 240w, https://kamadiam.com/img/built/39m0sj94DQ-320.png 320w, https://kamadiam.com/img/built/39m0sj94DQ-480.png 480w, https://kamadiam.com/img/built/39m0sj94DQ-640.png 640w, https://kamadiam.com/img/built/39m0sj94DQ-672.png 672w, https://kamadiam.com/img/built/39m0sj94DQ-800.png 800w, https://kamadiam.com/img/built/39m0sj94DQ-1200.png 1200w&quot; sizes=&quot;(max-width: 768px) 100vw, 65ch&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;ts-node&lt;/code&gt; 是一個常見的 TypeScript 執行工具，能在開發時協助你直接執行 &lt;code&gt;.ts&lt;/code&gt; 檔案。它適合快速測試與腳本，但正式部署仍要依專案的建置方式決定。&lt;/p&gt;&lt;p&gt;在專案中安裝 &lt;code&gt;ts-node&lt;/code&gt; 與 TypeScript：&lt;/p&gt;&lt;pre class=&quot;language-sh&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; --save-dev ts-node typescript&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;使用專案內的執行檔：&lt;/p&gt;&lt;pre class=&quot;language-sh&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;npx ts-node src/hello.ts&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;TypeScript-Playground&quot;&gt;TypeScript Playground&lt;/h3&gt;&lt;p&gt;如果你暫時不想安裝任何東西，&lt;a href=&quot;https://www.typescriptlang.org/play&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;TypeScript Playground&lt;/a&gt; 是很方便的線上沙盒。你可以貼上程式碼，查看型別錯誤與編譯後的 JavaScript，適合用來驗證小範例。&lt;/p&gt;&lt;p&gt;它不是完整的本機專案環境，但用來試驗語法很夠用。先在 Playground 把概念煮熟，再搬回專案，通常比直接在正式環境亂加鹽安全。&lt;/p&gt;&lt;h3 id=&quot;Deno&quot;&gt;Deno&lt;/h3&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://kamadiam.com/img/built/3AYMBX_Ib5-240.webp 240w&quot; sizes=&quot;(max-width: 768px) 100vw, 65ch&quot;&gt;&lt;img src=&quot;https://kamadiam.com/img/built/3AYMBX_Ib5-240.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;240&quot; height=&quot;92&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;Deno 是另一個 JavaScript 與 TypeScript 執行環境，提供自己的工具鏈。請依照 &lt;a href=&quot;https://docs.deno.com/runtime/getting_started/installation/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Deno 官方安裝文件&lt;/a&gt; 安裝，不要把 &lt;code&gt;deno&lt;/code&gt; 當成一般 npm 套件用 &lt;code&gt;pnpm add -g&lt;/code&gt; 安裝。&lt;/p&gt;&lt;p&gt;建立 &lt;code&gt;hello.ts&lt;/code&gt;：&lt;/p&gt;&lt;pre class=&quot;language-typescript&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// hello.ts&lt;/span&gt;
&lt;span class=&quot;token builtin&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Hello from Deno!&#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;/code&gt;&lt;/pre&gt;&lt;p&gt;接著執行：&lt;/p&gt;&lt;pre class=&quot;language-sh&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;deno run hello.ts&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;Bun&quot;&gt;Bun&lt;/h3&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://kamadiam.com/img/built/0rvCce6ac4-240.webp 240w&quot; sizes=&quot;(max-width: 768px) 100vw, 65ch&quot;&gt;&lt;img src=&quot;https://kamadiam.com/img/built/0rvCce6ac4-240.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;240&quot; height=&quot;92&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;Bun 是另一個 JavaScript 執行環境，也提供執行 TypeScript、套件管理與建置等工具。請依照 &lt;a href=&quot;https://bun.sh/docs/installation&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Bun 官方安裝文件&lt;/a&gt; 安裝；它是獨立的執行環境，不是用 &lt;code&gt;pnpm add bun -g&lt;/code&gt; 取得的 npm 套件。&lt;/p&gt;&lt;p&gt;建立 &lt;code&gt;hello.ts&lt;/code&gt;：&lt;/p&gt;&lt;pre class=&quot;language-typescript&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// hello.ts&lt;/span&gt;
&lt;span class=&quot;token builtin&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Hello from Bun!&#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;/code&gt;&lt;/pre&gt;&lt;p&gt;使用 Bun 執行：&lt;/p&gt;&lt;pre class=&quot;language-sh&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;bun run hello.ts&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;接下來可以學什麼?&quot;&gt;接下來可以學什麼？&lt;/h2&gt;&lt;p&gt;現在你已經走過基本流程：&lt;/p&gt;&lt;ol&gt;&lt;li&gt;安裝 Node.js 與專案內的 TypeScript。&lt;/li&gt;&lt;li&gt;用 &lt;code&gt;tsconfig.json&lt;/code&gt; 設定編譯行為。&lt;/li&gt;&lt;li&gt;用 &lt;code&gt;tsc&lt;/code&gt; 檢查並輸出 JavaScript。&lt;/li&gt;&lt;li&gt;視情境選擇 Node.js、&lt;code&gt;ts-node&lt;/code&gt;、Deno 或 Bun。&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;下一步可以學習基本型別、函式型別、物件型別、泛型，以及如何在實際 JavaScript 專案中逐步加入 TypeScript。先從能讓程式更容易理解的型別開始，不必一口氣把整本手冊吞下去——那樣連編譯器都會替你噎到。&lt;/p&gt;&lt;h2 id=&quot;參考資料&quot;&gt;參考資料&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://www.typescriptlang.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;TypeScript 官方網站&lt;/a&gt;：TypeScript 的語言介紹與最新文件入口。&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/intro.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;TypeScript Handbook&lt;/a&gt;：型別系統、編譯器與語言功能的正式教學。&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://www.typescriptlang.org/tsconfig/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;TypeScript &lt;code&gt;tsconfig.json&lt;/code&gt; 參考&lt;/a&gt;：查詢編譯器選項與設定方式。&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nodejs.org/en/download&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Node.js 官方下載頁&lt;/a&gt;：取得 Node.js 與查看支援的安裝方式。&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://docs.deno.com/runtime/getting_started/installation/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Deno 安裝文件&lt;/a&gt;：Deno 執行環境的官方安裝指引。&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://bun.sh/docs/installation&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Bun 安裝文件&lt;/a&gt;：Bun 執行環境的官方安裝指引。&lt;/li&gt;&lt;/ul&gt;</content>
  </entry>
  <entry>
    <title>文章目錄 TOC 怎麼做？SEO 最佳實踐 + WordPress／11ty 三種做法</title>
    <link href="https://kamadiam.com/table-of-contents/" />
    <updated>2026-08-29T00:00:00Z</updated>
    <id>https://kamadiam.com/table-of-contents/</id>
    <content type="html">&lt;p&gt;長文沒有目錄，讀者就得一路往下捲，像在網頁裡玩「找不同」：不同的是重點到底藏在哪裡。&lt;strong&gt;Table of Contents（TOC，文章目錄）&lt;/strong&gt; 能把章節標題變成頁內連結，讓讀者先看地圖，再決定要走哪條路。&lt;/p&gt;&lt;p&gt;這篇讀完，你會知道 TOC 對閱讀體驗與 SEO 能幫多少、如何用原生 HTML 做出穩定錨點，以及靜態網站、JavaScript 和 WordPress 各自適合什麼情境。也會看到本站用 &lt;code&gt;[[toc]]&lt;/code&gt; 時，建置流程實際做了什麼。&lt;/p&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/table-of-contents/#TOC-%E5%88%B0%E5%BA%95%E8%A7%A3%E6%B1%BA%E4%BB%80%E9%BA%BC%E5%95%8F%E9%A1%8C&quot;&gt;TOC 到底解決什麼問題&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/table-of-contents/#%E5%85%88%E6%8A%8A%E6%A8%99%E9%A1%8C%E5%92%8C%E9%8C%A8%E9%BB%9E%E5%81%9A%E5%A5%BD&quot;&gt;先把標題和錨點做好&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/table-of-contents/#%E4%B8%89%E7%A8%AE-TOC-%E5%AF%A6%E4%BD%9C%E6%96%B9%E5%BC%8F&quot;&gt;三種 TOC 實作方式&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/table-of-contents/#%E4%B8%80%E5%80%8B%E5%8F%AF%E7%9B%B4%E6%8E%A5%E6%B8%AC%E8%A9%A6%E7%9A%84%E5%AE%8C%E6%95%B4%E7%AF%84%E4%BE%8B&quot;&gt;一個可直接測試的完整範例&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/table-of-contents/#2026-%E5%B9%B4%E5%AF%A6%E5%8B%99%E6%AA%A2%E6%9F%A5%E6%B8%85%E5%96%AE&quot;&gt;2026 年實務檢查清單&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/table-of-contents/#%E5%B8%B8%E8%A6%8B%E5%95%8F%E9%A1%8C&quot;&gt;常見問題&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/table-of-contents/#%E7%B5%90%E8%AA%9E&quot;&gt;結語&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/table-of-contents/#%E5%8F%83%E8%80%83%E8%B3%87%E6%96%99&quot;&gt;參考資料&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;h2 id=&quot;TOC-到底解決什麼問題&quot;&gt;TOC 到底解決什麼問題&lt;/h2&gt;&lt;h3 id=&quot;先解決讀者的問題&quot;&gt;先解決讀者的問題&lt;/h3&gt;&lt;p&gt;一份好的 TOC 至少有三個用途：&lt;/p&gt;&lt;ul&gt;&lt;li&gt;讓讀者快速掃過文章範圍，判斷內容是否值得讀&lt;/li&gt;&lt;li&gt;讓讀者直接跳到需要的章節，少滑幾個螢幕&lt;/li&gt;&lt;li&gt;讓讀者複製頁面網址後面的 &lt;code&gt;#錨點&lt;/code&gt;，把特定段落分享給別人&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;這些是使用者體驗上的好處，不是「放上 TOC，排名立刻升天」的魔法。SEO 沒有魔法按鈕，不然大家早就把鍵盤換成水晶球了。&lt;/p&gt;&lt;h3 id=&quot;SEO-能幫多少務實版&quot;&gt;SEO 能幫多少：務實版&lt;/h3&gt;&lt;p&gt;Google 並沒有保證「加入 TOC 就會排名更高」。比較可靠的做法，是把 TOC 做成一般 HTML 連結，並讓文章有清楚的標題結構：&lt;/p&gt;&lt;ul&gt;&lt;li&gt;用一個主要標題，再用 H2、H3 分出章節與小節&lt;/li&gt;&lt;li&gt;使用真正的 &lt;code&gt;&amp;lt;a href=&amp;quot;#section-id&amp;quot;&amp;gt;&lt;/code&gt;，而不是只有 JavaScript 點擊事件&lt;/li&gt;&lt;li&gt;讓連結文字描述目標內容，不要全部都叫「點這裡」&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Google 的文件指出，通常需要有 &lt;code&gt;href&lt;/code&gt; 的 &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; 元素，搜尋系統才容易擷取連結；至於搜尋結果是否顯示頁內跳轉連結，仍由搜尋系統自動判斷，不能手動保證。&lt;/p&gt;&lt;p&gt;另外，別把頁內跳轉（jump link）和網站導覽列裡的 sitelinks 混在一起。Google 已宣布從 2024 年 11 月 21 日起移除搜尋結果中的 sitelinks search box 視覺元素，但這不等於一般 sitelinks 或頁內錨點消失。&lt;/p&gt;&lt;h2 id=&quot;先把標題和錨點做好&quot;&gt;先把標題和錨點做好&lt;/h2&gt;&lt;p&gt;TOC 只是導航，不會替混亂的內容結構打掃房間。實作前先確認：&lt;/p&gt;&lt;ol&gt;&lt;li&gt;一篇文章有一個主要 H1；本站的版型會處理文章標題。&lt;/li&gt;&lt;li&gt;大章節用 H2，小節用 H3，層級不要從 H2 突然跳到 H4。&lt;/li&gt;&lt;li&gt;每個目標標題都有唯一且穩定的 &lt;code&gt;id&lt;/code&gt;。&lt;/li&gt;&lt;li&gt;TOC 的 &lt;code&gt;href&lt;/code&gt; 必須和目標標題的 &lt;code&gt;id&lt;/code&gt; 完全相同。&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;最小 HTML 例子如下：&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;article&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;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;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;nav&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;aria-label&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 tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;ol&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;li&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;a&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;#intro&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;a&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;li&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;li&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;a&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;#setup&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;a&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;li&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;ol&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;nav&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;section&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&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;intro&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;h2&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;h2&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;p&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;p&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;section&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;section&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&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;setup&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;h2&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;h2&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;p&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;p&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;section&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;article&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;id&lt;/code&gt; 不能在同一頁重複。若兩個標題都叫「結語」，產生器就需要處理碰撞，例如輸出 &lt;code&gt;結語&lt;/code&gt; 和 &lt;code&gt;結語-1&lt;/code&gt;。手動指定 ID 時也一樣：改了標題後，要一起檢查既有連結，否則讀者只會被送到一個很有禮貌的 404——它什麼都沒做，但態度很好。&lt;/p&gt;&lt;h2 id=&quot;三種-TOC-實作方式&quot;&gt;三種 TOC 實作方式&lt;/h2&gt;&lt;h3 id=&quot;A.-建置時產生靜態-TOC&quot;&gt;A. 建置時產生靜態 TOC&lt;/h3&gt;&lt;p&gt;靜態網站最適合這種方式。Markdown 內文放一個標記：&lt;/p&gt;&lt;pre class=&quot;language-markdown&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-markdown&quot;&gt;[[toc]]&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;建置工具讀取標題，輸出一般 HTML 清單與錨點。這種做法不需要額外的前端 JavaScript，HTML 載入時就有內容，通常也比較容易處理無 JavaScript、無障礙和版面穩定性。&lt;/p&gt;&lt;p&gt;本站的 &lt;code&gt;[[toc]]&lt;/code&gt; 由 &lt;code&gt;markdown-it-table-of-contents&lt;/code&gt; 處理，而且目前設定只列出 H2。換句話說，這篇文章的 H3 會出現在正文裡，但不會自動列進本站的目錄；這是目前建置設定的結果，不是 Markdown 的宇宙定律。&lt;/p&gt;&lt;h3 id=&quot;B.-用-JavaScript-動態掃描標題&quot;&gt;B. 用 JavaScript 動態掃描標題&lt;/h3&gt;&lt;p&gt;如果頁面內容是執行後才出現，或需要摺疊目錄、標示目前章節，JavaScript 就很有用。不過它最好是「增強功能」，不要成為唯一的導航來源。&lt;/p&gt;&lt;p&gt;下面是一個不使用 &lt;code&gt;innerHTML&lt;/code&gt; 的簡化範例。使用 &lt;code&gt;textContent&lt;/code&gt; 和 &lt;code&gt;createElement&lt;/code&gt;，可以避免把標題文字直接當成 HTML 解析：&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;nav&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&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;table-of-contents&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;aria-label&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 tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h2&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;h2&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;ol&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&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;toc-list&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;ol&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;nav&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-javascript&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; tocList &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;#toc-list&#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 keyword&quot;&gt;const&lt;/span&gt; headings &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;querySelectorAll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;article h2&#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 keyword&quot;&gt;const&lt;/span&gt; usedIds &lt;span class=&quot;token operator&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;Set&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;

headings&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 punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;heading&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; index&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;let&lt;/span&gt; id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; heading&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;section-&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;index &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; suffix &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;usedIds&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;has&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id&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;
		id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;heading&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;section-&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;index &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;suffix&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		suffix &lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&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;

	heading&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	usedIds&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id&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;const&lt;/span&gt; item &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;li&#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 keyword&quot;&gt;const&lt;/span&gt; link &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;a&#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;
	link&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;href &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;id&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	link&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;textContent &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; heading&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;textContent&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	item&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;link&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	tocList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;item&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;這段程式的重點不是「看到 H2 就開心地生一堆 &lt;code&gt;&amp;lt;li&amp;gt;&lt;/code&gt;」，而是確保每個連結都有 &lt;code&gt;href&lt;/code&gt;，每個目標都有唯一 ID。若頁面內容在這段程式執行後才載入，就需要把建立目錄的時機放到內容完成後；不要只在 &lt;code&gt;DOMContentLoaded&lt;/code&gt; 時猜測內容已經全部出現。&lt;/p&gt;&lt;h3 id=&quot;C.-使用-WordPress-外掛或區塊&quot;&gt;C. 使用 WordPress 外掛或區塊&lt;/h3&gt;&lt;p&gt;WordPress 可以使用 TOC 外掛，或由佈景主題／編輯器提供目錄區塊。選擇時先看這些條件：&lt;/p&gt;&lt;ul&gt;&lt;li&gt;能否排除不想出現的標題，例如「參考資料」&lt;/li&gt;&lt;li&gt;輸出的連結是否是真正的 &lt;code&gt;&amp;lt;a href=&amp;quot;#...&amp;quot;&amp;gt;&lt;/code&gt;&lt;/li&gt;&lt;li&gt;是否能處理重複標題並產生唯一 ID&lt;/li&gt;&lt;li&gt;手機版是否容易操作，且不會載入不必要的腳本與樣式&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;外掛功能越多不代表越適合。若只需要一份簡單目錄，卻順便載入一套動畫、三個 CSS 檔和一位不知道在做什麼的 JavaScript，這就有點像為了煮咖啡買了一台工業鍋爐。&lt;/p&gt;&lt;h2 id=&quot;一個可直接測試的完整範例&quot;&gt;一個可直接測試的完整範例&lt;/h2&gt;&lt;p&gt;你也可以&lt;a href=&quot;https://kamadiam.com/counter-example/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;開啟 CSS Counter 列表示範頁&lt;/a&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 doctype&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;!&lt;/span&gt;&lt;span class=&quot;token doctype-tag&quot;&gt;doctype&lt;/span&gt; &lt;span class=&quot;token name&quot;&gt;html&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;html&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;lang&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;zh-Hant&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;head&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;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;charset&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;utf-8&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;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&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;viewport&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;content&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;width=device-width, initial-scale=1&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;title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;TOC 示範&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;title&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;head&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;body&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;article&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;TOC 示範文章&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;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;nav&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;aria-label&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 tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h2&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;h2&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;ol&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;li&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;a&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;#intro&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;a&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;li&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;li&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;a&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;#practice&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;a&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;li&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;ol&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;nav&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;section&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&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;intro&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;h2&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;h2&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;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;TOC 先提供路線圖，讀者再決定要不要出發。&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;p&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;section&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;section&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&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;practice&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;h2&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;h2&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;p&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;p&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;section&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;article&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;body&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;html&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;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://kamadiam.com/img/built/BZPEs2ynMW-240.webp 240w, https://kamadiam.com/img/built/BZPEs2ynMW-320.webp 320w, https://kamadiam.com/img/built/BZPEs2ynMW-480.webp 480w, https://kamadiam.com/img/built/BZPEs2ynMW-640.webp 640w, https://kamadiam.com/img/built/BZPEs2ynMW-672.webp 672w, https://kamadiam.com/img/built/BZPEs2ynMW-800.webp 800w, https://kamadiam.com/img/built/BZPEs2ynMW-1200.webp 1200w&quot; sizes=&quot;(max-width: 768px) 100vw, 65ch&quot;&gt;&lt;img src=&quot;https://kamadiam.com/img/built/BZPEs2ynMW-240.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;421&quot; srcset=&quot;https://kamadiam.com/img/built/BZPEs2ynMW-240.png 240w, https://kamadiam.com/img/built/BZPEs2ynMW-320.png 320w, https://kamadiam.com/img/built/BZPEs2ynMW-480.png 480w, https://kamadiam.com/img/built/BZPEs2ynMW-640.png 640w, https://kamadiam.com/img/built/BZPEs2ynMW-672.png 672w, https://kamadiam.com/img/built/BZPEs2ynMW-800.png 800w, https://kamadiam.com/img/built/BZPEs2ynMW-1200.png 1200w&quot; sizes=&quot;(max-width: 768px) 100vw, 65ch&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;h2 id=&quot;2026-年實務檢查清單&quot;&gt;2026 年實務檢查清單&lt;/h2&gt;&lt;p&gt;發布前可以逐項確認：&lt;/p&gt;&lt;ul&gt;&lt;li&gt;[ ] H1、H2、H3 層級符合內容結構，沒有為了字體大小亂用標題&lt;/li&gt;&lt;li&gt;[ ] 每個 TOC 連結都有 &lt;code&gt;href=&amp;quot;#...&amp;quot;&lt;/code&gt;，目標標題有對應且唯一的 &lt;code&gt;id&lt;/code&gt;&lt;/li&gt;&lt;li&gt;[ ] 目錄文字和目標標題一致，讀者不需要玩猜謎遊戲&lt;/li&gt;&lt;li&gt;[ ] 靜態網站優先在建置時輸出；JavaScript 只負責互動增強&lt;/li&gt;&lt;li&gt;[ ] 目錄容器有 &lt;code&gt;nav&lt;/code&gt; 和 &lt;code&gt;aria-label&lt;/code&gt;，讓輔助科技知道它是導航&lt;/li&gt;&lt;li&gt;[ ] 手機版容易收合或掃讀，不會比文章本身還有存在感&lt;/li&gt;&lt;li&gt;[ ] 已實際點過每個連結，也測過重新整理後的 &lt;code&gt;#錨點&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;結構化資料可以協助搜尋系統理解頁面，但不能取代清楚的標題、內文和可操作的連結。最實際的 SEO 策略通常不華麗：先讓人找得到、讀得懂、跳得準，再期待搜尋系統看懂。&lt;/p&gt;&lt;h2 id=&quot;常見問題&quot;&gt;常見問題&lt;/h2&gt;&lt;h3 id=&quot;TOC-一定要放在文章最前面嗎?&quot;&gt;TOC 一定要放在文章最前面嗎？&lt;/h3&gt;&lt;p&gt;長文通常放在引言後最方便讀者掃描；如果是桌面版側欄，也可以固定在旁邊。重點是目錄要在 HTML 中真實存在，且連結能直接跳到內容。&lt;/p&gt;&lt;h3 id=&quot;中文標題的-id-應該怎麼產生?&quot;&gt;中文標題的 &lt;code&gt;id&lt;/code&gt; 應該怎麼產生？&lt;/h3&gt;&lt;p&gt;可以使用可讀的中文 ID、拼音，或其他穩定的 slug。選哪一種不是重點，重點是產生規則要穩定、結果不能重複，而且 TOC 和標題必須使用同一套規則。本站允許中文 slug，並讓 TOC 與標題 ID 共用自訂 slugify。&lt;/p&gt;&lt;h3 id=&quot;要不要把-H3-也放進目錄?&quot;&gt;要不要把 H3 也放進目錄？&lt;/h3&gt;&lt;p&gt;看文章長度與目錄深度。小節很多時，H3 會讓目錄變成另一篇文章；小節較少時，列出 H3 則能幫助讀者定位。本站目前只輸出 H2，因此若需要 H3 目錄，必須先調整建置設定，再同步檢查樣式與行動版體驗。&lt;/p&gt;&lt;h3 id=&quot;TOC-會不會和其他外掛衝突?&quot;&gt;TOC 會不會和其他外掛衝突？&lt;/h3&gt;&lt;p&gt;可能會。最常見的問題是兩個工具同時替標題產生 ID，結果一個工具改完，另一個工具的連結就失效。建議由單一工具負責 ID，並在建置後檢查 HTML 中是否有重複 ID 或找不到的 &lt;code&gt;href&lt;/code&gt;。&lt;/p&gt;&lt;h2 id=&quot;結語&quot;&gt;結語&lt;/h2&gt;&lt;p&gt;TOC 的核心價值很簡單：讓讀者更快找到重點，也讓頁面結構更容易被理解。靜態站通常優先選建置時產生的原生 HTML；需要高亮、摺疊或動態內容時，再讓 JavaScript 上場；WordPress 則要慎選外掛，別讓目錄為了五行功能背上一整個工具箱。&lt;/p&gt;&lt;p&gt;如果你正在走靜態網站路線，可以接著閱讀：&lt;a href=&quot;https://kamadiam.com/11ty-generator/&quot;&gt;11ty 入門&lt;/a&gt; 和 &lt;a href=&quot;https://kamadiam.com/pagespeed-core-web-vitals/&quot;&gt;PageSpeed 與 Core Web Vitals&lt;/a&gt;。文章審完、錨點點完，再來一杯咖啡——這次不必讓咖啡也建立一個 TOC。&lt;/p&gt;&lt;h2 id=&quot;參考資料&quot;&gt;參考資料&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://developers.google.com/search/docs/fundamentals/seo-starter-guide&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Google Search Central：SEO Starter Guide&lt;/a&gt;：說明可讀、分段並使用標題組織內容的基本原則。&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://developers.google.com/search/docs/crawling-indexing/links-crawlable&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Google Search Central：Make your links crawlable&lt;/a&gt;：說明搜尋系統如何擷取帶有 &lt;code&gt;href&lt;/code&gt; 的原生連結，以及描述性連結文字的重要性。&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://developers.google.com/search/blog/2024/10/sitelinks-search-box&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Google Search Central：Farewell, Sitelinks Search Box&lt;/a&gt;：說明 sitelinks search box 於 2024 年 11 月 21 日起移除，以及這項變更不影響一般 sitelinks。&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/id&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;MDN：HTML &lt;code&gt;id&lt;/code&gt; global attribute&lt;/a&gt;：說明 &lt;code&gt;id&lt;/code&gt; 的唯一性與在頁面內建立片段識別字的用途。&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://www.11ty.dev/docs/languages/markdown/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;11ty 文件：Markdown&lt;/a&gt;：說明 Eleventy 如何處理 Markdown，以及建置流程在靜態網站中的角色。&lt;/li&gt;&lt;/ul&gt;</content>
  </entry>
  <entry>
    <title>Tab Manager Plus 教學：Chrome 分頁管理終極指南</title>
    <link href="https://kamadiam.com/tab-management/" />
    <updated>2026-08-29T00:00:00Z</updated>
    <id>https://kamadiam.com/tab-management/</id>
    <content type="html">&lt;p&gt;你是不是也有過這種經驗：只是想找一個分頁，卻得在分頁列裡滑到天荒地老，最後連自己要找什麼都忘了？分頁開太多時，瀏覽器的工作列看起來就像咖啡店打烊前的桌面——每一杯都像重要的，但沒人知道哪一杯是你的。&lt;/p&gt;&lt;p&gt;這篇會介紹兩款 Chrome 分頁管理擴充功能：Tab Manager Plus 與 Tab Manager Auto。讀完後，你會知道它們各自適合什麼情境、能處理哪些工作，以及如何選擇；不必再用「關掉全部分頁，靠記憶重新開始」這種有點悲壯的整理法。&lt;/p&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/tab-management/#%E5%85%88%E8%AA%8D%E8%AD%98-Chrome-%E7%9A%84%E5%88%86%E9%A0%81%E6%95%B4%E7%90%86%E6%96%B9%E5%BC%8F&quot;&gt;先認識 Chrome 的分頁整理方式&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/tab-management/#Tab-Manager-Plus-for-Chrome%E5%BF%AB%E9%80%9F%E6%89%BE%E5%9B%9E%E5%88%86%E9%A0%81&quot;&gt;Tab Manager Plus for Chrome：快速找回分頁&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/tab-management/#Tab-Manager-Auto%E8%AE%93%E5%88%86%E9%A0%81%E6%8C%89%E7%B6%B2%E5%9F%9F%E8%87%AA%E5%8B%95%E5%88%86%E7%B5%84&quot;&gt;Tab Manager Auto：讓分頁按網域自動分組&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/tab-management/#Tab-Manager-Plus-%E8%88%87-Tab-Manager-Auto-%E6%80%8E%E9%BA%BC%E9%81%B8?&quot;&gt;Tab Manager Plus 與 Tab Manager Auto 怎麼選？&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/tab-management/#%E4%BD%BF%E7%94%A8%E5%89%8D%E7%9A%84%E5%85%A9%E5%80%8B%E6%8F%90%E9%86%92&quot;&gt;使用前的兩個提醒&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/tab-management/#%E7%B5%90%E8%AA%9E&quot;&gt;結語&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/tab-management/#%E5%8F%83%E8%80%83%E8%B3%87%E6%96%99&quot;&gt;參考資料&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;h2 id=&quot;先認識-Chrome-的分頁整理方式&quot;&gt;先認識 Chrome 的分頁整理方式&lt;/h2&gt;&lt;p&gt;在開始比較擴充功能前，先釐清問題會比較好。Chrome 本身已經支援分頁群組，可以替群組命名、選顏色、收合分頁，也能把分頁拖進群組。若你只需要偶爾整理幾個主題，內建功能可能已經夠用。&lt;/p&gt;&lt;p&gt;但當你同時開著工作、研究、購物和一堆「等等再看」的分頁時，手動拖曳就會變成另一種工作。這時，擴充功能的價值在於幫你快速找到分頁，或按照規則自動整理。&lt;/p&gt;&lt;h2 id=&quot;Tab-Manager-Plus-for-Chrome快速找回分頁&quot;&gt;Tab Manager Plus for Chrome：快速找回分頁&lt;/h2&gt;&lt;h3 id=&quot;它適合什麼情境?&quot;&gt;它適合什麼情境？&lt;/h3&gt;&lt;p&gt;&lt;a href=&quot;https://chrome.google.com/webstore/detail/tab-manager-plus-for-chro/cnkdjjdmfiffagllbiiilooaoofcoeff&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://kamadiam.com/img/built/VRwjtt4lH1-240.webp 240w, https://kamadiam.com/img/built/VRwjtt4lH1-320.webp 320w, https://kamadiam.com/img/built/VRwjtt4lH1-480.webp 480w, https://kamadiam.com/img/built/VRwjtt4lH1-640.webp 640w, https://kamadiam.com/img/built/VRwjtt4lH1-672.webp 672w, https://kamadiam.com/img/built/VRwjtt4lH1-800.webp 800w, https://kamadiam.com/img/built/VRwjtt4lH1-1076.webp 1076w&quot; sizes=&quot;(max-width: 768px) 100vw, 65ch&quot;&gt;&lt;img src=&quot;https://kamadiam.com/img/built/VRwjtt4lH1-240.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;1076&quot; height=&quot;314&quot; srcset=&quot;https://kamadiam.com/img/built/VRwjtt4lH1-240.png 240w, https://kamadiam.com/img/built/VRwjtt4lH1-320.png 320w, https://kamadiam.com/img/built/VRwjtt4lH1-480.png 480w, https://kamadiam.com/img/built/VRwjtt4lH1-640.png 640w, https://kamadiam.com/img/built/VRwjtt4lH1-672.png 672w, https://kamadiam.com/img/built/VRwjtt4lH1-800.png 800w, https://kamadiam.com/img/built/VRwjtt4lH1-1076.png 1076w&quot; sizes=&quot;(max-width: 768px) 100vw, 65ch&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Tab Manager Plus 的核心工作很直接：把目前開啟的分頁與視窗集中列出，讓你用標題或網址搜尋，而不是一直在分頁列裡左右滑。Chrome Web Store 的說明也把「快速尋找分頁、查看所有視窗、找出重複分頁、限制每個視窗的分頁數」列為主要功能。&lt;/p&gt;&lt;p&gt;換句話說，它比較像一個「分頁搜尋器」，而不是替你決定所有分頁要怎麼生活。這種工具很適合習慣自己分類、但不想自己找分頁的人。&lt;/p&gt;&lt;h3 id=&quot;主要功能&quot;&gt;主要功能&lt;/h3&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://kamadiam.com/img/built/M1gOKChu_Z-240.webp 240w, https://kamadiam.com/img/built/M1gOKChu_Z-320.webp 320w, https://kamadiam.com/img/built/M1gOKChu_Z-480.webp 480w, https://kamadiam.com/img/built/M1gOKChu_Z-640.webp 640w, https://kamadiam.com/img/built/M1gOKChu_Z-672.webp 672w, https://kamadiam.com/img/built/M1gOKChu_Z-800.webp 800w, https://kamadiam.com/img/built/M1gOKChu_Z-1200.webp 1200w&quot; sizes=&quot;(max-width: 768px) 100vw, 65ch&quot;&gt;&lt;img src=&quot;https://kamadiam.com/img/built/M1gOKChu_Z-240.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;902&quot; srcset=&quot;https://kamadiam.com/img/built/M1gOKChu_Z-240.png 240w, https://kamadiam.com/img/built/M1gOKChu_Z-320.png 320w, https://kamadiam.com/img/built/M1gOKChu_Z-480.png 480w, https://kamadiam.com/img/built/M1gOKChu_Z-640.png 640w, https://kamadiam.com/img/built/M1gOKChu_Z-672.png 672w, https://kamadiam.com/img/built/M1gOKChu_Z-800.png 800w, https://kamadiam.com/img/built/M1gOKChu_Z-1200.png 1200w&quot; sizes=&quot;(max-width: 768px) 100vw, 65ch&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;集中查看&lt;/strong&gt;：一次查看目前視窗中的分頁與視窗，少一點盲目搜尋。&lt;/li&gt;&lt;li&gt;&lt;strong&gt;搜尋標題與網址&lt;/strong&gt;：開啟管理介面後直接輸入關鍵字，縮小結果範圍。&lt;/li&gt;&lt;li&gt;&lt;strong&gt;標記重複分頁&lt;/strong&gt;：找出重複頁面後，再決定要保留、移動或關閉哪些分頁。&lt;/li&gt;&lt;li&gt;&lt;strong&gt;批次管理&lt;/strong&gt;：依商店說明，可選取多個分頁，進行移動、建立或刪除等操作。&lt;/li&gt;&lt;li&gt;&lt;strong&gt;限制分頁數量&lt;/strong&gt;：可以設定每個視窗的分頁上限；達到限制後，擴充功能會依其設定處理後續分頁。&lt;/li&gt;&lt;li&gt;&lt;strong&gt;隱身模式支援&lt;/strong&gt;：這不是安裝後就自動開啟的功能。需要到 Chrome 的擴充功能管理頁，針對該擴充功能開啟「允許在無痕模式中使用」。&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;如果你的痛點是「我明明開過，但它現在在哪裡」，Tab Manager Plus 通常比自動分組更對症。它不會替你安排人生，只負責把分頁叫到你面前——這已經比很多會議主持人可靠了。&lt;/p&gt;&lt;h2 id=&quot;Tab-Manager-Auto讓分頁按網域自動分組&quot;&gt;Tab Manager Auto：讓分頁按網域自動分組&lt;/h2&gt;&lt;h3 id=&quot;它的整理方式&quot;&gt;它的整理方式&lt;/h3&gt;&lt;p&gt;Tab Manager Auto 的方向不同。根據 Chrome Web Store 的功能說明，它會依分頁的來源網域自動分組，也能把最近使用的分頁排到該群組較前面的位置，並允許使用者自訂分組。&lt;/p&gt;&lt;p&gt;想像你開了 GitHub、文件網站和幾個測試頁面。你不必每次都手動拖曳，工具會嘗試依照網域把相關分頁集合起來。分頁終於學會排隊了，只是它不會幫你買咖啡。&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://chrome.google.com/webstore/detail/tab-manager-auto/mplpoddifhoaicmpbjgpfnbljcabibak&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://kamadiam.com/img/built/kZbBsFNSgZ-240.webp 240w, https://kamadiam.com/img/built/kZbBsFNSgZ-320.webp 320w, https://kamadiam.com/img/built/kZbBsFNSgZ-480.webp 480w, https://kamadiam.com/img/built/kZbBsFNSgZ-640.webp 640w, https://kamadiam.com/img/built/kZbBsFNSgZ-672.webp 672w&quot; sizes=&quot;(max-width: 768px) 100vw, 65ch&quot;&gt;&lt;img src=&quot;https://kamadiam.com/img/built/kZbBsFNSgZ-240.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;672&quot; height=&quot;185&quot; srcset=&quot;https://kamadiam.com/img/built/kZbBsFNSgZ-240.png 240w, https://kamadiam.com/img/built/kZbBsFNSgZ-320.png 320w, https://kamadiam.com/img/built/kZbBsFNSgZ-480.png 480w, https://kamadiam.com/img/built/kZbBsFNSgZ-640.png 640w, https://kamadiam.com/img/built/kZbBsFNSgZ-672.png 672w&quot; sizes=&quot;(max-width: 768px) 100vw, 65ch&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/p&gt;&lt;h3 id=&quot;主要功能-2&quot;&gt;主要功能&lt;/h3&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://kamadiam.com/img/built/mf3pvxVHAt-240.webp 240w, https://kamadiam.com/img/built/mf3pvxVHAt-320.webp 320w, https://kamadiam.com/img/built/mf3pvxVHAt-480.webp 480w, https://kamadiam.com/img/built/mf3pvxVHAt-640.webp 640w, https://kamadiam.com/img/built/mf3pvxVHAt-672.webp 672w, https://kamadiam.com/img/built/mf3pvxVHAt-800.webp 800w, https://kamadiam.com/img/built/mf3pvxVHAt-1200.webp 1200w&quot; sizes=&quot;(max-width: 768px) 100vw, 65ch&quot;&gt;&lt;img src=&quot;https://kamadiam.com/img/built/mf3pvxVHAt-240.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;42&quot; srcset=&quot;https://kamadiam.com/img/built/mf3pvxVHAt-240.png 240w, https://kamadiam.com/img/built/mf3pvxVHAt-320.png 320w, https://kamadiam.com/img/built/mf3pvxVHAt-480.png 480w, https://kamadiam.com/img/built/mf3pvxVHAt-640.png 640w, https://kamadiam.com/img/built/mf3pvxVHAt-672.png 672w, https://kamadiam.com/img/built/mf3pvxVHAt-800.png 800w, https://kamadiam.com/img/built/mf3pvxVHAt-1200.png 1200w&quot; sizes=&quot;(max-width: 768px) 100vw, 65ch&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;依網域分組&lt;/strong&gt;：相同來源網域的分頁會被安排到同一組。&lt;/li&gt;&lt;li&gt;&lt;strong&gt;依最近使用狀態排序&lt;/strong&gt;：商店說明指出，最後使用的分頁會被移到所屬群組較前的位置。&lt;/li&gt;&lt;li&gt;&lt;strong&gt;自訂群組&lt;/strong&gt;：除了自動整理，也可以建立自己的分頁群組。&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;這款工具適合「我不想每天整理，但希望分頁至少不要散落各處」的人。不過，自動化不代表永遠符合你的工作習慣；如果不同專案使用同一個網域，最後仍可能需要手動調整。&lt;/p&gt;&lt;h2 id=&quot;Tab-Manager-Plus-與-Tab-Manager-Auto-怎麼選?&quot;&gt;Tab Manager Plus 與 Tab Manager Auto 怎麼選？&lt;/h2&gt;&lt;p&gt;兩款工具不是誰一定比較強，而是解決的問題不同。先問自己：「我最常浪費時間在找分頁，還是整理分頁？」&lt;/p&gt;&lt;h3 id=&quot;需要搜尋與控制選-Tab-Manager-Plus&quot;&gt;需要搜尋與控制：選 Tab Manager Plus&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;分頁很多，常常找不到某一頁。&lt;/li&gt;&lt;li&gt;需要跨多個視窗搜尋與移動分頁。&lt;/li&gt;&lt;li&gt;想自己決定哪些分頁要保留或關閉。&lt;/li&gt;&lt;li&gt;偶爾才整理一次，不希望工具一直改變分頁配置。&lt;/li&gt;&lt;/ul&gt;&lt;h3 id=&quot;需要自動分組選-Tab-Manager-Auto&quot;&gt;需要自動分組：選 Tab Manager Auto&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;你常同時開啟同一網站的多個分頁。&lt;/li&gt;&lt;li&gt;希望分頁依網域自動集合。&lt;/li&gt;&lt;li&gt;可以接受偶爾檢查自動分組結果。&lt;/li&gt;&lt;li&gt;想把手動拖曳的時間交給工具處理。&lt;/li&gt;&lt;/ul&gt;&lt;h3 id=&quot;兩者一起用可以嗎?&quot;&gt;兩者一起用可以嗎？&lt;/h3&gt;&lt;p&gt;技術上可以同時安裝，但不代表一定要同時啟用所有自動功能。Tab Manager Auto 負責按規則整理，Tab Manager Plus 負責搜尋與批次管理；如果兩者同時修改分頁位置，結果可能和你的預期不同。&lt;/p&gt;&lt;p&gt;建議先安裝一款，使用幾天後再決定是否需要另一款。擴充功能越多不一定越有效率，瀏覽器不是自助餐盤，拿太多最後只會端不穩。&lt;/p&gt;&lt;h2 id=&quot;使用前的兩個提醒&quot;&gt;使用前的兩個提醒&lt;/h2&gt;&lt;p&gt;第一，安裝擴充功能前，請查看它要求的權限與開發者資訊，只安裝你信任的工具。Chrome 官方也提醒，擴充功能可能需要讀取或變更你造訪網站上的資料，因此權限不是可以一路按「同意」帶過的小字。&lt;/p&gt;&lt;p&gt;第二，若要在無痕模式使用擴充功能，請開啟 &lt;code&gt;chrome://extensions&lt;/code&gt;，找到擴充功能並進入「詳細資料」，再打開「允許在無痕模式中使用」。無痕模式不會讓擴充功能自動獲得這項權限；而且無痕視窗的工作階段資料也有不同的保存規則，別把它當成普通視窗的隱形斗篷。&lt;/p&gt;&lt;h2 id=&quot;結語&quot;&gt;結語&lt;/h2&gt;&lt;p&gt;如果你最常說的是「那個分頁到底在哪裡」，先試 Tab Manager Plus；如果你更想要「分頁自己按網域排好」，可以試 Tab Manager Auto。也可以先使用 Chrome 內建的分頁群組，確認自己的整理習慣後，再決定是否需要擴充功能。&lt;/p&gt;&lt;p&gt;分頁管理的目標不是把分頁列整理得像樣品屋，而是讓你下一次找資料時，真的找得到。整理完記得喝口咖啡——分頁可以自動分組，咖啡目前還不會。&lt;/p&gt;&lt;h2 id=&quot;參考資料&quot;&gt;參考資料&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://chromewebstore.google.com/detail/tab-manager-plus-for-chro/cnkdjjdmfiffagllbiiilooofcoeff&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Tab Manager Plus for Chrome｜Chrome Web Store&lt;/a&gt;：查閱擴充功能列出的搜尋分頁、重複分頁、視窗管理與分頁限制等功能。&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://chromewebstore.google.com/detail/tab-manager-auto/mplpoddifhoaicmpbjgpfnbljcabibak&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Tab Manager Auto｜Chrome Web Store&lt;/a&gt;：查閱依來源網域自動分組、最近使用分頁排序與自訂分組的功能說明。&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://support.google.com/chrome/answer/2664769?hl=zh-Hant&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;安裝及管理擴充功能｜Google Chrome 說明&lt;/a&gt;：查閱安裝擴充功能、檢查權限，以及開啟無痕模式使用權限的步驟。&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://support.google.com/chrome/answer/2391819?hl=zh-Hant&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;管理 Chrome 中的分頁｜Google Chrome 說明&lt;/a&gt;：查閱 Chrome 內建分頁群組、命名、收合與同步等功能。&lt;/li&gt;&lt;/ul&gt;</content>
  </entry>
  <entry>
    <title>Statamic 教學：Flat-file CMS 入門 vs WordPress／11ty</title>
    <link href="https://kamadiam.com/statamic-getting-started/" />
    <updated>2026-08-29T00:00:00Z</updated>
    <id>https://kamadiam.com/statamic-getting-started/</id>
    <content type="html">&lt;p&gt;WordPress 功能很多，這是優點，也是有時讓人打開後台就先想泡咖啡的原因。如果你想要一個能用控制面板管理內容、又能把內容和模板放進 Git 的 CMS，Statamic 值得認真看一眼。它把 CMS 的方便，和檔案型工作流程放在同一張桌上——咖啡則放旁邊，避免灑到 YAML。&lt;/p&gt;&lt;p&gt;這篇會帶你認識 Statamic 的 flat-file 模式、用 Laravel Herd 建立本機環境，接著做出首頁、文章集合、文章列表與可重複使用的模板片段。讀完後，你也會知道它和 WordPress、11ty 的定位差在哪裡，以及什麼情況適合選它。&lt;/p&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/statamic-getting-started/#Statamic-%E6%98%AF%E4%BB%80%E9%BA%BC?&quot;&gt;Statamic 是什麼？&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/statamic-getting-started/#%E5%A4%96%E6%8E%9B%E8%88%87-Starter-Kits&quot;&gt;外掛與 Starter Kits&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/statamic-getting-started/#%E5%85%88%E6%BA%96%E5%82%99%E6%9C%AC%E6%A9%9F%E9%96%8B%E7%99%BC%E7%92%B0%E5%A2%83&quot;&gt;先準備本機開發環境&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/statamic-getting-started/#%E8%AA%8D%E8%AD%98%E9%A6%96%E9%A0%81%E8%88%87-Antlers-%E6%A8%A1%E6%9D%BF&quot;&gt;認識首頁與 Antlers 模板&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/statamic-getting-started/#%E5%BB%BA%E7%AB%8B%E9%83%A8%E8%90%BD%E6%A0%BC%E7%9A%84%E5%85%A7%E5%AE%B9%E9%9B%86%E5%90%88&quot;&gt;建立部落格的內容集合&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/statamic-getting-started/#%E9%A1%AF%E7%A4%BA%E6%96%87%E7%AB%A0%E5%88%97%E8%A1%A8&quot;&gt;顯示文章列表&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/statamic-getting-started/#%E6%8A%8A%E9%87%8D%E8%A4%87%E5%85%A7%E5%AE%B9%E6%8A%BD%E6%88%90-Partial&quot;&gt;把重複內容抽成 Partial&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/statamic-getting-started/#%E5%8A%A0%E4%B8%8A%E5%B0%8E%E8%A6%BD%E5%88%97&quot;&gt;加上導覽列&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/statamic-getting-started/#%E7%94%A8-Blueprint-%E5%A2%9E%E5%8A%A0%E7%B2%BE%E9%81%B8%E5%9C%96%E7%89%87&quot;&gt;用 Blueprint 增加精選圖片&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/statamic-getting-started/#StatamicWordPress-%E8%88%87-11ty-%E6%80%8E%E9%BA%BC%E9%81%B8?&quot;&gt;Statamic、WordPress 與 11ty 怎麼選？&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/statamic-getting-started/#%E9%81%A9%E5%90%88%E8%AA%B0%E4%BD%BF%E7%94%A8?&quot;&gt;適合誰使用？&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/statamic-getting-started/#%E7%B5%90%E8%AA%9E&quot;&gt;結語&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/statamic-getting-started/#%E5%8F%83%E8%80%83%E8%B3%87%E6%96%99&quot;&gt;參考資料&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;h2 id=&quot;Statamic-是什麼?&quot;&gt;Statamic 是什麼？&lt;/h2&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://kamadiam.com/img/built/aVNw1Fc5b1-240.webp 240w, https://kamadiam.com/img/built/aVNw1Fc5b1-320.webp 320w, https://kamadiam.com/img/built/aVNw1Fc5b1-480.webp 480w, https://kamadiam.com/img/built/aVNw1Fc5b1-640.webp 640w, https://kamadiam.com/img/built/aVNw1Fc5b1-672.webp 672w, https://kamadiam.com/img/built/aVNw1Fc5b1-800.webp 800w, https://kamadiam.com/img/built/aVNw1Fc5b1-1200.webp 1200w&quot; sizes=&quot;(max-width: 768px) 100vw, 65ch&quot;&gt;&lt;img src=&quot;https://kamadiam.com/img/built/aVNw1Fc5b1-240.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;638&quot; srcset=&quot;https://kamadiam.com/img/built/aVNw1Fc5b1-240.png 240w, https://kamadiam.com/img/built/aVNw1Fc5b1-320.png 320w, https://kamadiam.com/img/built/aVNw1Fc5b1-480.png 480w, https://kamadiam.com/img/built/aVNw1Fc5b1-640.png 640w, https://kamadiam.com/img/built/aVNw1Fc5b1-672.png 672w, https://kamadiam.com/img/built/aVNw1Fc5b1-800.png 800w, https://kamadiam.com/img/built/aVNw1Fc5b1-1200.png 1200w&quot; sizes=&quot;(max-width: 768px) 100vw, 65ch&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;Statamic 是建立在 Laravel 上的內容管理系統。它預設使用 flat-file 模式：內容、設定與模板可以用 YAML、Markdown、Antlers 模板等檔案管理，而不是一開始就要求你準備資料庫。&lt;/p&gt;&lt;p&gt;「檔案型」不代表它只有編輯器。Statamic 仍然提供控制面板、內容集合、欄位藍圖、路由和模板系統；需要時，也能改用資料庫儲存。這種設計的重點，是先讓專案保持簡單，等需求真的長大再決定資料儲存方式。&lt;/p&gt;&lt;p&gt;換句話說，Statamic 不是把 CMS 藏在資料庫裡，而是讓你可以在控制面板和程式碼編輯器之間自由切換。內容可以給編輯者管理，結構和模板則可以交給 Git 追蹤。這比在資料庫裡尋找某段設定，少一點考古，多一點開發。&lt;/p&gt;&lt;h2 id=&quot;外掛與-Starter-Kits&quot;&gt;外掛與 Starter Kits&lt;/h2&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://kamadiam.com/img/built/HfTAUAJ_5D-240.webp 240w, https://kamadiam.com/img/built/HfTAUAJ_5D-320.webp 320w&quot; sizes=&quot;(max-width: 768px) 100vw, 65ch&quot;&gt;&lt;img src=&quot;https://kamadiam.com/img/built/HfTAUAJ_5D-240.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;320&quot; height=&quot;200&quot; srcset=&quot;https://kamadiam.com/img/built/HfTAUAJ_5D-240.png 240w, https://kamadiam.com/img/built/HfTAUAJ_5D-320.png 320w&quot; sizes=&quot;(max-width: 768px) 100vw, 65ch&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;Statamic 有外掛與 Marketplace，可以依專案需求增加功能。不過，選外掛前仍然要先確認相容版本、維護狀態和授權方式；「有外掛」不等於「所有外掛都該裝」。外掛裝太多，網站會像咖啡加了十種糖漿：不是不能喝，但已經忘記原本是什麼味道。&lt;/p&gt;&lt;h3 id=&quot;Starter-Kits-是什麼?&quot;&gt;Starter Kits 是什麼？&lt;/h3&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://kamadiam.com/img/built/oZPoadNdix-240.webp 240w, https://kamadiam.com/img/built/oZPoadNdix-320.webp 320w, https://kamadiam.com/img/built/oZPoadNdix-480.webp 480w, https://kamadiam.com/img/built/oZPoadNdix-640.webp 640w, https://kamadiam.com/img/built/oZPoadNdix-672.webp 672w, https://kamadiam.com/img/built/oZPoadNdix-800.webp 800w, https://kamadiam.com/img/built/oZPoadNdix-1200.webp 1200w&quot; sizes=&quot;(max-width: 768px) 100vw, 65ch&quot;&gt;&lt;img src=&quot;https://kamadiam.com/img/built/oZPoadNdix-240.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;776&quot; srcset=&quot;https://kamadiam.com/img/built/oZPoadNdix-240.png 240w, https://kamadiam.com/img/built/oZPoadNdix-320.png 320w, https://kamadiam.com/img/built/oZPoadNdix-480.png 480w, https://kamadiam.com/img/built/oZPoadNdix-640.png 640w, https://kamadiam.com/img/built/oZPoadNdix-672.png 672w, https://kamadiam.com/img/built/oZPoadNdix-800.png 800w, https://kamadiam.com/img/built/oZPoadNdix-1200.png 1200w&quot; sizes=&quot;(max-width: 768px) 100vw, 65ch&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;Starter Kits 是預先準備好的網站起點，通常包含內容結構、模板、樣式和功能。它們比較像「可以繼續修改的專案骨架」，不是 WordPress 那種套上去就結束的主題。每個 Kit 可以有自己的內容模型與設計，因此使用前要先看清楚它包含哪些檔案和功能。&lt;/p&gt;&lt;p&gt;第一次使用時，建議先從空白專案開始。你會比較清楚每一個集合、欄位和模板從哪裡來；熟悉後，再用 Starter Kit 加速重複性高的專案。&lt;/p&gt;&lt;h2 id=&quot;先準備本機開發環境&quot;&gt;先準備本機開發環境&lt;/h2&gt;&lt;p&gt;Statamic 的官方文件把 Laravel Herd 列為 macOS 和 Windows 初學者的推薦路徑。Herd 會協助準備 PHP、Composer 和本機 Web 伺服器，省下手動拼裝開發環境的步驟。&lt;/p&gt;&lt;p&gt;如果你已經有 PHP 和 Composer，也可以直接使用 Statamic CLI。這篇先採 Herd 路線，因為少一點環境設定，就多一點時間看網站真的跑起來。&lt;/p&gt;&lt;h3 id=&quot;使用-Laravel-Herd&quot;&gt;使用 Laravel Herd&lt;/h3&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://kamadiam.com/img/built/fGEJY7Y1pI-240.webp 240w, https://kamadiam.com/img/built/fGEJY7Y1pI-320.webp 320w, https://kamadiam.com/img/built/fGEJY7Y1pI-480.webp 480w, https://kamadiam.com/img/built/fGEJY7Y1pI-640.webp 640w, https://kamadiam.com/img/built/fGEJY7Y1pI-672.webp 672w, https://kamadiam.com/img/built/fGEJY7Y1pI-800.webp 800w, https://kamadiam.com/img/built/fGEJY7Y1pI-1200.webp 1200w&quot; sizes=&quot;(max-width: 768px) 100vw, 65ch&quot;&gt;&lt;img src=&quot;https://kamadiam.com/img/built/fGEJY7Y1pI-240.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;900&quot; srcset=&quot;https://kamadiam.com/img/built/fGEJY7Y1pI-240.png 240w, https://kamadiam.com/img/built/fGEJY7Y1pI-320.png 320w, https://kamadiam.com/img/built/fGEJY7Y1pI-480.png 480w, https://kamadiam.com/img/built/fGEJY7Y1pI-640.png 640w, https://kamadiam.com/img/built/fGEJY7Y1pI-672.png 672w, https://kamadiam.com/img/built/fGEJY7Y1pI-800.png 800w, https://kamadiam.com/img/built/fGEJY7Y1pI-1200.png 1200w&quot; sizes=&quot;(max-width: 768px) 100vw, 65ch&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;安裝 Herd 後，把專案放進 Herd 管理的目錄，再安裝 Statamic CLI：&lt;/p&gt;&lt;pre class=&quot;language-sh&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token function&quot;&gt;composer&lt;/span&gt; global require statamic/cli&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;接著建立一個新專案。這個指令會啟動設定精靈，讓你選擇空白網站或 Starter Kit，並建立第一位管理者：&lt;/p&gt;&lt;pre class=&quot;language-sh&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;cd&lt;/span&gt; ~/Herd
statamic new try-statamic&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;完成後，Herd 會把專案提供在類似以下的本機網址：&lt;/p&gt;&lt;pre class=&quot;language-text&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;http://try-statamic.test&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;控制面板則位於 &lt;code&gt;/cp&lt;/code&gt;：&lt;/p&gt;&lt;pre class=&quot;language-text&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;http://try-statamic.test/cp&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://kamadiam.com/img/built/diR2LzKEWJ-240.webp 240w, https://kamadiam.com/img/built/diR2LzKEWJ-320.webp 320w, https://kamadiam.com/img/built/diR2LzKEWJ-480.webp 480w, https://kamadiam.com/img/built/diR2LzKEWJ-640.webp 640w, https://kamadiam.com/img/built/diR2LzKEWJ-672.webp 672w, https://kamadiam.com/img/built/diR2LzKEWJ-800.webp 800w, https://kamadiam.com/img/built/diR2LzKEWJ-1198.webp 1198w&quot; sizes=&quot;(max-width: 768px) 100vw, 65ch&quot;&gt;&lt;img src=&quot;https://kamadiam.com/img/built/diR2LzKEWJ-240.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;1198&quot; height=&quot;1144&quot; srcset=&quot;https://kamadiam.com/img/built/diR2LzKEWJ-240.png 240w, https://kamadiam.com/img/built/diR2LzKEWJ-320.png 320w, https://kamadiam.com/img/built/diR2LzKEWJ-480.png 480w, https://kamadiam.com/img/built/diR2LzKEWJ-640.png 640w, https://kamadiam.com/img/built/diR2LzKEWJ-672.png 672w, https://kamadiam.com/img/built/diR2LzKEWJ-800.png 800w, https://kamadiam.com/img/built/diR2LzKEWJ-1198.png 1198w&quot; sizes=&quot;(max-width: 768px) 100vw, 65ch&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://kamadiam.com/img/built/F1ZClxgADa-240.webp 240w&quot; sizes=&quot;(max-width: 768px) 100vw, 65ch&quot;&gt;&lt;img src=&quot;https://kamadiam.com/img/built/F1ZClxgADa-240.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;240&quot; height=&quot;232&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://kamadiam.com/img/built/_BwPYXL20T-240.webp 240w, https://kamadiam.com/img/built/_BwPYXL20T-320.webp 320w, https://kamadiam.com/img/built/_BwPYXL20T-480.webp 480w, https://kamadiam.com/img/built/_BwPYXL20T-640.webp 640w, https://kamadiam.com/img/built/_BwPYXL20T-672.webp 672w, https://kamadiam.com/img/built/_BwPYXL20T-800.webp 800w, https://kamadiam.com/img/built/_BwPYXL20T-1200.webp 1200w&quot; sizes=&quot;(max-width: 768px) 100vw, 65ch&quot;&gt;&lt;img src=&quot;https://kamadiam.com/img/built/_BwPYXL20T-240.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;630&quot; srcset=&quot;https://kamadiam.com/img/built/_BwPYXL20T-240.png 240w, https://kamadiam.com/img/built/_BwPYXL20T-320.png 320w, https://kamadiam.com/img/built/_BwPYXL20T-480.png 480w, https://kamadiam.com/img/built/_BwPYXL20T-640.png 640w, https://kamadiam.com/img/built/_BwPYXL20T-672.png 672w, https://kamadiam.com/img/built/_BwPYXL20T-800.png 800w, https://kamadiam.com/img/built/_BwPYXL20T-1200.png 1200w&quot; sizes=&quot;(max-width: 768px) 100vw, 65ch&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;看到首頁和登入畫面，就代表基本環境完成。接下來不急著安裝十個套件，我們先做一個最小的內容流程。&lt;/p&gt;&lt;h2 id=&quot;認識首頁與-Antlers-模板&quot;&gt;認識首頁與 Antlers 模板&lt;/h2&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://kamadiam.com/img/built/0wXjIC1ZWi-207.webp 207w&quot; sizes=&quot;(max-width: 768px) 100vw, 65ch&quot;&gt;&lt;img src=&quot;https://kamadiam.com/img/built/0wXjIC1ZWi-207.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;207&quot; height=&quot;300&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://kamadiam.com/img/built/iJU_b7yP3E-240.webp 240w, https://kamadiam.com/img/built/iJU_b7yP3E-320.webp 320w, https://kamadiam.com/img/built/iJU_b7yP3E-480.webp 480w, https://kamadiam.com/img/built/iJU_b7yP3E-640.webp 640w, https://kamadiam.com/img/built/iJU_b7yP3E-672.webp 672w, https://kamadiam.com/img/built/iJU_b7yP3E-800.webp 800w, https://kamadiam.com/img/built/iJU_b7yP3E-1200.webp 1200w&quot; sizes=&quot;(max-width: 768px) 100vw, 65ch&quot;&gt;&lt;img src=&quot;https://kamadiam.com/img/built/iJU_b7yP3E-240.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;208&quot; srcset=&quot;https://kamadiam.com/img/built/iJU_b7yP3E-240.png 240w, https://kamadiam.com/img/built/iJU_b7yP3E-320.png 320w, https://kamadiam.com/img/built/iJU_b7yP3E-480.png 480w, https://kamadiam.com/img/built/iJU_b7yP3E-640.png 640w, https://kamadiam.com/img/built/iJU_b7yP3E-672.png 672w, https://kamadiam.com/img/built/iJU_b7yP3E-800.png 800w, https://kamadiam.com/img/built/iJU_b7yP3E-1200.png 1200w&quot; sizes=&quot;(max-width: 768px) 100vw, 65ch&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://kamadiam.com/img/built/GIqrK4m0if-240.webp 240w, https://kamadiam.com/img/built/GIqrK4m0if-320.webp 320w, https://kamadiam.com/img/built/GIqrK4m0if-480.webp 480w, https://kamadiam.com/img/built/GIqrK4m0if-640.webp 640w, https://kamadiam.com/img/built/GIqrK4m0if-672.webp 672w, https://kamadiam.com/img/built/GIqrK4m0if-800.webp 800w, https://kamadiam.com/img/built/GIqrK4m0if-1200.webp 1200w&quot; sizes=&quot;(max-width: 768px) 100vw, 65ch&quot;&gt;&lt;img src=&quot;https://kamadiam.com/img/built/GIqrK4m0if-240.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;590&quot; srcset=&quot;https://kamadiam.com/img/built/GIqrK4m0if-240.png 240w, https://kamadiam.com/img/built/GIqrK4m0if-320.png 320w, https://kamadiam.com/img/built/GIqrK4m0if-480.png 480w, https://kamadiam.com/img/built/GIqrK4m0if-640.png 640w, https://kamadiam.com/img/built/GIqrK4m0if-672.png 672w, https://kamadiam.com/img/built/GIqrK4m0if-800.png 800w, https://kamadiam.com/img/built/GIqrK4m0if-1200.png 1200w&quot; sizes=&quot;(max-width: 768px) 100vw, 65ch&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;Statamic 的預設首頁模板位於 &lt;code&gt;resources/views/home.antlers.html&lt;/code&gt;。副檔名中的 &lt;code&gt;.antlers.html&lt;/code&gt; 很重要：它告訴 Statamic 這是一個要由 Antlers 解析的模板，不是原封不動送出的 HTML。&lt;/p&gt;&lt;p&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;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;歡迎光臨我的 Statamic 實驗站！&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;

{{ content }}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;{{ content }}&lt;/code&gt; 會放入目前頁面的內容。模板儲存後重新整理頁面，就能看到結果。Antlers 使用雙大括號，看起來像鬍子；如果模板開始長出真的鬍子，通常是熬夜太久，不是語法功能。&lt;/p&gt;&lt;h3 id=&quot;Layout-負責共同外框&quot;&gt;Layout 負責共同外框&lt;/h3&gt;&lt;p&gt;網站的共同結構通常放在 &lt;code&gt;resources/views/layout.antlers.html&lt;/code&gt;，例如 HTML 外框、導覽列、頁尾和資產引用。頁面模板產生的內容，會插入 &lt;code&gt;{{ template_content }}&lt;/code&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 doctype&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;!&lt;/span&gt;&lt;span class=&quot;token doctype-tag&quot;&gt;doctype&lt;/span&gt; &lt;span class=&quot;token name&quot;&gt;html&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;html&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;lang&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;{{ site:short_locale }}&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;head&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;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;charset&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;utf-8&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;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&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;viewport&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;content&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;width=device-width, initial-scale=1&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;title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;{{ title ?? site:name }}&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;title&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;head&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;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    {{ template_content }}
&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;body&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;html&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;你可以把 Layout 想成相框：每一頁的內容會變，外框則可以共用。這樣修改頁尾時，不必逐頁複製貼上，少一次複製，也少一次「為什麼只有這頁沒有更新」的偵探劇。&lt;/p&gt;&lt;h2 id=&quot;建立部落格的內容集合&quot;&gt;建立部落格的內容集合&lt;/h2&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://kamadiam.com/img/built/za8-TXmmzh-240.webp 240w, https://kamadiam.com/img/built/za8-TXmmzh-320.webp 320w, https://kamadiam.com/img/built/za8-TXmmzh-480.webp 480w, https://kamadiam.com/img/built/za8-TXmmzh-640.webp 640w, https://kamadiam.com/img/built/za8-TXmmzh-672.webp 672w, https://kamadiam.com/img/built/za8-TXmmzh-800.webp 800w, https://kamadiam.com/img/built/za8-TXmmzh-1200.webp 1200w&quot; sizes=&quot;(max-width: 768px) 100vw, 65ch&quot;&gt;&lt;img src=&quot;https://kamadiam.com/img/built/za8-TXmmzh-240.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;742&quot; srcset=&quot;https://kamadiam.com/img/built/za8-TXmmzh-240.png 240w, https://kamadiam.com/img/built/za8-TXmmzh-320.png 320w, https://kamadiam.com/img/built/za8-TXmmzh-480.png 480w, https://kamadiam.com/img/built/za8-TXmmzh-640.png 640w, https://kamadiam.com/img/built/za8-TXmmzh-672.png 672w, https://kamadiam.com/img/built/za8-TXmmzh-800.png 800w, https://kamadiam.com/img/built/za8-TXmmzh-1200.png 1200w&quot; sizes=&quot;(max-width: 768px) 100vw, 65ch&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;在 Statamic 裡，Collection 是一組有共同結構的內容，例如文章、產品或作品集。建立 &lt;code&gt;blog&lt;/code&gt; 集合後，文章項目會依集合設定儲存；flat-file 模式下，你可以在專案內直接找到並編輯這些 Markdown 檔案。&lt;/p&gt;&lt;p&gt;先規劃一個簡單的部落格：&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;title&lt;/code&gt;：文章標題&lt;/li&gt;&lt;li&gt;&lt;code&gt;date&lt;/code&gt;：發布日期&lt;/li&gt;&lt;li&gt;&lt;code&gt;author&lt;/code&gt;：作者&lt;/li&gt;&lt;li&gt;&lt;code&gt;content&lt;/code&gt;：正文&lt;/li&gt;&lt;li&gt;&lt;code&gt;featured_image&lt;/code&gt;：精選圖片&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;欄位不是越多越專業。先從讀者真的需要的資料開始，之後再透過 Blueprint 增加欄位。&lt;/p&gt;&lt;h3 id=&quot;產生文章模板&quot;&gt;產生文章模板&lt;/h3&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://kamadiam.com/img/built/SVg-j9xQi8-240.webp 240w, https://kamadiam.com/img/built/SVg-j9xQi8-320.webp 320w, https://kamadiam.com/img/built/SVg-j9xQi8-480.webp 480w, https://kamadiam.com/img/built/SVg-j9xQi8-640.webp 640w, https://kamadiam.com/img/built/SVg-j9xQi8-672.webp 672w, https://kamadiam.com/img/built/SVg-j9xQi8-800.webp 800w, https://kamadiam.com/img/built/SVg-j9xQi8-1200.webp 1200w&quot; sizes=&quot;(max-width: 768px) 100vw, 65ch&quot;&gt;&lt;img src=&quot;https://kamadiam.com/img/built/SVg-j9xQi8-240.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;336&quot; srcset=&quot;https://kamadiam.com/img/built/SVg-j9xQi8-240.png 240w, https://kamadiam.com/img/built/SVg-j9xQi8-320.png 320w, https://kamadiam.com/img/built/SVg-j9xQi8-480.png 480w, https://kamadiam.com/img/built/SVg-j9xQi8-640.png 640w, https://kamadiam.com/img/built/SVg-j9xQi8-672.png 672w, https://kamadiam.com/img/built/SVg-j9xQi8-800.png 800w, https://kamadiam.com/img/built/SVg-j9xQi8-1200.png 1200w&quot; sizes=&quot;(max-width: 768px) 100vw, 65ch&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://kamadiam.com/img/built/37s9_CL0wI-240.webp 240w, https://kamadiam.com/img/built/37s9_CL0wI-320.webp 320w, https://kamadiam.com/img/built/37s9_CL0wI-480.webp 480w, https://kamadiam.com/img/built/37s9_CL0wI-640.webp 640w, https://kamadiam.com/img/built/37s9_CL0wI-672.webp 672w, https://kamadiam.com/img/built/37s9_CL0wI-800.webp 800w, https://kamadiam.com/img/built/37s9_CL0wI-1200.webp 1200w&quot; sizes=&quot;(max-width: 768px) 100vw, 65ch&quot;&gt;&lt;img src=&quot;https://kamadiam.com/img/built/37s9_CL0wI-240.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;510&quot; srcset=&quot;https://kamadiam.com/img/built/37s9_CL0wI-240.png 240w, https://kamadiam.com/img/built/37s9_CL0wI-320.png 320w, https://kamadiam.com/img/built/37s9_CL0wI-480.png 480w, https://kamadiam.com/img/built/37s9_CL0wI-640.png 640w, https://kamadiam.com/img/built/37s9_CL0wI-672.png 672w, https://kamadiam.com/img/built/37s9_CL0wI-800.png 800w, https://kamadiam.com/img/built/37s9_CL0wI-1200.png 1200w&quot; sizes=&quot;(max-width: 768px) 100vw, 65ch&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;建立集合時，Statamic 可以協助產生基本模板，例如：&lt;/p&gt;&lt;pre class=&quot;language-text&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;resources/views/blog/index.antlers.html
resources/views/blog/show.antlers.html&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;前者用來顯示文章列表，後者用來顯示單篇文章。若你的專案版本或設定產生了不同的檔案名稱，請以實際產生的檔案為準；模板路徑是專案設定的一部分，不要只靠截圖猜路徑。&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://kamadiam.com/img/built/jDPfdpNjZ9-240.webp 240w&quot; sizes=&quot;(max-width: 768px) 100vw, 65ch&quot;&gt;&lt;img src=&quot;https://kamadiam.com/img/built/jDPfdpNjZ9-240.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;240&quot; height=&quot;284&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;h3 id=&quot;設定日期與網址&quot;&gt;設定日期與網址&lt;/h3&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://kamadiam.com/img/built/QYWamz1FE5-240.webp 240w, https://kamadiam.com/img/built/QYWamz1FE5-320.webp 320w, https://kamadiam.com/img/built/QYWamz1FE5-480.webp 480w, https://kamadiam.com/img/built/QYWamz1FE5-640.webp 640w, https://kamadiam.com/img/built/QYWamz1FE5-672.webp 672w, https://kamadiam.com/img/built/QYWamz1FE5-800.webp 800w, https://kamadiam.com/img/built/QYWamz1FE5-1200.webp 1200w&quot; sizes=&quot;(max-width: 768px) 100vw, 65ch&quot;&gt;&lt;img src=&quot;https://kamadiam.com/img/built/QYWamz1FE5-240.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;341&quot; srcset=&quot;https://kamadiam.com/img/built/QYWamz1FE5-240.png 240w, https://kamadiam.com/img/built/QYWamz1FE5-320.png 320w, https://kamadiam.com/img/built/QYWamz1FE5-480.png 480w, https://kamadiam.com/img/built/QYWamz1FE5-640.png 640w, https://kamadiam.com/img/built/QYWamz1FE5-672.png 672w, https://kamadiam.com/img/built/QYWamz1FE5-800.png 800w, https://kamadiam.com/img/built/QYWamz1FE5-1200.png 1200w&quot; sizes=&quot;(max-width: 768px) 100vw, 65ch&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;發布日期不是每個網站都必須顯示的欄位，但部落格通常需要它來排序和呈現文章資訊。網址則由 Collection 的 route 設定決定，建議一開始保持簡單，確定內容模型後再設計更複雜的 URL。&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://kamadiam.com/img/built/cz9y1Px3Zb-240.webp 240w, https://kamadiam.com/img/built/cz9y1Px3Zb-320.webp 320w, https://kamadiam.com/img/built/cz9y1Px3Zb-480.webp 480w, https://kamadiam.com/img/built/cz9y1Px3Zb-640.webp 640w, https://kamadiam.com/img/built/cz9y1Px3Zb-672.webp 672w, https://kamadiam.com/img/built/cz9y1Px3Zb-800.webp 800w, https://kamadiam.com/img/built/cz9y1Px3Zb-1200.webp 1200w&quot; sizes=&quot;(max-width: 768px) 100vw, 65ch&quot;&gt;&lt;img src=&quot;https://kamadiam.com/img/built/cz9y1Px3Zb-240.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;587&quot; srcset=&quot;https://kamadiam.com/img/built/cz9y1Px3Zb-240.png 240w, https://kamadiam.com/img/built/cz9y1Px3Zb-320.png 320w, https://kamadiam.com/img/built/cz9y1Px3Zb-480.png 480w, https://kamadiam.com/img/built/cz9y1Px3Zb-640.png 640w, https://kamadiam.com/img/built/cz9y1Px3Zb-672.png 672w, https://kamadiam.com/img/built/cz9y1Px3Zb-800.png 800w, https://kamadiam.com/img/built/cz9y1Px3Zb-1200.png 1200w&quot; sizes=&quot;(max-width: 768px) 100vw, 65ch&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://kamadiam.com/img/built/2iA12LCQIq-240.webp 240w, https://kamadiam.com/img/built/2iA12LCQIq-320.webp 320w, https://kamadiam.com/img/built/2iA12LCQIq-480.webp 480w, https://kamadiam.com/img/built/2iA12LCQIq-640.webp 640w, https://kamadiam.com/img/built/2iA12LCQIq-672.webp 672w, https://kamadiam.com/img/built/2iA12LCQIq-800.webp 800w, https://kamadiam.com/img/built/2iA12LCQIq-1200.webp 1200w&quot; sizes=&quot;(max-width: 768px) 100vw, 65ch&quot;&gt;&lt;img src=&quot;https://kamadiam.com/img/built/2iA12LCQIq-240.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;204&quot; srcset=&quot;https://kamadiam.com/img/built/2iA12LCQIq-240.png 240w, https://kamadiam.com/img/built/2iA12LCQIq-320.png 320w, https://kamadiam.com/img/built/2iA12LCQIq-480.png 480w, https://kamadiam.com/img/built/2iA12LCQIq-640.png 640w, https://kamadiam.com/img/built/2iA12LCQIq-672.png 672w, https://kamadiam.com/img/built/2iA12LCQIq-800.png 800w, https://kamadiam.com/img/built/2iA12LCQIq-1200.png 1200w&quot; sizes=&quot;(max-width: 768px) 100vw, 65ch&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://kamadiam.com/img/built/gEnHF7yzj8-240.webp 240w, https://kamadiam.com/img/built/gEnHF7yzj8-320.webp 320w, https://kamadiam.com/img/built/gEnHF7yzj8-480.webp 480w, https://kamadiam.com/img/built/gEnHF7yzj8-640.webp 640w, https://kamadiam.com/img/built/gEnHF7yzj8-672.webp 672w, https://kamadiam.com/img/built/gEnHF7yzj8-800.webp 800w, https://kamadiam.com/img/built/gEnHF7yzj8-1200.webp 1200w&quot; sizes=&quot;(max-width: 768px) 100vw, 65ch&quot;&gt;&lt;img src=&quot;https://kamadiam.com/img/built/gEnHF7yzj8-240.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;402&quot; srcset=&quot;https://kamadiam.com/img/built/gEnHF7yzj8-240.png 240w, https://kamadiam.com/img/built/gEnHF7yzj8-320.png 320w, https://kamadiam.com/img/built/gEnHF7yzj8-480.png 480w, https://kamadiam.com/img/built/gEnHF7yzj8-640.png 640w, https://kamadiam.com/img/built/gEnHF7yzj8-672.png 672w, https://kamadiam.com/img/built/gEnHF7yzj8-800.png 800w, https://kamadiam.com/img/built/gEnHF7yzj8-1200.png 1200w&quot; sizes=&quot;(max-width: 768px) 100vw, 65ch&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;h2 id=&quot;顯示文章列表&quot;&gt;顯示文章列表&lt;/h2&gt;&lt;p&gt;建立第一篇文章後，就可以用 Collection Tag 讀取 &lt;code&gt;blog&lt;/code&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;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;{{ title }}&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;
{{ content }}

&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;ul&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    {{ collection:blog limit=&quot;5&quot; }}
        &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;li&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;a&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;{{ url }}&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;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;{{ title }}&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;span&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;time&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;{{ date }}&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;time&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;a&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;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    {{ /collection:blog }}
&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;ul&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;collection:blog&lt;/code&gt; 會讀取 &lt;code&gt;blog&lt;/code&gt; 集合，&lt;code&gt;limit=&amp;quot;5&amp;quot;&lt;/code&gt; 則限制輸出數量。這裡的 &lt;code&gt;blog&lt;/code&gt; 是集合 handle，不是隨便取的裝飾文字；handle 打錯時，模板通常不會替你把咖啡端過來。&lt;/p&gt;&lt;h3 id=&quot;部落格索引頁&quot;&gt;部落格索引頁&lt;/h3&gt;&lt;p&gt;在 Pages 集合建立一個部落格頁面，將它設為文章列表使用的模板。&lt;code&gt;resources/views/blog/index.antlers.html&lt;/code&gt; 可以重用前面的 Collection Tag：&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;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;{{ title }}&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;
{{ content }}

{{ collection:blog limit=&quot;5&quot; }}
    &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;a&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;{{ url }}&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;{{ title }}&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;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
{{ /collection:blog }}&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;單篇文章頁&quot;&gt;單篇文章頁&lt;/h3&gt;&lt;p&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;article&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;{{ title }}&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;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;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Published on {{ date }} by {{ author:name }}&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;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    {{ content }}
&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;article&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;{{ content }}&lt;/code&gt; 是文章正文，&lt;code&gt;{{ author:name }}&lt;/code&gt; 則是讀取作者資料的寫法之一；實際欄位名稱要和你的 Blueprint 一致。&lt;/p&gt;&lt;h2 id=&quot;把重複內容抽成-Partial&quot;&gt;把重複內容抽成 Partial&lt;/h2&gt;&lt;p&gt;文章列表會同時出現在首頁和索引頁。若兩邊都直接複製同一段模板，日後改一個 HTML class 就要改兩次，久了會養出兩份長得很像、脾氣卻不同的程式碼。&lt;/p&gt;&lt;p&gt;在 &lt;code&gt;resources/views/blog/&lt;/code&gt; 建立 &lt;code&gt;_listing.antlers.html&lt;/code&gt;，底線是常見的 partial 命名約定：&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;ul&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    {{ collection:blog limit=&quot;5&quot; }}
        &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;li&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;a&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;{{ url }}&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;{{ title }}&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;a&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;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    {{ /collection:blog }}
&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;ul&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;/p&gt;&lt;pre class=&quot;language-html&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;{{ partial:blog/_listing }}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Partial 是可重用的模板片段，不是完整頁面。把列表集中管理後，首頁和索引頁就能共享同一份輸出邏輯。&lt;/p&gt;&lt;h2 id=&quot;加上導覽列&quot;&gt;加上導覽列&lt;/h2&gt;&lt;p&gt;如果網站有 Pages 集合，可以使用 Nav Tag 產生導覽：&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;nav&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    {{ nav from=&quot;pages&quot; include_home=&quot;true&quot; }}
        &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;a&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;{{ url }}&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;{{ title }}&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;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    {{ /nav }}
&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;nav&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;把這段放進 Layout，所有套用該 Layout 的頁面就能共用導覽列。樣式可以之後再加，先讓資料流動起來；CSS 不會因為等五分鐘而生氣，但你可能會。&lt;/p&gt;&lt;h2 id=&quot;用-Blueprint-增加精選圖片&quot;&gt;用 Blueprint 增加精選圖片&lt;/h2&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://kamadiam.com/img/built/IIB9bVr1I3-240.webp 240w, https://kamadiam.com/img/built/IIB9bVr1I3-320.webp 320w, https://kamadiam.com/img/built/IIB9bVr1I3-480.webp 480w, https://kamadiam.com/img/built/IIB9bVr1I3-640.webp 640w, https://kamadiam.com/img/built/IIB9bVr1I3-672.webp 672w, https://kamadiam.com/img/built/IIB9bVr1I3-800.webp 800w, https://kamadiam.com/img/built/IIB9bVr1I3-1200.webp 1200w&quot; sizes=&quot;(max-width: 768px) 100vw, 65ch&quot;&gt;&lt;img src=&quot;https://kamadiam.com/img/built/IIB9bVr1I3-240.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;334&quot; srcset=&quot;https://kamadiam.com/img/built/IIB9bVr1I3-240.png 240w, https://kamadiam.com/img/built/IIB9bVr1I3-320.png 320w, https://kamadiam.com/img/built/IIB9bVr1I3-480.png 480w, https://kamadiam.com/img/built/IIB9bVr1I3-640.png 640w, https://kamadiam.com/img/built/IIB9bVr1I3-672.png 672w, https://kamadiam.com/img/built/IIB9bVr1I3-800.png 800w, https://kamadiam.com/img/built/IIB9bVr1I3-1200.png 1200w&quot; sizes=&quot;(max-width: 768px) 100vw, 65ch&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://kamadiam.com/img/built/IP6RYRqgTv-240.webp 240w, https://kamadiam.com/img/built/IP6RYRqgTv-320.webp 320w, https://kamadiam.com/img/built/IP6RYRqgTv-480.webp 480w, https://kamadiam.com/img/built/IP6RYRqgTv-640.webp 640w, https://kamadiam.com/img/built/IP6RYRqgTv-672.webp 672w, https://kamadiam.com/img/built/IP6RYRqgTv-800.webp 800w, https://kamadiam.com/img/built/IP6RYRqgTv-1200.webp 1200w&quot; sizes=&quot;(max-width: 768px) 100vw, 65ch&quot;&gt;&lt;img src=&quot;https://kamadiam.com/img/built/IP6RYRqgTv-240.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;554&quot; srcset=&quot;https://kamadiam.com/img/built/IP6RYRqgTv-240.png 240w, https://kamadiam.com/img/built/IP6RYRqgTv-320.png 320w, https://kamadiam.com/img/built/IP6RYRqgTv-480.png 480w, https://kamadiam.com/img/built/IP6RYRqgTv-640.png 640w, https://kamadiam.com/img/built/IP6RYRqgTv-672.png 672w, https://kamadiam.com/img/built/IP6RYRqgTv-800.png 800w, https://kamadiam.com/img/built/IP6RYRqgTv-1200.png 1200w&quot; sizes=&quot;(max-width: 768px) 100vw, 65ch&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;Blueprint 用來定義集合或頁面的欄位。你可以在 &lt;code&gt;blog&lt;/code&gt; 集合的 Blueprint 增加 &lt;code&gt;featured_image&lt;/code&gt;，選擇適合圖片資產的欄位型別，並限制單篇文章的圖片數量。&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://kamadiam.com/img/built/5b_EL8GYWm-240.webp 240w&quot; sizes=&quot;(max-width: 768px) 100vw, 65ch&quot;&gt;&lt;img src=&quot;https://kamadiam.com/img/built/5b_EL8GYWm-240.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;240&quot; height=&quot;177&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://kamadiam.com/img/built/A7fMX3qceb-240.webp 240w, https://kamadiam.com/img/built/A7fMX3qceb-320.webp 320w, https://kamadiam.com/img/built/A7fMX3qceb-480.webp 480w, https://kamadiam.com/img/built/A7fMX3qceb-640.webp 640w, https://kamadiam.com/img/built/A7fMX3qceb-672.webp 672w, https://kamadiam.com/img/built/A7fMX3qceb-800.webp 800w, https://kamadiam.com/img/built/A7fMX3qceb-1200.webp 1200w&quot; sizes=&quot;(max-width: 768px) 100vw, 65ch&quot;&gt;&lt;img src=&quot;https://kamadiam.com/img/built/A7fMX3qceb-240.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;557&quot; srcset=&quot;https://kamadiam.com/img/built/A7fMX3qceb-240.png 240w, https://kamadiam.com/img/built/A7fMX3qceb-320.png 320w, https://kamadiam.com/img/built/A7fMX3qceb-480.png 480w, https://kamadiam.com/img/built/A7fMX3qceb-640.png 640w, https://kamadiam.com/img/built/A7fMX3qceb-672.png 672w, https://kamadiam.com/img/built/A7fMX3qceb-800.png 800w, https://kamadiam.com/img/built/A7fMX3qceb-1200.png 1200w&quot; sizes=&quot;(max-width: 768px) 100vw, 65ch&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://kamadiam.com/img/built/UTPL2vgdCR-240.webp 240w, https://kamadiam.com/img/built/UTPL2vgdCR-320.webp 320w, https://kamadiam.com/img/built/UTPL2vgdCR-480.webp 480w, https://kamadiam.com/img/built/UTPL2vgdCR-640.webp 640w, https://kamadiam.com/img/built/UTPL2vgdCR-672.webp 672w, https://kamadiam.com/img/built/UTPL2vgdCR-800.webp 800w, https://kamadiam.com/img/built/UTPL2vgdCR-1200.webp 1200w&quot; sizes=&quot;(max-width: 768px) 100vw, 65ch&quot;&gt;&lt;img src=&quot;https://kamadiam.com/img/built/UTPL2vgdCR-240.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;452&quot; srcset=&quot;https://kamadiam.com/img/built/UTPL2vgdCR-240.png 240w, https://kamadiam.com/img/built/UTPL2vgdCR-320.png 320w, https://kamadiam.com/img/built/UTPL2vgdCR-480.png 480w, https://kamadiam.com/img/built/UTPL2vgdCR-640.png 640w, https://kamadiam.com/img/built/UTPL2vgdCR-672.png 672w, https://kamadiam.com/img/built/UTPL2vgdCR-800.png 800w, https://kamadiam.com/img/built/UTPL2vgdCR-1200.png 1200w&quot; sizes=&quot;(max-width: 768px) 100vw, 65ch&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;設定欄位時，請分清楚「顯示名稱」和「Handle」。模板會使用 Handle，例如 &lt;code&gt;featured_image&lt;/code&gt;。圖片的替代文字也應該在內容編輯時填好，讓無法看見圖片的讀者仍然知道它代表什麼。&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://kamadiam.com/img/built/p4DTO5Il-R-240.webp 240w, https://kamadiam.com/img/built/p4DTO5Il-R-320.webp 320w, https://kamadiam.com/img/built/p4DTO5Il-R-480.webp 480w, https://kamadiam.com/img/built/p4DTO5Il-R-640.webp 640w, https://kamadiam.com/img/built/p4DTO5Il-R-672.webp 672w, https://kamadiam.com/img/built/p4DTO5Il-R-800.webp 800w, https://kamadiam.com/img/built/p4DTO5Il-R-1200.webp 1200w&quot; sizes=&quot;(max-width: 768px) 100vw, 65ch&quot;&gt;&lt;img src=&quot;https://kamadiam.com/img/built/p4DTO5Il-R-240.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;235&quot; srcset=&quot;https://kamadiam.com/img/built/p4DTO5Il-R-240.png 240w, https://kamadiam.com/img/built/p4DTO5Il-R-320.png 320w, https://kamadiam.com/img/built/p4DTO5Il-R-480.png 480w, https://kamadiam.com/img/built/p4DTO5Il-R-640.png 640w, https://kamadiam.com/img/built/p4DTO5Il-R-672.png 672w, https://kamadiam.com/img/built/p4DTO5Il-R-800.png 800w, https://kamadiam.com/img/built/p4DTO5Il-R-1200.png 1200w&quot; sizes=&quot;(max-width: 768px) 100vw, 65ch&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://kamadiam.com/img/built/qeSfr4KQ_y-240.webp 240w, https://kamadiam.com/img/built/qeSfr4KQ_y-320.webp 320w, https://kamadiam.com/img/built/qeSfr4KQ_y-480.webp 480w, https://kamadiam.com/img/built/qeSfr4KQ_y-640.webp 640w, https://kamadiam.com/img/built/qeSfr4KQ_y-672.webp 672w, https://kamadiam.com/img/built/qeSfr4KQ_y-800.webp 800w, https://kamadiam.com/img/built/qeSfr4KQ_y-1200.webp 1200w&quot; sizes=&quot;(max-width: 768px) 100vw, 65ch&quot;&gt;&lt;img src=&quot;https://kamadiam.com/img/built/qeSfr4KQ_y-240.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;150&quot; srcset=&quot;https://kamadiam.com/img/built/qeSfr4KQ_y-240.png 240w, https://kamadiam.com/img/built/qeSfr4KQ_y-320.png 320w, https://kamadiam.com/img/built/qeSfr4KQ_y-480.png 480w, https://kamadiam.com/img/built/qeSfr4KQ_y-640.png 640w, https://kamadiam.com/img/built/qeSfr4KQ_y-672.png 672w, https://kamadiam.com/img/built/qeSfr4KQ_y-800.png 800w, https://kamadiam.com/img/built/qeSfr4KQ_y-1200.png 1200w&quot; sizes=&quot;(max-width: 768px) 100vw, 65ch&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://kamadiam.com/img/built/Ne-fvxMxAL-240.webp 240w, https://kamadiam.com/img/built/Ne-fvxMxAL-320.webp 320w, https://kamadiam.com/img/built/Ne-fvxMxAL-480.webp 480w, https://kamadiam.com/img/built/Ne-fvxMxAL-640.webp 640w, https://kamadiam.com/img/built/Ne-fvxMxAL-672.webp 672w, https://kamadiam.com/img/built/Ne-fvxMxAL-800.webp 800w, https://kamadiam.com/img/built/Ne-fvxMxAL-1200.webp 1200w&quot; sizes=&quot;(max-width: 768px) 100vw, 65ch&quot;&gt;&lt;img src=&quot;https://kamadiam.com/img/built/Ne-fvxMxAL-240.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;394&quot; srcset=&quot;https://kamadiam.com/img/built/Ne-fvxMxAL-240.png 240w, https://kamadiam.com/img/built/Ne-fvxMxAL-320.png 320w, https://kamadiam.com/img/built/Ne-fvxMxAL-480.png 480w, https://kamadiam.com/img/built/Ne-fvxMxAL-640.png 640w, https://kamadiam.com/img/built/Ne-fvxMxAL-672.png 672w, https://kamadiam.com/img/built/Ne-fvxMxAL-800.png 800w, https://kamadiam.com/img/built/Ne-fvxMxAL-1200.png 1200w&quot; sizes=&quot;(max-width: 768px) 100vw, 65ch&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://kamadiam.com/img/built/PDGDuepy3o-240.webp 240w, https://kamadiam.com/img/built/PDGDuepy3o-320.webp 320w, https://kamadiam.com/img/built/PDGDuepy3o-480.webp 480w, https://kamadiam.com/img/built/PDGDuepy3o-640.webp 640w, https://kamadiam.com/img/built/PDGDuepy3o-672.webp 672w, https://kamadiam.com/img/built/PDGDuepy3o-800.webp 800w, https://kamadiam.com/img/built/PDGDuepy3o-1200.webp 1200w&quot; sizes=&quot;(max-width: 768px) 100vw, 65ch&quot;&gt;&lt;img src=&quot;https://kamadiam.com/img/built/PDGDuepy3o-240.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;643&quot; srcset=&quot;https://kamadiam.com/img/built/PDGDuepy3o-240.png 240w, https://kamadiam.com/img/built/PDGDuepy3o-320.png 320w, https://kamadiam.com/img/built/PDGDuepy3o-480.png 480w, https://kamadiam.com/img/built/PDGDuepy3o-640.png 640w, https://kamadiam.com/img/built/PDGDuepy3o-672.png 672w, https://kamadiam.com/img/built/PDGDuepy3o-800.png 800w, https://kamadiam.com/img/built/PDGDuepy3o-1200.png 1200w&quot; sizes=&quot;(max-width: 768px) 100vw, 65ch&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;在文章模板中輸出圖片時，欄位的實際回傳格式會受 Blueprint 和資產設定影響。最基本的概念如下：&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;{{ featured_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;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;{{ featured_image:alt }}&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;如果專案使用資產陣列或需要圖片轉換，請依官方 Assets 文件和實際欄位輸出調整模板，不要直接照抄一段不符合資料格式的範例。&lt;/p&gt;&lt;h2 id=&quot;StatamicWordPress-與-11ty-怎麼選?&quot;&gt;Statamic、WordPress 與 11ty 怎麼選？&lt;/h2&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://kamadiam.com/img/built/XO8O-kttxA-240.webp 240w, https://kamadiam.com/img/built/XO8O-kttxA-320.webp 320w, https://kamadiam.com/img/built/XO8O-kttxA-480.webp 480w, https://kamadiam.com/img/built/XO8O-kttxA-640.webp 640w, https://kamadiam.com/img/built/XO8O-kttxA-672.webp 672w, https://kamadiam.com/img/built/XO8O-kttxA-800.webp 800w, https://kamadiam.com/img/built/XO8O-kttxA-1200.webp 1200w&quot; sizes=&quot;(max-width: 768px) 100vw, 65ch&quot;&gt;&lt;img src=&quot;https://kamadiam.com/img/built/XO8O-kttxA-240.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;465&quot; srcset=&quot;https://kamadiam.com/img/built/XO8O-kttxA-240.png 240w, https://kamadiam.com/img/built/XO8O-kttxA-320.png 320w, https://kamadiam.com/img/built/XO8O-kttxA-480.png 480w, https://kamadiam.com/img/built/XO8O-kttxA-640.png 640w, https://kamadiam.com/img/built/XO8O-kttxA-672.png 672w, https://kamadiam.com/img/built/XO8O-kttxA-800.png 800w, https://kamadiam.com/img/built/XO8O-kttxA-1200.png 1200w&quot; sizes=&quot;(max-width: 768px) 100vw, 65ch&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://kamadiam.com/img/built/Ue3P_eqQTv-240.webp 240w, https://kamadiam.com/img/built/Ue3P_eqQTv-320.webp 320w, https://kamadiam.com/img/built/Ue3P_eqQTv-480.webp 480w, https://kamadiam.com/img/built/Ue3P_eqQTv-640.webp 640w, https://kamadiam.com/img/built/Ue3P_eqQTv-672.webp 672w, https://kamadiam.com/img/built/Ue3P_eqQTv-800.webp 800w, https://kamadiam.com/img/built/Ue3P_eqQTv-1200.webp 1200w&quot; sizes=&quot;(max-width: 768px) 100vw, 65ch&quot;&gt;&lt;img src=&quot;https://kamadiam.com/img/built/Ue3P_eqQTv-240.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;465&quot; srcset=&quot;https://kamadiam.com/img/built/Ue3P_eqQTv-240.png 240w, https://kamadiam.com/img/built/Ue3P_eqQTv-320.png 320w, https://kamadiam.com/img/built/Ue3P_eqQTv-480.png 480w, https://kamadiam.com/img/built/Ue3P_eqQTv-640.png 640w, https://kamadiam.com/img/built/Ue3P_eqQTv-672.png 672w, https://kamadiam.com/img/built/Ue3P_eqQTv-800.png 800w, https://kamadiam.com/img/built/Ue3P_eqQTv-1200.png 1200w&quot; sizes=&quot;(max-width: 768px) 100vw, 65ch&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://kamadiam.com/img/built/UAjQ2k0erw-240.webp 240w, https://kamadiam.com/img/built/UAjQ2k0erw-320.webp 320w, https://kamadiam.com/img/built/UAjQ2k0erw-480.webp 480w, https://kamadiam.com/img/built/UAjQ2k0erw-640.webp 640w, https://kamadiam.com/img/built/UAjQ2k0erw-672.webp 672w, https://kamadiam.com/img/built/UAjQ2k0erw-800.webp 800w, https://kamadiam.com/img/built/UAjQ2k0erw-1030.webp 1030w&quot; sizes=&quot;(max-width: 768px) 100vw, 65ch&quot;&gt;&lt;img src=&quot;https://kamadiam.com/img/built/UAjQ2k0erw-240.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; width=&quot;1030&quot; height=&quot;1490&quot; srcset=&quot;https://kamadiam.com/img/built/UAjQ2k0erw-240.png 240w, https://kamadiam.com/img/built/UAjQ2k0erw-320.png 320w, https://kamadiam.com/img/built/UAjQ2k0erw-480.png 480w, https://kamadiam.com/img/built/UAjQ2k0erw-640.png 640w, https://kamadiam.com/img/built/UAjQ2k0erw-672.png 672w, https://kamadiam.com/img/built/UAjQ2k0erw-800.png 800w, https://kamadiam.com/img/built/UAjQ2k0erw-1030.png 1030w&quot; sizes=&quot;(max-width: 768px) 100vw, 65ch&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;三者都能拿來做網站，但解決的問題不同：&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Statamic&lt;/strong&gt;：適合需要控制面板、內容集合、欄位藍圖，又希望內容與設定能以檔案管理的團隊。它建立在 Laravel 上，前端可以使用 Antlers，也能依專案選擇其他做法。&lt;/li&gt;&lt;li&gt;&lt;strong&gt;WordPress&lt;/strong&gt;：適合想快速使用成熟後台、佈景主題和外掛生態的使用者。彈性很高，但網站的實際行為會取決於佈景主題、外掛和主機環境。&lt;/li&gt;&lt;li&gt;&lt;strong&gt;11ty&lt;/strong&gt;：適合偏好 Markdown、Git 和靜態輸出的開發者。它是靜態網站產生器，不是內建內容編輯控制面的 CMS；需要多人後台時，通常要另外整合服務。&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Statamic 的 flat-file 模式和 11ty 都重視檔案與版本控制，但使用感不同：11ty 偏向建置時把內容產生為靜態檔案；Statamic 則保留 CMS 的管理介面和動態應用程式能力。選擇時先問自己：編輯者需要後台嗎？內容是否需要複雜欄位？部署流程和團隊習慣是哪一種？&lt;/p&gt;&lt;h2 id=&quot;適合誰使用?&quot;&gt;適合誰使用？&lt;/h2&gt;&lt;p&gt;Statamic 適合以下情境：&lt;/p&gt;&lt;ul&gt;&lt;li&gt;內容團隊需要控制面板，但開發團隊想用 Git 管理內容結構和模板。&lt;/li&gt;&lt;li&gt;專案需要自訂 Collection、Blueprint、路由和前端設計。&lt;/li&gt;&lt;li&gt;團隊熟悉 Laravel、PHP 或希望沿用 Laravel 的開發方式。&lt;/li&gt;&lt;li&gt;想從 flat-file 開始，未來再依規模評估資料庫或 Headless 架構。&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;如果你只想寫幾篇 Markdown，並且不需要後台，11ty 可能更直接。如果你希望安裝主題、調整設定後立即上線，WordPress 的現成生態可能更省力。Statamic 的自由度很高，但自由度也代表你需要自己做一些決定；它不是「完全不用想」的 CMS，而是「可以決定要怎麼想」的 CMS。&lt;/p&gt;&lt;h2 id=&quot;結語&quot;&gt;結語&lt;/h2&gt;&lt;p&gt;Statamic 的核心魅力，不只是「不用資料庫」這一句話，而是把 CMS、檔案型內容、版本控制和 Laravel 開發流程接在一起。從 CLI 建立專案，設定 Collection，再用 Antlers 和 Partial 組出頁面，你可以逐步理解每一層，而不是一次吞下一整套黑盒子。&lt;/p&gt;&lt;p&gt;先做一個小部落格，確認內容模型和模板邏輯，再考慮外掛、Starter Kit、圖片處理或部署。網站開發像手沖咖啡：水溫、比例和流程都重要，但第一杯先喝得下去，比研究半天濾杯角度更重要。&lt;/p&gt;&lt;h2 id=&quot;參考資料&quot;&gt;參考資料&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://statamic.dev/getting-started/quick-start-guide&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Statamic Quick Start Guide&lt;/a&gt;：官方快速入門流程，涵蓋安裝、首頁、Collection 與模板。&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://statamic.dev/getting-started/installing/local&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Install Statamic with the CLI&lt;/a&gt;：官方 CLI 安裝需求與 &lt;code&gt;statamic new&lt;/code&gt; 建站流程。&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://statamic.dev/getting-started/installing/laravel-herd&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Install Statamic with Laravel Herd&lt;/a&gt;：官方 Herd 本機環境安裝說明。&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://statamic.dev/getting-started/core-concepts&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Core Concepts&lt;/a&gt;：官方說明 flat-file 模式、內容管理與架構取捨。&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://statamic.dev/content-modeling/collections&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Collections&lt;/a&gt;：官方說明 Collection、Entry 儲存方式與路由。&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://statamic.dev/frontend/antlers&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Antlers Templates&lt;/a&gt;：官方 Antlers 模板語法、檔案位置與 Layout 說明。&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://statamic.dev/starter-kits/overview&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Starter Kits Overview&lt;/a&gt;：官方說明 Starter Kits 和 WordPress themes 的差異。&lt;/li&gt;&lt;/ul&gt;</content>
  </entry>
  <entry>
    <title>SOLID 原則 PHP 實戰：五大設計原則速查 + 設計模式對照</title>
    <link href="https://kamadiam.com/solid-principles-in-php/" />
    <updated>2026-08-29T00:00:00Z</updated>
    <id>https://kamadiam.com/solid-principles-in-php/</id>
    <content type="html">&lt;p&gt;寫程式不難，難的是半年後回頭看，還敢不敢改自己的程式碼。當一個類別同時算薪水、寫資料庫、寄信，甚至順便幫你煮咖啡，需求一變就會像耳機線一樣打結。這時候，SOLID 原則可以當一張設計地圖。&lt;/p&gt;&lt;p&gt;這篇會用 PHP 範例帶你理解 SOLID 的五個原則：它們各自想解決什麼問題、怎麼拆出比較健康的設計，以及什麼時候不必硬套。讀完後，你應該能看出程式碼「哪裡開始變胖」，也知道該從哪裡下刀。&lt;/p&gt;&lt;h2 id=&quot;SOLID-速查表&quot;&gt;SOLID 速查表&lt;/h2&gt;&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;字母&lt;/th&gt;&lt;th&gt;原則&lt;/th&gt;&lt;th&gt;白話&lt;/th&gt;&lt;th&gt;違反時的症狀&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;S&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Single Responsibility&lt;/td&gt;&lt;td&gt;一個類別只有一個改變的理由&lt;/td&gt;&lt;td&gt;改 A 功能卻壞 B 功能&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;O&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Open/Closed&lt;/td&gt;&lt;td&gt;對擴充開放、對修改封閉&lt;/td&gt;&lt;td&gt;每加需求就改核心類別&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;L&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Liskov Substitution&lt;/td&gt;&lt;td&gt;子類別可替換父類別&lt;/td&gt;&lt;td&gt;繼承後行為不一致&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;I&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Interface Segregation&lt;/td&gt;&lt;td&gt;介面要小而專&lt;/td&gt;&lt;td&gt;被迫實作用不到的方法&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;D&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Dependency Inversion&lt;/td&gt;&lt;td&gt;依賴抽象，不依賴具體&lt;/td&gt;&lt;td&gt;高層直接 new 低層實作&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;blockquote&gt;&lt;p&gt;延伸閱讀：&lt;a href=&quot;https://kamadiam.com/php-object-and-class/&quot;&gt;PHP 類別入門&lt;/a&gt; · &lt;a href=&quot;https://kamadiam.com/php-basic-tutorial/&quot;&gt;PHP 設計模式系列&lt;/a&gt;&lt;/p&gt;&lt;/blockquote&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/solid-principles-in-php/#SOLID-%E9%80%9F%E6%9F%A5%E8%A1%A8&quot;&gt;SOLID 速查表&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/solid-principles-in-php/#SOLID-%E6%98%AF%E4%BB%80%E9%BA%BC?&quot;&gt;SOLID 是什麼？&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/solid-principles-in-php/#S%E5%96%AE%E4%B8%80%E8%81%B7%E8%B2%AC%E5%8E%9F%E5%89%87SRP&quot;&gt;S：單一職責原則（SRP）&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/solid-principles-in-php/#O%E9%96%8B%E6%94%BE%E5%B0%81%E9%96%89%E5%8E%9F%E5%89%87OCP&quot;&gt;O：開放封閉原則（OCP）&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/solid-principles-in-php/#L%E9%87%8C%E6%B0%8F%E6%9B%BF%E6%8F%9B%E5%8E%9F%E5%89%87LSP&quot;&gt;L：里氏替換原則（LSP）&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/solid-principles-in-php/#I%E4%BB%8B%E9%9D%A2%E9%9A%94%E9%9B%A2%E5%8E%9F%E5%89%87ISP&quot;&gt;I：介面隔離原則（ISP）&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/solid-principles-in-php/#D%E4%BE%9D%E8%B3%B4%E5%8F%8D%E8%BD%89%E5%8E%9F%E5%89%87DIP&quot;&gt;D：依賴反轉原則（DIP）&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/solid-principles-in-php/#SOLID-%E8%88%87%E8%A8%AD%E8%A8%88%E6%A8%A1%E5%BC%8F%E6%80%8E%E9%BA%BC%E5%88%86?&quot;&gt;SOLID 與設計模式怎麼分？&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/solid-principles-in-php/#%E5%AF%A6%E5%8B%99%E4%B8%8A%E6%80%8E%E9%BA%BC%E4%BD%BF%E7%94%A8-SOLID?&quot;&gt;實務上怎麼使用 SOLID？&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/solid-principles-in-php/#%E7%B8%BD%E7%B5%90&quot;&gt;總結&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/solid-principles-in-php/#%E5%B8%B8%E8%A6%8B-FAQ&quot;&gt;常見 FAQ&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/solid-principles-in-php/#%E5%8F%83%E8%80%83%E8%B3%87%E6%96%99&quot;&gt;參考資料&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;h2 id=&quot;SOLID-是什麼?&quot;&gt;SOLID 是什麼？&lt;/h2&gt;&lt;p&gt;SOLID 是五個物件導向設計原則英文名稱的首字母：&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;S — Single Responsibility Principle（單一職責原則）&lt;/strong&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;O — Open/Closed Principle（開放封閉原則）&lt;/strong&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;L — Liskov Substitution Principle（里氏替換原則）&lt;/strong&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;I — Interface Segregation Principle（介面隔離原則）&lt;/strong&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;D — Dependency Inversion Principle（依賴反轉原則）&lt;/strong&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;它們不是 PHP 的語法，也不是一份「照著做就永遠正確」的檢查表，而是幫助你降低耦合、控制變動範圍的思考工具。換句話說，SOLID 的重點不是把類別拆到像樂高散落一地，而是讓每個變動有比較清楚的落點。&lt;/p&gt;&lt;h2 id=&quot;S單一職責原則SRP&quot;&gt;S：單一職責原則（SRP）&lt;/h2&gt;&lt;p&gt;單一職責原則常被簡化成「一個類別只做一件事」。這句話方向沒錯，但還不夠精準。Robert C. Martin 對 SRP 的經典說法是：一個模組應該只有一個「改變的理由」。&lt;/p&gt;&lt;p&gt;這裡的「職責」不是方法數量，而是誰會要求它改變。薪資規則由人資提出修改、資料庫格式由後端提出修改、畫面格式由前端提出修改；如果三種需求都集中在 &lt;code&gt;Employee&lt;/code&gt;，它就背了太多不同的改變理由。&lt;/p&gt;&lt;h3 id=&quot;容易失控的寫法&quot;&gt;容易失控的寫法&lt;/h3&gt;&lt;pre class=&quot;language-php&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;EmployeeService&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;calculateSalary&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name type-declaration&quot;&gt;Employee&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$employee&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 return-type&quot;&gt;int&lt;/span&gt;
	&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 variable&quot;&gt;$employee&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;baseSalary&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;+&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$employee&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;bonus&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;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name type-declaration&quot;&gt;Employee&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$employee&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 return-type&quot;&gt;void&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 keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;renderProfile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name type-declaration&quot;&gt;Employee&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$employee&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 return-type&quot;&gt;string&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// 組合 HTML&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;&amp;lt;p&gt;&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$employee&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;name&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;.&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;&amp;lt;/p&gt;&#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;這個類別現在知道薪資規則、資料儲存和 HTML。任何一項需求變更，都可能碰到同一個檔案。類別不是不能有三個方法，而是這三個方法的變動原因彼此無關。&lt;/p&gt;&lt;h3 id=&quot;讓職責各就各位&quot;&gt;讓職責各就各位&lt;/h3&gt;&lt;pre class=&quot;language-php&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;SalaryCalculator&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;calculate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name type-declaration&quot;&gt;Employee&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$employee&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 return-type&quot;&gt;int&lt;/span&gt;
	&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 variable&quot;&gt;$employee&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;baseSalary&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;+&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$employee&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;bonus&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;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;EmployeeRepository&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name type-declaration&quot;&gt;Employee&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$employee&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 return-type&quot;&gt;void&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;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;EmployeeProfileRenderer&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name type-declaration&quot;&gt;Employee&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$employee&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 return-type&quot;&gt;string&lt;/span&gt;
	&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 string single-quoted-string&quot;&gt;&#39;&amp;lt;p&gt;&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$employee&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;name&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;.&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;&amp;lt;/p&gt;&#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;拆開後，薪資規則、儲存方式與顯示格式各自有清楚的責任邊界，也比較容易單獨測試。SRP 不是「每個類別只能有一個 public method」，而是「不要把不同的變動原因綁在一起」。&lt;/p&gt;&lt;h2 id=&quot;O開放封閉原則OCP&quot;&gt;O：開放封閉原則（OCP）&lt;/h2&gt;&lt;p&gt;開放封閉原則是：軟體實體應該對擴充開放，對修改封閉。意思不是原始碼永遠不能改，而是當你新增一種行為時，最好能透過新增實作來完成，避免反覆修改已經穩定的核心流程。&lt;/p&gt;&lt;h3 id=&quot;if-越長,咖啡越苦&quot;&gt;&lt;code&gt;if&lt;/code&gt; 越長，咖啡越苦&lt;/h3&gt;&lt;p&gt;假設結帳服務用條件判斷處理每種折扣：&lt;/p&gt;&lt;pre class=&quot;language-php&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;discountFor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-hint&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$memberType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword type-hint&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$amount&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 return-type&quot;&gt;int&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 variable&quot;&gt;$memberType&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;regular&#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 keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&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 variable&quot;&gt;$memberType&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;vip&#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 keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-casting&quot;&gt;int&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 variable&quot;&gt;$amount&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.1&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 variable&quot;&gt;$memberType&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;staff&#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 keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-casting&quot;&gt;int&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 variable&quot;&gt;$amount&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.3&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;return&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&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;if&lt;/code&gt; 迷宮。迷宮本身沒有錯，但維護它通常不會讓人心情變好。&lt;/p&gt;&lt;h3 id=&quot;將變動的規則抽成策略&quot;&gt;將變動的規則抽成策略&lt;/h3&gt;&lt;pre class=&quot;language-php&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;DiscountPolicy&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;discountFor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-hint&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$amount&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 return-type&quot;&gt;int&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;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;VipDiscount&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DiscountPolicy&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;discountFor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-hint&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$amount&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 return-type&quot;&gt;int&lt;/span&gt;
	&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;&lt;span class=&quot;token keyword type-casting&quot;&gt;int&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 variable&quot;&gt;$amount&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.1&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;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;StaffDiscount&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DiscountPolicy&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;discountFor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-hint&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$amount&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 return-type&quot;&gt;int&lt;/span&gt;
	&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;&lt;span class=&quot;token keyword type-casting&quot;&gt;int&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 variable&quot;&gt;$amount&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.3&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;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;Checkout&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;__construct&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name type-declaration&quot;&gt;DiscountPolicy&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$discountPolicy&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;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;total&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-hint&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$amount&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 return-type&quot;&gt;int&lt;/span&gt;
	&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 variable&quot;&gt;$amount&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;discountPolicy&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;discountFor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$amount&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;DiscountPolicy&lt;/code&gt; 實作，&lt;code&gt;Checkout&lt;/code&gt; 不必跟著修改。這就是常見的 Strategy（策略）做法，也是 OCP 的一種實現方式。&lt;/p&gt;&lt;p&gt;不過，OCP 不代表每一個小函式都要先抽介面。尚未出現變動點時過度抽象，反而會增加理解成本。先觀察真正會變的地方，再決定要不要抽象，通常比較務實。&lt;/p&gt;&lt;h2 id=&quot;L里氏替換原則LSP&quot;&gt;L：里氏替換原則（LSP）&lt;/h2&gt;&lt;p&gt;里氏替換原則要求：如果程式碼期待的是某個父型別或介面，那麼換成它的子型別或實作後，原本的正確性與使用方式仍然成立。&lt;/p&gt;&lt;p&gt;重點不是「有 &lt;code&gt;extends&lt;/code&gt; 就算繼承成功」，而是子類別必須遵守父類別對外做出的契約。若父類別承諾「呼叫 &lt;code&gt;withdraw()&lt;/code&gt; 會扣款」，子類別就不能突然改成「遇到某種帳戶就丟出不支援例外」，否則呼叫端就得先猜對方是哪一種物件。&lt;/p&gt;&lt;h3 id=&quot;會讓呼叫端踩雷的繼承&quot;&gt;會讓呼叫端踩雷的繼承&lt;/h3&gt;&lt;pre class=&quot;language-php&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;Bird&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;fly&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 return-type&quot;&gt;void&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;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;Penguin&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Bird&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;fly&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 return-type&quot;&gt;void&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LogicException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;企鵝不會飛&#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 punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;letBirdFly&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name type-declaration&quot;&gt;Bird&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$bird&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 return-type&quot;&gt;void&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token variable&quot;&gt;$bird&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fly&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;letBirdFly()&lt;/code&gt; 接受 &lt;code&gt;Bird&lt;/code&gt;，照它的契約自然會呼叫 &lt;code&gt;fly()&lt;/code&gt;。傳入 &lt;code&gt;Penguin&lt;/code&gt; 後卻必定失敗，代表這個繼承關係只是在語法上成立，在行為上卻不成立。&lt;/p&gt;&lt;h3 id=&quot;讓抽象符合真正能力&quot;&gt;讓抽象符合真正能力&lt;/h3&gt;&lt;pre class=&quot;language-php&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;Animal&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;interface&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;Flyable&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;fly&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 return-type&quot;&gt;void&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;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;Sparrow&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Animal&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Flyable&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;fly&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 return-type&quot;&gt;void&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;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;Penguin&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Animal&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token comment&quot;&gt;// 企鵝是動物，但不是 Flyable&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;這裡不是把企鵝排除在鳥類之外，而是避免對「會飛」做出錯誤承諾。設計抽象時，先問「這個型別真的能保證什麼行為？」比先問「它是不是某某的子類別？」更重要。&lt;/p&gt;&lt;h2 id=&quot;I介面隔離原則ISP&quot;&gt;I：介面隔離原則（ISP）&lt;/h2&gt;&lt;p&gt;介面隔離原則主張：不要讓使用者依賴它用不到的方法。這裡的使用者可以是實作介面的類別，也可以是呼叫該介面的其他程式碼。&lt;/p&gt;&lt;h3 id=&quot;一個什麼都管的介面&quot;&gt;一個什麼都管的介面&lt;/h3&gt;&lt;pre class=&quot;language-php&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;Worker&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;code&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 return-type&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;test&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 return-type&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;cook&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 return-type&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;clean&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 return-type&quot;&gt;void&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;Developer&lt;/code&gt; 只需要寫程式與測試，卻被迫實作 &lt;code&gt;cook()&lt;/code&gt; 和 &lt;code&gt;clean()&lt;/code&gt;，介面就太胖了。更麻煩的是，介面新增一個方法時，所有實作者都可能被迫同步修改。&lt;/p&gt;&lt;h3 id=&quot;拆成能組合的小介面&quot;&gt;拆成能組合的小介面&lt;/h3&gt;&lt;pre class=&quot;language-php&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;Coder&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;code&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 return-type&quot;&gt;void&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;interface&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;Tester&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;test&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 return-type&quot;&gt;void&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;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;Developer&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Coder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Tester
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;code&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 return-type&quot;&gt;void&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 keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;test&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 return-type&quot;&gt;void&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;/code&gt;&lt;/pre&gt;&lt;p&gt;小介面讓依賴關係更精準，也讓類別可以用組合表達能力。介面不是自助餐，不需要把所有方法都端上桌；端太多，最後通常只會剩下沒人想吃的抽象。&lt;/p&gt;&lt;h2 id=&quot;D依賴反轉原則DIP&quot;&gt;D：依賴反轉原則（DIP）&lt;/h2&gt;&lt;p&gt;依賴反轉原則有兩層意思：&lt;/p&gt;&lt;ol&gt;&lt;li&gt;高層模組不應直接依賴低層模組；兩者都應依賴抽象。&lt;/li&gt;&lt;li&gt;抽象不應依賴細節；細節應依賴抽象。&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;高層模組是業務規則，例如「註冊使用者」；低層模組則可能是 MySQL、檔案系統或第三方 API。業務規則不需要知道資料是存在哪一台資料庫，才能完成自己的工作。&lt;/p&gt;&lt;h3 id=&quot;把具體資料庫寫死&quot;&gt;把具體資料庫寫死&lt;/h3&gt;&lt;pre class=&quot;language-php&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;MySqlUserRepository&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-hint&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$email&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 return-type&quot;&gt;void&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// 寫入 MySQL&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;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;RegisterUser&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name type-declaration&quot;&gt;MySqlUserRepository&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$repository&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;__construct&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 variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;repository&lt;/span&gt; &lt;span class=&quot;token operator&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;MySqlUserRepository&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;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;handle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-hint&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$email&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 return-type&quot;&gt;void&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;repository&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$email&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;RegisterUser&lt;/code&gt; 自己建立 &lt;code&gt;MySqlUserRepository&lt;/code&gt;，所以測試時很難替換成記憶體版本，未來改用其他儲存方式也得修改業務類別。&lt;/p&gt;&lt;h3 id=&quot;讓高層依賴抽象&quot;&gt;讓高層依賴抽象&lt;/h3&gt;&lt;pre class=&quot;language-php&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;UserRepository&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-hint&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$email&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 return-type&quot;&gt;void&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;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;MySqlUserRepository&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UserRepository&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-hint&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$email&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 return-type&quot;&gt;void&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// 寫入 MySQL&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;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;RegisterUser&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;__construct&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name type-declaration&quot;&gt;UserRepository&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$repository&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;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;handle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-hint&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$email&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 return-type&quot;&gt;void&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;repository&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$email&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;RegisterUser&lt;/code&gt; 只依賴 &lt;code&gt;UserRepository&lt;/code&gt; 這個抽象，具體實作透過建構子注入。測試時可以傳入一個測試用 repository；正式環境則傳入 &lt;code&gt;MySqlUserRepository&lt;/code&gt;。這通常也被稱為依賴注入，但要注意：&lt;strong&gt;依賴注入是達成解耦的一種手法，DIP 是設計原則&lt;/strong&gt;，兩者不是同義詞。&lt;/p&gt;&lt;h2 id=&quot;SOLID-與設計模式怎麼分?&quot;&gt;SOLID 與設計模式怎麼分？&lt;/h2&gt;&lt;p&gt;SOLID 是設計時的原則；設計模式則是針對常見問題整理出的解法。兩者可以互相呼應，但不是一對一的配對表。&lt;/p&gt;&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;原則&lt;/th&gt;&lt;th&gt;常見呼應方式&lt;/th&gt;&lt;th&gt;重點&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;SRP&lt;/td&gt;&lt;td&gt;Facade、Repository&lt;/td&gt;&lt;td&gt;把不同變動原因分開&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;OCP&lt;/td&gt;&lt;td&gt;Strategy、Decorator&lt;/td&gt;&lt;td&gt;新增行為時減少修改核心&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;LSP&lt;/td&gt;&lt;td&gt;多型、合約測試&lt;/td&gt;&lt;td&gt;子型別遵守抽象契約&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ISP&lt;/td&gt;&lt;td&gt;小型角色介面&lt;/td&gt;&lt;td&gt;只依賴真正需要的能力&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;DIP&lt;/td&gt;&lt;td&gt;Dependency Injection、Factory&lt;/td&gt;&lt;td&gt;高層依賴抽象&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;例如，Factory 解決的是「物件怎麼建立」，Singleton 解決的是「限制實例數量」。它們都可能出現在某個遵循 DIP 的設計裡，但使用 Singleton 不會自動讓程式變得 SOLID；全域狀態該難測，還是會難測。&lt;/p&gt;&lt;h2 id=&quot;實務上怎麼使用-SOLID?&quot;&gt;實務上怎麼使用 SOLID？&lt;/h2&gt;&lt;p&gt;SOLID 最適合拿來處理「已經看見的變動」與「正在造成痛苦的耦合」，不適合當成開工前的儀式。你可以用下面幾個問題做快速檢查：&lt;/p&gt;&lt;ol&gt;&lt;li&gt;這個類別是否同時受到不同角色的需求影響？如果是，回頭看 SRP。&lt;/li&gt;&lt;li&gt;每次新增類型都要修改同一串條件判斷嗎？如果是，考慮 OCP。&lt;/li&gt;&lt;li&gt;子類別是否需要丟出「這個方法我不支援」的例外？如果是，檢查 LSP。&lt;/li&gt;&lt;li&gt;類別是否被迫實作或依賴一大堆用不到的方法？如果是，看看 ISP。&lt;/li&gt;&lt;li&gt;業務邏輯是否直接 &lt;code&gt;new&lt;/code&gt; 出資料庫、郵件服務或第三方 SDK？如果是，考慮 DIP。&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;最後也別忘了 YAGNI：還沒有需求、沒有變動跡象時，先寫清楚簡單的程式碼，通常比預先蓋一座抽象城堡更好維護。SOLID 的目標是降低變更成本，不是增加類別數量。類別多到需要地圖才能找到入口，那就不是 SOLID，是迷宮設計。&lt;/p&gt;&lt;h2 id=&quot;總結&quot;&gt;總結&lt;/h2&gt;&lt;p&gt;SOLID 五原則可以濃縮成一句話：讓程式碼的責任清楚、變動可控、依賴可替換，並且讓抽象真正符合它承諾的行為。&lt;/p&gt;&lt;p&gt;你不需要一次重寫整個專案。下一次看到超長類別、巨型 &lt;code&gt;if&lt;/code&gt;、不自然的繼承，先挑一個最痛的地方改善，跑測試，再觀察設計是否更容易理解。好的設計通常不是一杯咖啡喝完突然出現，而是每次修改少一點心驚膽跳。&lt;/p&gt;&lt;h2 id=&quot;常見-FAQ&quot;&gt;常見 FAQ&lt;/h2&gt;&lt;h3 id=&quot;SOLID-一定要五個全用嗎?&quot;&gt;SOLID 一定要五個全用嗎？&lt;/h3&gt;&lt;p&gt;不用。SOLID 是思考工具，不是開工 checklist。先從最痛的地方（通常是 SRP 或 DIP）下手即可。&lt;/p&gt;&lt;h3 id=&quot;SOLID-和設計模式衝突嗎?&quot;&gt;SOLID 和設計模式衝突嗎？&lt;/h3&gt;&lt;p&gt;不衝突。設計模式是具體解法，SOLID 是設計方向。例如 Factory 常配合 DIP 使用，但 Singleton 濫用可能違反 SRP。&lt;/p&gt;&lt;h3 id=&quot;小專案也要-SOLID-嗎?&quot;&gt;小專案也要 SOLID 嗎？&lt;/h3&gt;&lt;p&gt;小腳本、一次性工具不必。當程式碼會被多人維護、需求會持續變動時，SOLID 的投報才明顯。&lt;/p&gt;&lt;h2 id=&quot;參考資料&quot;&gt;參考資料&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://blog.cleancoder.com/uncle-bob/2014/05/08/SingleResponsibilityPrinciple.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;The Single Responsibility Principle — Robert C. Martin&lt;/a&gt;：說明 SRP 的「一個改變理由」觀點。&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://blog.cleancoder.com/uncle-bob/2014/05/12/TheOpenClosedPrinciple.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;The Open Closed Principle — Robert C. Martin&lt;/a&gt;：介紹 OCP 與擴充、修改之間的取捨。&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Liskov_substitution_principle&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Liskov Substitution Principle — Wikipedia&lt;/a&gt;：整理里氏替換原則的正式定義與背景。&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://www.php.net/manual/en/language.oop5.interfaces.php&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;PHP 手冊：物件介面&lt;/a&gt;：查閱 PHP 介面的宣告與實作規則。&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://www.php.net/manual/en/language.oop5.decon.php&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;PHP 手冊：建構子與解構子&lt;/a&gt;：查閱範例中建構子注入所使用的 PHP 物件語法。&lt;/li&gt;&lt;/ul&gt;</content>
  </entry>
  <entry>
    <title>PHP 類別與物件入門：Class／Object 完全指南</title>
    <link href="https://kamadiam.com/php-object-and-class/" />
    <updated>2026-08-29T00:00:00Z</updated>
    <id>https://kamadiam.com/php-object-and-class/</id>
    <content type="html">&lt;p&gt;如果你曾經寫過一串「先宣告資料、再呼叫函式、最後祈禱不要改壞其他地方」的 PHP，歡迎來到物件導向的世界。這裡會把資料和相關行為放在一起，讓程式不必像咖啡店的桌面一樣，什麼東西都堆在同一區。&lt;/p&gt;&lt;p&gt;這篇會用簡單的 PHP 範例帶你理解類別（Class）、物件（Object）、屬性、方法、&lt;code&gt;$this&lt;/code&gt; 與 &lt;code&gt;new&lt;/code&gt;，也會說明封裝和物件之間的組合。讀完後，你應該能看懂基本的 OOP 程式，並開始自己設計小型類別。&lt;/p&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/php-object-and-class/#%E7%82%BA%E4%BB%80%E9%BA%BC%E8%A6%81%E5%AD%B8-PHP-%E7%89%A9%E4%BB%B6%E5%B0%8E%E5%90%91&quot;&gt;為什麼要學 PHP 物件導向&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/php-object-and-class/#%E9%A1%9E%E5%88%A5%E8%88%87%E7%89%A9%E4%BB%B6%E6%98%AF%E4%BB%80%E9%BA%BC&quot;&gt;類別與物件是什麼&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/php-object-and-class/#%E7%94%A8-PHP-%E5%BB%BA%E7%AB%8B%E7%AC%AC%E4%B8%80%E5%80%8B%E9%A1%9E%E5%88%A5&quot;&gt;用 PHP 建立第一個類別&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/php-object-and-class/#%E7%94%A8%E9%A1%9E%E5%88%A5%E7%AE%A1%E7%90%86%E6%9B%B8%E7%B1%8D&quot;&gt;用類別管理書籍&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/php-object-and-class/#%E5%B0%81%E8%A3%9D%E8%88%87%E5%8F%AF%E8%A6%8B%E6%80%A7&quot;&gt;封裝與可見性&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/php-object-and-class/#%E9%80%99%E7%AF%87%E7%9A%84%E9%87%8D%E9%BB%9E%E6%95%B4%E7%90%86&quot;&gt;這篇的重點整理&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/php-object-and-class/#%E5%8F%83%E8%80%83%E8%B3%87%E6%96%99&quot;&gt;參考資料&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;h2 id=&quot;為什麼要學-PHP-物件導向&quot;&gt;為什麼要學 PHP 物件導向&lt;/h2&gt;&lt;p&gt;物件導向程式設計（Object-Oriented Programming，OOP）不是把每個變數都改名成物件，也不是看到 &lt;code&gt;class&lt;/code&gt; 就要立刻召開架構會議。它是一種組織程式的方式：把相關的資料和操作資料的程式碼放在同一個類別裡。&lt;/p&gt;&lt;p&gt;這樣做有幾個實際好處：&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;更容易組織&lt;/strong&gt;：一個類別可以集中描述某種事物的資料與行為。&lt;/li&gt;&lt;li&gt;&lt;strong&gt;減少重複&lt;/strong&gt;：同一個類別可以建立多個物件，不必複製一整套程式碼。&lt;/li&gt;&lt;li&gt;&lt;strong&gt;比較容易維護&lt;/strong&gt;：需求改變時，通常可以先從相關類別下手，不必在整個專案裡玩尋寶。&lt;/li&gt;&lt;li&gt;&lt;strong&gt;方便協作&lt;/strong&gt;：大型專案可以依照責任拆分不同類別，讓程式碼邊界更清楚。&lt;/li&gt;&lt;li&gt;&lt;strong&gt;支援封裝&lt;/strong&gt;：可以限制外部直接修改資料的方式，降低資料被亂改的機會。&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;OOP 不會自動讓所有程式變漂亮；類別設計得不好，仍然可能長成一團義大利麵，只是現在義大利麵住進了 &lt;code&gt;class&lt;/code&gt; 裡。&lt;/p&gt;&lt;h2 id=&quot;類別與物件是什麼&quot;&gt;類別與物件是什麼&lt;/h2&gt;&lt;p&gt;先用一個生活化的比喻：&lt;strong&gt;類別像設計圖，物件像依照設計圖做出來的成品&lt;/strong&gt;。&lt;/p&gt;&lt;p&gt;例如，&lt;code&gt;Cake&lt;/code&gt; 類別可以描述蛋糕有哪些資料（口味、尺寸），以及可以做什麼事（烘烤）。依照這個類別建立出來的巧克力蛋糕和草莓蛋糕，就是兩個不同的物件。&lt;/p&gt;&lt;p&gt;換句話說：&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;類別（Class）&lt;/strong&gt;：定義物件應該有哪些屬性和方法。&lt;/li&gt;&lt;li&gt;&lt;strong&gt;物件（Object）&lt;/strong&gt;：類別建立出來的實例，擁有自己的資料。&lt;/li&gt;&lt;li&gt;&lt;strong&gt;實例化（Instantiation）&lt;/strong&gt;：使用 &lt;code&gt;new&lt;/code&gt;，依照類別建立物件的過程。&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;類別本身比較像規格，物件才是程式執行時真正拿來工作的東西。設計圖不能自己吃蛋糕，這點 PHP 也沒有打算替它開例外。&lt;/p&gt;&lt;h2 id=&quot;用-PHP-建立第一個類別&quot;&gt;用 PHP 建立第一個類別&lt;/h2&gt;&lt;h3 id=&quot;類別包含屬性與方法&quot;&gt;類別包含屬性與方法&lt;/h3&gt;&lt;p&gt;以下建立一個簡單的 &lt;code&gt;Cake&lt;/code&gt; 類別：&lt;/p&gt;&lt;pre class=&quot;language-php&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token php language-php&quot;&gt;&lt;span class=&quot;token delimiter important&quot;&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;Cake&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$flavor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;bake&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;echo&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;正在烘烤 &#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;flavor&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39; 口味的蛋糕。&#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&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;這段程式有兩種成員：&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;flavor&lt;/code&gt; 和 &lt;code&gt;size&lt;/code&gt; 是&lt;strong&gt;屬性（Property）&lt;/strong&gt;，用來保存物件的資料。&lt;/li&gt;&lt;li&gt;&lt;code&gt;bake()&lt;/code&gt; 是&lt;strong&gt;方法（Method）&lt;/strong&gt;，用來描述物件可以執行的行為。&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;方法裡的 &lt;code&gt;$this&lt;/code&gt; 代表「目前正在呼叫這個方法的物件」。因此，&lt;code&gt;$this-&amp;gt;flavor&lt;/code&gt; 讀取的是目前這個蛋糕自己的口味，而不是某個神秘的全域蛋糕。&lt;/p&gt;&lt;h3 id=&quot;使用-new-建立物件&quot;&gt;使用 &lt;code&gt;new&lt;/code&gt; 建立物件&lt;/h3&gt;&lt;p&gt;有了類別之後，可以用 &lt;code&gt;new&lt;/code&gt; 建立物件，再設定它的屬性：&lt;/p&gt;&lt;pre class=&quot;language-php&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$chocolateCake&lt;/span&gt; &lt;span class=&quot;token operator&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;Cake&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 variable&quot;&gt;$chocolateCake&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;flavor&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;巧克力&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$chocolateCake&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;size&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;大&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token variable&quot;&gt;$chocolateCake&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;bake&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;$chocolateCake&lt;/code&gt; 是 &lt;code&gt;Cake&lt;/code&gt; 的一個物件。箭頭運算子 &lt;code&gt;-&amp;gt;&lt;/code&gt; 用來存取物件的屬性或方法，所以：&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;$chocolateCake-&amp;gt;flavor&lt;/code&gt; 代表這個物件的 &lt;code&gt;flavor&lt;/code&gt; 屬性。&lt;/li&gt;&lt;li&gt;&lt;code&gt;$chocolateCake-&amp;gt;bake()&lt;/code&gt; 代表呼叫這個物件的 &lt;code&gt;bake()&lt;/code&gt; 方法。&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;同一個類別也能建立多個彼此獨立的物件：&lt;/p&gt;&lt;pre class=&quot;language-php&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$chocolateCake&lt;/span&gt; &lt;span class=&quot;token operator&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;Cake&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 variable&quot;&gt;$chocolateCake&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;flavor&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;巧克力&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token variable&quot;&gt;$strawberryCake&lt;/span&gt; &lt;span class=&quot;token operator&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;Cake&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 variable&quot;&gt;$strawberryCake&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;flavor&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;草莓&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token variable&quot;&gt;$chocolateCake&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;bake&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 variable&quot;&gt;$strawberryCake&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;bake&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;Cake&lt;/code&gt; 類別，但各自保存不同的 &lt;code&gt;flavor&lt;/code&gt;。類別像食譜，物件則是不同口味的成品；PHP 沒有規定所有蛋糕都得是巧克力，算是對甜點界的基本尊重。&lt;/p&gt;&lt;h2 id=&quot;用類別管理書籍&quot;&gt;用類別管理書籍&lt;/h2&gt;&lt;p&gt;接著把概念放進一個比較完整的例子。假設我們要描述一本書，可以把書名和作者放進屬性，再提供一個方法顯示書籍資訊：&lt;/p&gt;&lt;pre class=&quot;language-php&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;Book&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$author&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;describe&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;return&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;title&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;，作者：&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;author&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;/p&gt;&lt;pre class=&quot;language-php&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$book&lt;/span&gt; &lt;span class=&quot;token operator&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;Book&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 variable&quot;&gt;$book&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;title&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;哈比人&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$book&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;author&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;J. R. R. 托爾金&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$book&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;describe&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;Book&lt;/code&gt; 是類別，&lt;code&gt;$book&lt;/code&gt; 是物件。&lt;code&gt;describe()&lt;/code&gt; 不需要另外接收 &lt;code&gt;$book&lt;/code&gt;，因為它透過 &lt;code&gt;$this&lt;/code&gt; 就能取得目前物件的資料。&lt;/p&gt;&lt;h3 id=&quot;把多個物件放在一起&quot;&gt;把多個物件放在一起&lt;/h3&gt;&lt;p&gt;物件也可以成為另一個物件的資料。像書架可以保存多本 &lt;code&gt;Book&lt;/code&gt;：&lt;/p&gt;&lt;pre class=&quot;language-php&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;Bookshelf&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$books&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 punctuation&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;addBook&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name type-declaration&quot;&gt;Book&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$book&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 variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;books&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;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$book&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;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;listBooks&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;foreach&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;books&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$book&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;echo&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$book&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;describe&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;.&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;PHP_EOL&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;Bookshelf&lt;/code&gt;：&lt;/p&gt;&lt;pre class=&quot;language-php&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$firstBook&lt;/span&gt; &lt;span class=&quot;token operator&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;Book&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 variable&quot;&gt;$firstBook&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;title&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;哈比人&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$firstBook&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;author&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;J. R. R. 托爾金&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token variable&quot;&gt;$secondBook&lt;/span&gt; &lt;span class=&quot;token operator&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;Book&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 variable&quot;&gt;$secondBook&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;title&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;傲慢與偏見&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$secondBook&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;author&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;珍・奧斯汀&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token variable&quot;&gt;$bookshelf&lt;/span&gt; &lt;span class=&quot;token operator&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;Bookshelf&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 variable&quot;&gt;$bookshelf&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addBook&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$firstBook&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 variable&quot;&gt;$bookshelf&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addBook&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$secondBook&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 variable&quot;&gt;$bookshelf&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;listBooks&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;Bookshelf&lt;/code&gt; 的 &lt;code&gt;books&lt;/code&gt; 屬性是一個陣列，裡面放著多個 &lt;code&gt;Book&lt;/code&gt; 物件。這種「一個物件管理其他物件」的做法，就是物件組合（composition）的簡單例子。&lt;/p&gt;&lt;h2 id=&quot;封裝與可見性&quot;&gt;封裝與可見性&lt;/h2&gt;&lt;p&gt;前面的屬性使用 &lt;code&gt;public&lt;/code&gt;，代表類別外部也能直接讀寫。這對示範很方便，但在實際程式裡，未必希望任何地方都能隨意修改資料。&lt;/p&gt;&lt;p&gt;PHP 提供三種常見的可見性：&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;public&lt;/code&gt;：類別內外都可以存取。&lt;/li&gt;&lt;li&gt;&lt;code&gt;protected&lt;/code&gt;：類別本身和繼承它的子類別可以存取。&lt;/li&gt;&lt;li&gt;&lt;code&gt;private&lt;/code&gt;：只有宣告該成員的類別本身可以存取。&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;例如，把書名設為私有，並透過方法控制修改方式：&lt;/p&gt;&lt;pre class=&quot;language-php&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;Book&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;setTitle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$title&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 variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;title&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$title&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;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;getTitle&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;return&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;title&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 variable&quot;&gt;$book&lt;/span&gt; &lt;span class=&quot;token operator&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;Book&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 variable&quot;&gt;$book&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setTitle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;哈比人&#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 keyword&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$book&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getTitle&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;strong&gt;封裝（Encapsulation）&lt;/strong&gt;：把資料和操作資料的方法放在一起，並限制外部直接碰觸內部狀態的方式。日後如果需要檢查書名是否為空白，可以在 &lt;code&gt;setTitle()&lt;/code&gt; 裡加入規則，而不必搜尋整個專案的每一個賦值動作。&lt;/p&gt;&lt;h2 id=&quot;這篇的重點整理&quot;&gt;這篇的重點整理&lt;/h2&gt;&lt;p&gt;到這裡，你已經掌握 PHP OOP 的基本詞彙：&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;類別&lt;/strong&gt;是建立物件的定義，描述屬性與方法。&lt;/li&gt;&lt;li&gt;&lt;strong&gt;物件&lt;/strong&gt;是類別的實例，保存自己的資料。&lt;/li&gt;&lt;li&gt;&lt;strong&gt;屬性&lt;/strong&gt;代表物件的狀態，例如書名或作者。&lt;/li&gt;&lt;li&gt;&lt;strong&gt;方法&lt;/strong&gt;代表物件的行為，例如列出書籍資訊。&lt;/li&gt;&lt;li&gt;&lt;strong&gt;&lt;code&gt;new&lt;/code&gt;&lt;/strong&gt; 用來依照類別建立物件。&lt;/li&gt;&lt;li&gt;&lt;strong&gt;&lt;code&gt;$this&lt;/code&gt;&lt;/strong&gt; 在物件方法中代表目前的物件。&lt;/li&gt;&lt;li&gt;&lt;strong&gt;封裝&lt;/strong&gt;透過可見性與方法，控制資料如何被使用。&lt;/li&gt;&lt;li&gt;&lt;strong&gt;物件組合&lt;/strong&gt;可以讓一個物件管理其他物件，例如書架管理書籍。&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;當你開始看到程式裡有許多互相關聯的資料和函式時，可以問自己：「這些東西是不是其實屬於同一種事物？」如果答案是，那就可能是設計類別的好時機。先把責任分清楚，再來喝咖啡；不然最後連咖啡都要被迫繼承 &lt;code&gt;Everything&lt;/code&gt; 類別。&lt;/p&gt;&lt;h2 id=&quot;參考資料&quot;&gt;參考資料&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://www.php.net/manual/en/language.oop5.php&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;PHP 官方文件：Classes and Objects&lt;/a&gt;：查閱 PHP 類別與物件的完整說明。&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://www.php.net/manual/en/language.oop5.basic.php&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;PHP 官方文件：The Basics&lt;/a&gt;：了解類別宣告、物件建立與 &lt;code&gt;$this&lt;/code&gt; 的基本用法。&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://www.php.net/manual/en/language.oop5.visibility.php&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;PHP 官方文件：Visibility&lt;/a&gt;：查閱 &lt;code&gt;public&lt;/code&gt;、&lt;code&gt;protected&lt;/code&gt; 與 &lt;code&gt;private&lt;/code&gt; 的存取規則。&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://www.php.net/manual/en/language.oop5.properties.php&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;PHP 官方文件：Properties&lt;/a&gt;：了解類別屬性的宣告與物件屬性存取方式。&lt;/li&gt;&lt;/ul&gt;</content>
  </entry>
  <entry>
    <title>PHP try catch 教學：例外處理完整指南（含 finally）</title>
    <link href="https://kamadiam.com/php-exception-handling/" />
    <updated>2026-08-29T00:00:00Z</updated>
    <id>https://kamadiam.com/php-exception-handling/</id>
    <content type="html">&lt;p&gt;例外處理有點像替程式準備安全網：希望它永遠用不到，但真的出事時，總不能只對著錯誤畫面喝咖啡。這篇會用白話說明 PHP 的 &lt;code&gt;try&lt;/code&gt;、&lt;code&gt;catch&lt;/code&gt;、&lt;code&gt;finally&lt;/code&gt;、&lt;code&gt;Throwable&lt;/code&gt; 與自訂例外，讓你知道該在哪裡接住問題，也知道哪些問題根本不是同一種球。&lt;/p&gt;&lt;p&gt;讀完後，你應該能寫出基本的例外處理、依例外類型採取不同策略，並在資料庫操作中保留原始錯誤脈絡，而不是把所有錯誤都包成一句「好像哪裡怪怪的」。&lt;/p&gt;&lt;h2 id=&quot;語法速查&quot;&gt;語法速查&lt;/h2&gt;&lt;pre class=&quot;language-php&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;try&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 keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;SpecificException&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$e&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 comment&quot;&gt;// 處理特定例外&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Throwable&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$e&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 comment&quot;&gt;// 兜底：任何 Error 或 Exception&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;finally&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;/code&gt;&lt;/pre&gt;&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;關鍵字&lt;/th&gt;&lt;th&gt;用途&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;throw new Exception(&#39;msg&#39;)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;主動拋出例外&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;catch (Type $e)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;依類型接住&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;finally&lt;/code&gt;&lt;/td&gt;&lt;td&gt;清理資源（可省略）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;Throwable&lt;/code&gt;&lt;/td&gt;&lt;td&gt;PHP 7+ 同時涵蓋 Error 與 Exception&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&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/php-exception-handling/#%E8%AA%9E%E6%B3%95%E9%80%9F%E6%9F%A5&quot;&gt;語法速查&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/php-exception-handling/#%E5%85%88%E5%88%86%E6%B8%85%E6%A5%9A%E4%BE%8B%E5%A4%96%E9%8C%AF%E8%AA%A4%E8%88%87-Throwable&quot;&gt;先分清楚：例外、錯誤與 Throwable&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/php-exception-handling/#trycatch-%E8%88%87-finally-%E6%80%8E%E9%BA%BC%E5%90%88%E4%BD%9C?&quot;&gt;try、catch 與 finally 怎麼合作？&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/php-exception-handling/#%E6%AA%94%E6%A1%88%E6%93%8D%E4%BD%9C%E4%B8%8D%E4%B8%80%E5%AE%9A%E6%9C%83%E8%87%AA%E5%8B%95%E6%8B%8B%E5%87%BA%E4%BE%8B%E5%A4%96&quot;&gt;檔案操作不一定會自動拋出例外&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/php-exception-handling/#%E5%BB%BA%E7%AB%8B%E8%87%AA%E8%A8%82%E4%BE%8B%E5%A4%96&quot;&gt;建立自訂例外&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/php-exception-handling/#%E5%A4%9A%E5%80%8B-catch%E9%A0%86%E5%BA%8F%E5%B0%B1%E6%98%AF%E5%84%AA%E5%85%88%E6%AC%8A&quot;&gt;多個 catch：順序就是優先權&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/php-exception-handling/#finally-%E7%9A%84%E5%AF%A6%E5%8B%99%E7%94%A8%E6%B3%95&quot;&gt;finally 的實務用法&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/php-exception-handling/#%E5%AF%A6%E7%94%A8%E6%A1%88%E4%BE%8B%E5%8C%85%E8%A3%9D-PDO-%E4%BE%8B%E5%A4%96&quot;&gt;實用案例：包裝 PDO 例外&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/php-exception-handling/#%E5%B8%B8%E8%A6%8B%E5%95%8F%E9%A1%8C&quot;&gt;常見問題&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/php-exception-handling/#%E7%B8%BD%E7%B5%90&quot;&gt;總結&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/php-exception-handling/#%E5%8F%83%E8%80%83%E8%B3%87%E6%96%99&quot;&gt;參考資料&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;h2 id=&quot;先分清楚例外錯誤與-Throwable&quot;&gt;先分清楚：例外、錯誤與 &lt;code&gt;Throwable&lt;/code&gt;&lt;/h2&gt;&lt;p&gt;PHP 中可以被 &lt;code&gt;throw&lt;/code&gt; 拋出、再由 &lt;code&gt;catch&lt;/code&gt; 接住的物件，必須實作 &lt;code&gt;Throwable&lt;/code&gt; 介面。內建的 &lt;code&gt;Exception&lt;/code&gt; 與 &lt;code&gt;Error&lt;/code&gt; 都屬於這個家族，但兩者的繼承關係不同：&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;Exception&lt;/code&gt; 通常代表應用程式或函式庫預期可以處理的例外。&lt;/li&gt;&lt;li&gt;&lt;code&gt;Error&lt;/code&gt; 代表 PHP 執行期間發現的錯誤，例如部分型別或算術錯誤。&lt;/li&gt;&lt;li&gt;&lt;code&gt;Throwable&lt;/code&gt; 是兩者共同的上層介面。&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;因此，&lt;code&gt;catch (Exception $e)&lt;/code&gt; 不會接住 &lt;code&gt;Error&lt;/code&gt;。如果你確實需要同時處理兩者，可以捕獲 &lt;code&gt;Throwable&lt;/code&gt;；不過，這不代表所有錯誤都適合被吃掉。語法錯誤、設定問題或程式設計錯誤，通常應該在開發與記錄階段被找出，而不是在生產環境偷偷假裝沒事。&lt;/p&gt;&lt;h2 id=&quot;trycatch-與-finally-怎麼合作?&quot;&gt;&lt;code&gt;try&lt;/code&gt;、&lt;code&gt;catch&lt;/code&gt; 與 &lt;code&gt;finally&lt;/code&gt; 怎麼合作？&lt;/h2&gt;&lt;p&gt;這三個關鍵字各自負責一件事：&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;try&lt;/code&gt;：放入可能拋出例外的程式碼。&lt;/li&gt;&lt;li&gt;&lt;code&gt;catch&lt;/code&gt;：接住符合指定類型的 &lt;code&gt;Throwable&lt;/code&gt;，並決定如何回應。&lt;/li&gt;&lt;li&gt;&lt;code&gt;finally&lt;/code&gt;：不論是否發生例外，都在流程離開 &lt;code&gt;try&lt;/code&gt;／&lt;code&gt;catch&lt;/code&gt; 前執行清理工作。&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;一個最小但完整的例子如下：&lt;/p&gt;&lt;pre class=&quot;language-php&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token php language-php&quot;&gt;&lt;span class=&quot;token delimiter important&quot;&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;divide&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-hint&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$dividend&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword type-hint&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$divisor&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 return-type&quot;&gt;float&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 variable&quot;&gt;$divisor&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&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;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;InvalidArgumentException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;除數不能是 0。&#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;return&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$dividend&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$divisor&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;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;divide&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&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;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;InvalidArgumentException&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$exception&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;echo&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;輸入無效：&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$exception&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getMessage&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;finally&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;PHP_EOL&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;這段清理流程一定會執行。&#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&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;throw&lt;/code&gt; 之後，同一個區塊中尚未執行的程式碼會被跳過，PHP 會往外尋找第一個符合類型的 &lt;code&gt;catch&lt;/code&gt;。如果一路找不到，例外就會繼續往呼叫堆疊上層傳遞，最後可能成為未捕獲例外。&lt;/p&gt;&lt;p&gt;另外，&lt;code&gt;finally&lt;/code&gt; 不是「錯誤發生時才執行」的區塊；成功與失敗都會走到它。它很適合放關閉檔案、釋放鎖定或還原暫存狀態等清理工作。清理區塊也不要再次拋出無關例外，否則可能覆蓋原本更重要的錯誤——錯誤會排隊，但不保證乖乖排隊。&lt;/p&gt;&lt;h2 id=&quot;檔案操作不一定會自動拋出例外&quot;&gt;檔案操作不一定會自動拋出例外&lt;/h2&gt;&lt;p&gt;這是初學者很容易踩到的坑：並非所有 PHP 函式失敗時都會拋出 &lt;code&gt;Exception&lt;/code&gt;。例如 &lt;code&gt;fopen()&lt;/code&gt; 找不到檔案時，通常會回傳 &lt;code&gt;false&lt;/code&gt; 並產生警告；單純把它放進 &lt;code&gt;try&lt;/code&gt;，不會自動變成可捕獲的例外。&lt;/p&gt;&lt;p&gt;如果你的程式需要用例外流程處理這種失敗，就要明確檢查回傳值，再自行拋出例外：&lt;/p&gt;&lt;pre class=&quot;language-php&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token php language-php&quot;&gt;&lt;span class=&quot;token delimiter important&quot;&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;openFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-hint&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$path&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 variable&quot;&gt;$handle&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fopen&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;r&#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 keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$handle&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token constant 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 keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RuntimeException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;無法開啟檔案：&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$path&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;return&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$handle&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 variable&quot;&gt;$handle&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&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;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token variable&quot;&gt;$handle&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;openFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;__DIR__&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;/example.txt&#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 variable&quot;&gt;$content&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;stream_get_contents&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$handle&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;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RuntimeException&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$exception&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;echo&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;檔案處理失敗：&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$exception&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getMessage&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;finally&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 function&quot;&gt;is_resource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$handle&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 function&quot;&gt;fclose&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$handle&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&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;這裡的重點不是把每個警告都硬轉成例外，而是先確認你使用的 API 如何回報失敗，再選擇一致的處理方式。&lt;/p&gt;&lt;h2 id=&quot;建立自訂例外&quot;&gt;建立自訂例外&lt;/h2&gt;&lt;p&gt;當程式只丟出一個通用的 &lt;code&gt;Exception&lt;/code&gt;，呼叫端往往只能看訊息猜發生什麼事。建立有語意的例外類別，可以讓 &lt;code&gt;catch&lt;/code&gt; 更精準，也讓日後搜尋記錄檔容易很多。&lt;/p&gt;&lt;p&gt;自訂例外通常只要繼承 &lt;code&gt;Exception&lt;/code&gt; 或它的子類別即可：&lt;/p&gt;&lt;pre class=&quot;language-php&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token php language-php&quot;&gt;&lt;span class=&quot;token delimiter important&quot;&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;FileNotFoundException&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RuntimeException&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-definition function&quot;&gt;readConfig&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-hint&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$path&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 return-type&quot;&gt;string&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 operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;is_file&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$path&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;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;FileNotFoundException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;找不到設定檔：&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$path&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 variable&quot;&gt;$config&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;file_get_contents&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$path&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 variable&quot;&gt;$config&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token constant 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 keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RuntimeException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;無法讀取設定檔：&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$path&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;return&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$config&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;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token variable&quot;&gt;$config&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;readConfig&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;__DIR__&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;/config.php&#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;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;FileNotFoundException&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$exception&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;echo&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;設定檔不存在，請檢查部署內容。&#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&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;類別名稱本身就是文件。與其在每個地方解析一長串錯誤訊息，不如讓 &lt;code&gt;FileNotFoundException&lt;/code&gt; 直接說明「是哪一類問題」。&lt;/p&gt;&lt;p&gt;如果需要保留較低層的原因，可以在建立新例外時傳入上一個例外。這種做法稱為例外鏈結：&lt;/p&gt;&lt;pre class=&quot;language-php&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token php language-php&quot;&gt;&lt;span class=&quot;token delimiter important&quot;&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;DatabaseException&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RuntimeException&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;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token comment&quot;&gt;// 這裡假設資料庫函式庫拋出了 PDOException。&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PDOException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;連線被拒絕&#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;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;PDOException&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$exception&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;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DatabaseException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;無法完成資料庫操作。&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$exception&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&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;上層可以用 &lt;code&gt;getMessage()&lt;/code&gt; 取得對使用者較安全的訊息，也可以用 &lt;code&gt;getPrevious()&lt;/code&gt; 追查原始原因。請避免直接把資料庫帳號、密碼、完整 SQL 或堆疊資訊顯示給使用者；這些內容比較適合寫入受保護的伺服器記錄。&lt;/p&gt;&lt;h2 id=&quot;多個-catch順序就是優先權&quot;&gt;多個 &lt;code&gt;catch&lt;/code&gt;：順序就是優先權&lt;/h2&gt;&lt;p&gt;一個 &lt;code&gt;try&lt;/code&gt; 後面可以接多個 &lt;code&gt;catch&lt;/code&gt;。PHP 會從上到下比對，執行第一個符合的區塊。因此，具體的例外要放前面，較一般的類型放後面：&lt;/p&gt;&lt;pre class=&quot;language-php&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token php language-php&quot;&gt;&lt;span class=&quot;token delimiter important&quot;&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token comment&quot;&gt;// 可能拋出不同類型的 Throwable。&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;FileNotFoundException&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$exception&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;echo&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;找不到檔案。&#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 keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RuntimeException&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$exception&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;echo&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;執行時發生可預期的問題。&#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 keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Throwable&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$exception&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;echo&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;發生未預期的錯誤。&#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&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;反過來，如果先寫 &lt;code&gt;catch (Throwable $exception)&lt;/code&gt;，後面的區塊就永遠沒有機會接手。這就像先在門口安排一個「所有人都進來」的保全，後面再安排「只接待 PHP 開發者」的櫃檯，後者自然會失業。&lt;/p&gt;&lt;p&gt;若不同例外需要完全相同的處理方式，也可以使用聯合捕獲語法：&lt;/p&gt;&lt;pre class=&quot;language-php&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token php language-php&quot;&gt;&lt;span class=&quot;token delimiter important&quot;&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;try&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 keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;InvalidArgumentException&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UnexpectedValueException&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$exception&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;echo&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;收到無效的資料。&#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&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;finally-的實務用法&quot;&gt;&lt;code&gt;finally&lt;/code&gt; 的實務用法&lt;/h2&gt;&lt;p&gt;&lt;code&gt;finally&lt;/code&gt; 適合處理「無論成功或失敗都要做」的事情，例如關閉檔案、釋放鎖或還原狀態。它不適合拿來掩蓋錯誤，也不建議在裡面寫 &lt;code&gt;return&lt;/code&gt;，因為那可能覆蓋 &lt;code&gt;try&lt;/code&gt; 或 &lt;code&gt;catch&lt;/code&gt; 原本要回傳的值，讓除錯難度直接升級。&lt;/p&gt;&lt;p&gt;需要注意的是，&lt;code&gt;finally&lt;/code&gt; 不是萬能清潔工。若程式直接結束，或清理本身拋出新的例外，流程可能不會照你預想的方式完成。因此，清理程式應該保持簡單、可預期。&lt;/p&gt;&lt;h2 id=&quot;實用案例包裝-PDO-例外&quot;&gt;實用案例：包裝 PDO 例外&lt;/h2&gt;&lt;p&gt;以下用新增書籍的流程示範例外分層。範例明確設定 PDO 使用例外模式，避免把不同的錯誤回報方式混在一起：&lt;/p&gt;&lt;pre class=&quot;language-php&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token php language-php&quot;&gt;&lt;span class=&quot;token delimiter important&quot;&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;DatabaseException&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RuntimeException&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-definition function&quot;&gt;addBook&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name type-declaration&quot;&gt;PDO&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$db&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword type-hint&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword type-hint&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$author&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 return-type&quot;&gt;void&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token variable&quot;&gt;$statement&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$db&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;prepare&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
			&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;INSERT INTO books (title, author) VALUES (:title, :author)&#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 variable&quot;&gt;$statement&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;execute&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 single-quoted-string&quot;&gt;&#39;title&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;author&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$author&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;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;PDOException&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$exception&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;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DatabaseException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;新增書籍失敗。&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$exception&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;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token variable&quot;&gt;$db&lt;/span&gt; &lt;span class=&quot;token operator&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;PDO&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
		&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;mysql:host=localhost;dbname=library;charset=utf8mb4&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;token variable&quot;&gt;$username&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;token variable&quot;&gt;$password&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 class-name static-context&quot;&gt;PDO&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;ATTR_ERRMODE&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token class-name static-context&quot;&gt;PDO&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;ERRMODE_EXCEPTION&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 function&quot;&gt;addBook&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$db&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;1984&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;George Orwell&#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 keyword&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;書籍新增成功。&#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 keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;DatabaseException&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$exception&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;echo&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;目前無法新增書籍，請稍後再試。&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token comment&quot;&gt;// 實務上可在這裡記錄 $exception 與 getPrevious() 的資訊。&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;這個範例有三個值得保留的設計：&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;code&gt;addBook()&lt;/code&gt; 將底層的 &lt;code&gt;PDOException&lt;/code&gt; 包裝成應用程式看得懂的 &lt;code&gt;DatabaseException&lt;/code&gt;。&lt;/li&gt;&lt;li&gt;&lt;code&gt;throw new DatabaseException(..., 0, $exception)&lt;/code&gt; 保留原始例外，方便記錄與除錯。&lt;/li&gt;&lt;li&gt;對外只顯示安全、友善的訊息，不把資料庫細節直接送到瀏覽器。&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;資料庫連線的生命週期也不必硬塞進 &lt;code&gt;finally&lt;/code&gt;。當 &lt;code&gt;$db&lt;/code&gt; 離開作用域且沒有其他參照時，PHP 會回收物件；真正需要 &lt;code&gt;finally&lt;/code&gt; 的情境，應該是你有明確的資源或狀態需要無條件清理。&lt;/p&gt;&lt;h2 id=&quot;常見問題&quot;&gt;常見問題&lt;/h2&gt;&lt;h3 id=&quot;catch-Exception-$exception-能接住所有錯誤嗎?&quot;&gt;&lt;code&gt;catch (Exception $exception)&lt;/code&gt; 能接住所有錯誤嗎？&lt;/h3&gt;&lt;p&gt;不能。&lt;code&gt;Error&lt;/code&gt; 不繼承 &lt;code&gt;Exception&lt;/code&gt;。若需要同時接住實作 &lt;code&gt;Throwable&lt;/code&gt; 的 &lt;code&gt;Exception&lt;/code&gt; 與 &lt;code&gt;Error&lt;/code&gt;，可以使用 &lt;code&gt;catch (Throwable $exception)&lt;/code&gt;，但應先確認這樣做符合你的錯誤處理策略。&lt;/p&gt;&lt;h3 id=&quot;所有-PHP-函式失敗時都會拋出例外嗎?&quot;&gt;所有 PHP 函式失敗時都會拋出例外嗎？&lt;/h3&gt;&lt;p&gt;不會。不同函式的行為不同，有些會回傳 &lt;code&gt;false&lt;/code&gt;、&lt;code&gt;null&lt;/code&gt; 或錯誤碼，有些才會拋出例外。請先查閱該函式的文件，再決定是否需要檢查回傳值或設定錯誤模式。&lt;/p&gt;&lt;h3 id=&quot;finally-一定會執行嗎?&quot;&gt;&lt;code&gt;finally&lt;/code&gt; 一定會執行嗎？&lt;/h3&gt;&lt;p&gt;在正常離開 &lt;code&gt;try&lt;/code&gt;／&lt;code&gt;catch&lt;/code&gt; 的流程中，&lt;code&gt;finally&lt;/code&gt; 會執行，包括 &lt;code&gt;try&lt;/code&gt; 或 &lt;code&gt;catch&lt;/code&gt; 中遇到 &lt;code&gt;return&lt;/code&gt; 的情況。但若程式直接終止，或 &lt;code&gt;finally&lt;/code&gt; 自己拋出例外，就可能改變原本的流程與結果。&lt;/p&gt;&lt;h3 id=&quot;什麼時候該捕獲-Throwable?&quot;&gt;什麼時候該捕獲 &lt;code&gt;Throwable&lt;/code&gt;？&lt;/h3&gt;&lt;p&gt;在應用程式邊界、記錄未預期錯誤或建立統一錯誤回應時，&lt;code&gt;Throwable&lt;/code&gt; 可能有用。一般業務邏輯則應優先捕獲具體類型，否則很容易把真正的程式錯誤當成正常流程處理。&lt;/p&gt;&lt;h2 id=&quot;總結&quot;&gt;總結&lt;/h2&gt;&lt;p&gt;PHP 例外處理的核心不是「把所有錯誤抓起來」，而是讓錯誤在合適的邊界被辨識、記錄與回應。先分清楚 &lt;code&gt;Exception&lt;/code&gt;、&lt;code&gt;Error&lt;/code&gt; 與 &lt;code&gt;Throwable&lt;/code&gt;，再用具體的自訂例外表達意圖；需要清理時使用 &lt;code&gt;finally&lt;/code&gt;，需要跨層轉換時保留 &lt;code&gt;getPrevious()&lt;/code&gt; 的原始脈絡。&lt;/p&gt;&lt;p&gt;當你開始這樣設計，錯誤就不再只是紅色畫面，而會變成可以追蹤、可以處理的訊號。咖啡可以繼續喝，例外則交給正確的 &lt;code&gt;catch&lt;/code&gt;。&lt;/p&gt;&lt;h2 id=&quot;參考資料&quot;&gt;參考資料&lt;/h2&gt;&lt;ol&gt;&lt;li&gt;&lt;a href=&quot;https://www.php.net/manual/en/language.exceptions.php&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;PHP 官方文件：Exceptions&lt;/a&gt;：說明 &lt;code&gt;try&lt;/code&gt;、&lt;code&gt;catch&lt;/code&gt;、&lt;code&gt;finally&lt;/code&gt;、例外傳遞與 &lt;code&gt;Throwable&lt;/code&gt;。&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://www.php.net/manual/en/language.errors.php7.php&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;PHP 官方文件：Errors in PHP 7&lt;/a&gt;：說明 &lt;code&gt;Error&lt;/code&gt; 與 &lt;code&gt;Exception&lt;/code&gt; 的差異，以及 &lt;code&gt;Throwable&lt;/code&gt; 的角色。&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://www.php.net/manual/en/pdo.error-handling.php&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;PHP 官方文件：PDO — Errors and error handling&lt;/a&gt;：說明 PDO 的錯誤模式與 &lt;code&gt;PDOException&lt;/code&gt;。&lt;/li&gt;&lt;/ol&gt;</content>
  </entry>
  <entry>
    <title>PHP Composer 教學：依賴管理入門到實戰</title>
    <link href="https://kamadiam.com/php-composer-intro/" />
    <updated>2026-08-29T00:00:00Z</updated>
    <id>https://kamadiam.com/php-composer-intro/</id>
    <content type="html">&lt;p&gt;PHP 專案一大，手動 &lt;code&gt;require&lt;/code&gt; 檔案就像點餐時每道菜都要自己去廚房端：不是做不到，只是很快就會想喝咖啡冷靜一下。這篇會用一個小型流程，帶你理解 Composer 如何管理依賴、產生自動載入器，以及 &lt;code&gt;composer.json&lt;/code&gt;、&lt;code&gt;composer.lock&lt;/code&gt; 該怎麼分工。&lt;/p&gt;&lt;p&gt;看完之後，你應該能完成以下事情：&lt;/p&gt;&lt;ul&gt;&lt;li&gt;安裝 Composer，建立一個 Composer 專案。&lt;/li&gt;&lt;li&gt;使用 &lt;code&gt;require&lt;/code&gt;、&lt;code&gt;install&lt;/code&gt;、&lt;code&gt;update&lt;/code&gt; 等日常指令。&lt;/li&gt;&lt;li&gt;讀懂 &lt;code&gt;composer.json&lt;/code&gt; 的常見欄位。&lt;/li&gt;&lt;li&gt;用 PSR-4 設定自己的類別自動載入。&lt;/li&gt;&lt;li&gt;判斷什麼時候應該提交 &lt;code&gt;composer.lock&lt;/code&gt;。&lt;/li&gt;&lt;/ul&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/php-composer-intro/#Composer-%E6%98%AF%E4%BB%80%E9%BA%BC&quot;&gt;Composer 是什麼&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/php-composer-intro/#%E5%AE%89%E8%A3%9D-Composer&quot;&gt;安裝 Composer&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/php-composer-intro/#%E5%BB%BA%E7%AB%8B%E7%AC%AC%E4%B8%80%E5%80%8B-Composer-%E5%B0%88%E6%A1%88&quot;&gt;建立第一個 Composer 專案&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/php-composer-intro/#composer.json-%E5%B8%B8%E8%A6%8B%E6%AC%84%E4%BD%8D&quot;&gt;composer.json 常見欄位&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/php-composer-intro/#Composer-%E8%88%87-PSR-4-%E8%87%AA%E5%8B%95%E8%BC%89%E5%85%A5&quot;&gt;Composer 與 PSR-4 自動載入&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/php-composer-intro/#%E6%97%A5%E5%B8%B8%E6%9C%80%E5%B8%B8%E7%94%A8%E7%9A%84-Composer-%E6%8C%87%E4%BB%A4&quot;&gt;日常最常用的 Composer 指令&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/php-composer-intro/#composer.lock-%E8%A6%81%E4%B8%8D%E8%A6%81%E6%8F%90%E4%BA%A4&quot;&gt;composer.lock 要不要提交&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/php-composer-intro/#%E4%B8%80%E5%80%8B%E5%AF%A6%E7%94%A8%E7%9A%84%E5%B7%A5%E4%BD%9C%E6%B5%81%E7%A8%8B&quot;&gt;一個實用的工作流程&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://kamadiam.com/php-composer-intro/#%E5%8F%83%E8%80%83%E8%B3%87%E6%96%99&quot;&gt;參考資料&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://kamadiam.com/img/built/1VMWmP3SZx-240.webp 240w, https://kamadiam.com/img/built/1VMWmP3SZx-320.webp 320w, https://kamadiam.com/img/built/1VMWmP3SZx-480.webp 480w, https://kamadiam.com/img/built/1VMWmP3SZx-640.webp 640w, https://kamadiam.com/img/built/1VMWmP3SZx-672.webp 672w&quot; sizes=&quot;(max-width: 768px) 100vw, 65ch&quot;&gt;&lt;img src=&quot;https://kamadiam.com/img/built/1VMWmP3SZx-240.png&quot; alt=&quot;php composer icon&quot; loading=&quot;lazy&quot; width=&quot;672&quot; height=&quot;618&quot; srcset=&quot;https://kamadiam.com/img/built/1VMWmP3SZx-240.png 240w, https://kamadiam.com/img/built/1VMWmP3SZx-320.png 320w, https://kamadiam.com/img/built/1VMWmP3SZx-480.png 480w, https://kamadiam.com/img/built/1VMWmP3SZx-640.png 640w, https://kamadiam.com/img/built/1VMWmP3SZx-672.png 672w&quot; sizes=&quot;(max-width: 768px) 100vw, 65ch&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;h2 id=&quot;Composer-是什麼&quot;&gt;Composer 是什麼&lt;/h2&gt;&lt;p&gt;Composer 是 PHP 的&lt;strong&gt;依賴管理工具&lt;/strong&gt;。你可以在專案裡宣告需要哪些函式庫，以及允許使用哪些版本範圍；Composer 會解析這些依賴，下載套件與它們需要的其他套件，並把檔案放在專案的 &lt;code&gt;vendor/&lt;/code&gt; 目錄。&lt;/p&gt;&lt;p&gt;這裡有一個容易搞混的地方：Composer 管理的是「每個專案自己的依賴」，不是像作業系統套件管理器那樣，預設把函式庫裝成全機共用。不同專案可以因此使用不同版本，彼此不必為了搶同一杯咖啡而吵架。&lt;/p&gt;&lt;p&gt;Composer 也會產生自動載入器。PHP 程式只要載入 &lt;code&gt;vendor/autoload.php&lt;/code&gt;，就能使用已安裝套件提供的類別，不必逐一 &lt;code&gt;require&lt;/code&gt; 每個檔案。&lt;/p&gt;&lt;h2 id=&quot;安裝-Composer&quot;&gt;安裝 Composer&lt;/h2&gt;&lt;p&gt;請先確認電腦已經有 PHP，再依照 Composer 官方的安裝說明操作。macOS 使用 Homebrew 時，可以執行：&lt;/p&gt;&lt;pre class=&quot;language-bash&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;brew &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;composer&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;安裝完成後，用版本指令確認 Composer 可用：&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;composer&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--version&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;不同作業系統的安裝方式和前置需求可能不同，請以 &lt;a href=&quot;https://getcomposer.org/doc/00-intro.md&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Composer 官方安裝說明&lt;/a&gt; 為準。macOS 的 Homebrew 尚未安裝？可以先看看站內的 &lt;a href=&quot;https://kamadiam.com/homebrew-tutorial/&quot;&gt;Homebrew 安裝教學&lt;/a&gt;。&lt;/p&gt;&lt;h2 id=&quot;建立第一個-Composer-專案&quot;&gt;建立第一個 Composer 專案&lt;/h2&gt;&lt;h3 id=&quot;初始化-composer.json&quot;&gt;初始化 &lt;code&gt;composer.json&lt;/code&gt;&lt;/h3&gt;&lt;p&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;composer&lt;/span&gt; init&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Composer 會以互動問答引導你建立 &lt;code&gt;composer.json&lt;/code&gt;。這個檔案是專案的依賴與設定描述，通常放在版本庫的根目錄。&lt;/p&gt;&lt;p&gt;你也可以直接新增套件，讓 Composer 在需要時建立或更新 &lt;code&gt;composer.json&lt;/code&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;composer&lt;/span&gt; require monolog/monolog&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;monolog/monolog&lt;/code&gt; 是 Packagist 上實際存在的套件名稱；如果你要安裝其他套件，請先確認套件名稱與它支援的 PHP 版本。執行 &lt;code&gt;require&lt;/code&gt; 後，Composer 會更新 &lt;code&gt;composer.json&lt;/code&gt;、解析依賴、建立或更新 &lt;code&gt;composer.lock&lt;/code&gt;，並把套件安裝到 &lt;code&gt;vendor/&lt;/code&gt;。&lt;/p&gt;&lt;h3 id=&quot;在-PHP-程式中載入套件&quot;&gt;在 PHP 程式中載入套件&lt;/h3&gt;&lt;p&gt;在程式的入口檔案載入 Composer 自動載入器：&lt;/p&gt;&lt;pre class=&quot;language-php&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token php language-php&quot;&gt;&lt;span class=&quot;token delimiter important&quot;&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;__DIR__&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;/vendor/autoload.php&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;接著就能使用套件提供的類別。&lt;code&gt;__DIR__&lt;/code&gt; 代表目前 PHP 檔案所在的目錄，比依賴目前工作目錄更穩定；換句話說，程式從哪裡被執行，不會影響它尋找 &lt;code&gt;vendor/&lt;/code&gt; 的位置。&lt;/p&gt;&lt;h2 id=&quot;composer.json-常見欄位&quot;&gt;&lt;code&gt;composer.json&lt;/code&gt; 常見欄位&lt;/h2&gt;&lt;p&gt;一個專案的設定可以像這樣：&lt;/p&gt;&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;name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;vendor/project&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;專案描述&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;project&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;require&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;monolog/monolog&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&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;span class=&quot;token property&quot;&gt;&quot;require-dev&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;phpunit/phpunit&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&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;span class=&quot;token property&quot;&gt;&quot;autoload&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;psr-4&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;App&#92;&#92;&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;src/&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 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;test&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;phpunit&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;這段是示意結構，不代表你應該在正式專案中使用 &lt;code&gt;*&lt;/code&gt;。實務上應根據套件相容性選擇合適的版本約束，並讓 Composer 寫入 lock 檔來固定實際解析出的版本。&lt;/p&gt;&lt;p&gt;常見欄位如下：&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;name&lt;/code&gt;：套件名稱，通常使用 &lt;code&gt;廠商或組織名稱/專案名稱&lt;/code&gt; 格式。&lt;/li&gt;&lt;li&gt;&lt;code&gt;description&lt;/code&gt;：專案或套件的簡短說明。&lt;/li&gt;&lt;li&gt;&lt;code&gt;type&lt;/code&gt;：套件類型；應用程式與可重複使用的函式庫用途不同。&lt;/li&gt;&lt;li&gt;&lt;code&gt;require&lt;/code&gt;：正式執行需要的依賴。&lt;/li&gt;&lt;li&gt;&lt;code&gt;require-dev&lt;/code&gt;：開發、測試或靜態分析需要的依賴。&lt;/li&gt;&lt;li&gt;&lt;code&gt;autoload&lt;/code&gt;：正式程式碼的自動載入規則。&lt;/li&gt;&lt;li&gt;&lt;code&gt;autoload-dev&lt;/code&gt;：只在開發情境使用的自動載入規則。&lt;/li&gt;&lt;li&gt;&lt;code&gt;scripts&lt;/code&gt;：把常用工作包成 Composer 指令，例如 &lt;code&gt;composer test&lt;/code&gt;。&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;完整欄位和格式請查閱 &lt;a href=&quot;https://getcomposer.org/doc/04-schema.md&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Composer 的 &lt;code&gt;composer.json&lt;/code&gt; schema&lt;/a&gt;，不要把網路文章裡的範例直接當成最新規格。&lt;/p&gt;&lt;h2 id=&quot;Composer-與-PSR-4-自動載入&quot;&gt;Composer 與 PSR-4 自動載入&lt;/h2&gt;&lt;p&gt;Composer 是工具；PSR-4 是 PHP-FIG 制定的自動載入規範。PSR-4 描述命名空間如何對應到檔案路徑，Composer 則可以讀取這項設定並產生自動載入器。兩者不是同一件事，就像咖啡機和咖啡豆：常常一起出現，但職責不同。&lt;/p&gt;&lt;h3 id=&quot;設定-PSR-4&quot;&gt;設定 PSR-4&lt;/h3&gt;&lt;p&gt;假設專案結構如下：&lt;/p&gt;&lt;pre class=&quot;language-text&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;project/
├── composer.json
├── src/
│   └── Controllers/
│       └── UserController.php
└── public/
    └── index.php&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;在 &lt;code&gt;composer.json&lt;/code&gt; 加入：&lt;/p&gt;&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;autoload&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;psr-4&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;App&#92;&#92;&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;src/&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 punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;在這個映射下，&lt;code&gt;App&#92;Controllers&#92;UserController&lt;/code&gt; 會對應到 &lt;code&gt;src/Controllers/UserController.php&lt;/code&gt;。類別的命名空間、類別名稱與檔案路徑要一致：&lt;/p&gt;&lt;pre class=&quot;language-php&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token php language-php&quot;&gt;&lt;span class=&quot;token delimiter important&quot;&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;token package&quot;&gt;App&lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;Controllers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;UserController&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;index&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 return-type&quot;&gt;string&lt;/span&gt;
    &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 string single-quoted-string&quot;&gt;&#39;Hello, Composer!&#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&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&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;composer&lt;/span&gt; dump-autoload&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;然後在入口檔載入 &lt;code&gt;vendor/autoload.php&lt;/code&gt;，就可以使用 &lt;code&gt;new UserController()&lt;/code&gt;。PSR-4 的重點不是魔法，而是大家遵守同一套「命名空間對應路徑」的規則；規則一致，檔案就不用到處報到。&lt;/p&gt;&lt;h2 id=&quot;日常最常用的-Composer-指令&quot;&gt;日常最常用的 Composer 指令&lt;/h2&gt;&lt;h3 id=&quot;composer-require&quot;&gt;&lt;code&gt;composer require&lt;/code&gt;&lt;/h3&gt;&lt;p&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;composer&lt;/span&gt; require vendor/package&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;若套件只在測試或開發時使用，可加入 &lt;code&gt;--dev&lt;/code&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;composer&lt;/span&gt; require &lt;span class=&quot;token parameter variable&quot;&gt;--dev&lt;/span&gt; vendor/package&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;這個指令會修改 &lt;code&gt;composer.json&lt;/code&gt;，並重新解析與安裝依賴。&lt;/p&gt;&lt;h3 id=&quot;composer-install&quot;&gt;&lt;code&gt;composer install&lt;/code&gt;&lt;/h3&gt;&lt;p&gt;依照目前的 &lt;code&gt;composer.lock&lt;/code&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;composer&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;在拉下專案原始碼、CI 或部署環境中，通常應優先使用 &lt;code&gt;install&lt;/code&gt;。有 lock 檔時，Composer 會使用其中記錄的精確版本；沒有 lock 檔時，則會先解析依賴並建立它。&lt;/p&gt;&lt;h3 id=&quot;composer-update&quot;&gt;&lt;code&gt;composer update&lt;/code&gt;&lt;/h3&gt;&lt;p&gt;依照 &lt;code&gt;composer.json&lt;/code&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;composer&lt;/span&gt; update&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;這會更新 &lt;code&gt;composer.lock&lt;/code&gt;。它不是「每次安裝都該執行」的按鈕，而是你有意識地要更新依賴時才使用。否則一個看似平凡的更新，可能讓整個專案一起喝雙倍濃縮。&lt;/p&gt;&lt;p&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;composer&lt;/span&gt; update vendor/package&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;composer-remove&quot;&gt;&lt;code&gt;composer remove&lt;/code&gt;&lt;/h3&gt;&lt;p&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;composer&lt;/span&gt; remove vendor/package&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Composer 會同步更新 &lt;code&gt;composer.json&lt;/code&gt;、&lt;code&gt;composer.lock&lt;/code&gt; 與 &lt;code&gt;vendor/&lt;/code&gt;。&lt;/p&gt;&lt;h3 id=&quot;composer-dump-autoload&quot;&gt;&lt;code&gt;composer dump-autoload&lt;/code&gt;&lt;/h3&gt;&lt;p&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;composer&lt;/span&gt; dump-autoload&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;如果只是修改 &lt;code&gt;autoload&lt;/code&gt; 設定或需要重建 autoloader，使用這個指令即可，不必為了重建自動載入器而更新所有套件。&lt;/p&gt;&lt;p&gt;正式環境也可以使用 Composer 文件描述的最佳化選項，例如：&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;composer&lt;/span&gt; dump-autoload &lt;span class=&quot;token parameter variable&quot;&gt;--optimize&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;是否使用最佳化，以及要不要採用更嚴格的 classmap 選項，應依專案的部署方式測試後決定。&lt;/p&gt;&lt;h3 id=&quot;composer-show-與-composer-validate&quot;&gt;&lt;code&gt;composer show&lt;/code&gt; 與 &lt;code&gt;composer validate&lt;/code&gt;&lt;/h3&gt;&lt;p&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;composer&lt;/span&gt; show&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;檢查 &lt;code&gt;composer.json&lt;/code&gt; 與 lock 檔：&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;composer&lt;/span&gt; validate&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;當你接手別人的專案，這兩個指令很適合當作快速健康檢查：先看有哪些依賴，再確認設定檔是否合理。&lt;/p&gt;&lt;h2 id=&quot;composer.lock-要不要提交&quot;&gt;&lt;code&gt;composer.lock&lt;/code&gt; 要不要提交&lt;/h2&gt;&lt;p&gt;&lt;code&gt;composer.lock&lt;/code&gt; 記錄 Composer 解析出的精確依賴版本。它讓團隊成員、CI 和部署環境能安裝同一組版本，避免「我這裡可以跑」成為專案最常見的考古謎題。&lt;/p&gt;&lt;p&gt;一般可依專案類型判斷：&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;應用程式&lt;/strong&gt;：通常應提交 &lt;code&gt;composer.lock&lt;/code&gt;，讓每次安裝都使用經過確認的依賴版本。&lt;/li&gt;&lt;li&gt;&lt;strong&gt;可供其他專案安裝的函式庫&lt;/strong&gt;：lock 檔對使用者安裝你的函式庫不會產生同樣的鎖定效果，因此是否提交可依團隊測試流程決定；Composer 官方文件也說明，函式庫可以不提交 lock 檔。&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;不論哪一種專案，都不要把 &lt;code&gt;vendor/&lt;/code&gt; 當成依賴版本的主要紀錄。版本與相容性約束寫在 &lt;code&gt;composer.json&lt;/code&gt;，精確解析結果則由 &lt;code&gt;composer.lock&lt;/code&gt; 保存。&lt;/p&gt;&lt;h2 id=&quot;一個實用的工作流程&quot;&gt;一個實用的工作流程&lt;/h2&gt;&lt;p&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;composer&lt;/span&gt; init
&lt;span class=&quot;token function&quot;&gt;composer&lt;/span&gt; require vendor/package
&lt;span class=&quot;token function&quot;&gt;composer&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&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;composer&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&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;composer&lt;/span&gt; update
&lt;span class=&quot;token function&quot;&gt;composer&lt;/span&gt; validate&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;如果新增了自己的 PSR-4 類別映射，再執行：&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;composer&lt;/span&gt; dump-autoload&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;掌握這個分工後，Composer 就不再像一個只會吐出紅字的黑盒子：&lt;code&gt;composer.json&lt;/code&gt; 描述需求，&lt;code&gt;composer.lock&lt;/code&gt; 固定結果，&lt;code&gt;vendor/autoload.php&lt;/code&gt; 負責把類別帶進來。剩下的問題，先讓 Composer 解；解不出來，再泡一杯咖啡一起看錯誤訊息。&lt;/p&gt;&lt;h2 id=&quot;參考資料&quot;&gt;參考資料&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://getcomposer.org/doc/00-intro.md&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Composer Introduction&lt;/a&gt;：Composer 的定位、安裝方式與基本概念。&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://getcomposer.org/doc/01-basic-usage.md&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Composer Basic usage&lt;/a&gt;：&lt;code&gt;composer.json&lt;/code&gt;、依賴安裝、lock 檔與自動載入流程。&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://getcomposer.org/doc/03-cli.md&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Composer Command-line interface / Commands&lt;/a&gt;：&lt;code&gt;require&lt;/code&gt;、&lt;code&gt;install&lt;/code&gt;、&lt;code&gt;update&lt;/code&gt;、&lt;code&gt;dump-autoload&lt;/code&gt; 等指令的官方說明。&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://getcomposer.org/doc/04-schema.md&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;The &lt;code&gt;composer.json&lt;/code&gt; schema&lt;/a&gt;：設定檔欄位、版本約束與 PSR-4 映射格式。&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://www.php-fig.org/psr/psr-4/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;PSR-4: Autoloader&lt;/a&gt;：PHP-FIG 的 PSR-4 自動載入規範。&lt;/li&gt;&lt;/ul&gt;</content>
  </entry>
</feed>