<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>星星的筆記．Lucas</title><description>Lucas Yang 的部落格 - 即使是一顆小星星 也會閃耀著光芒✨</description><link>https://star-note-lucas.me</link><language>zh-TW</language><item><title>Windows 中優雅的開機自動執行 JS 腳本</title><link>https://star-note-lucas.me/posts/windows-startup-command</link><guid isPermaLink="true">https://star-note-lucas.me/posts/windows-startup-command</guid><pubDate>Sat, 18 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;這一陣子有在做兩個小工具，都是要在 Windows 開機的時候自動執行特定的 JS 腳本，原本我用了工作排程器 (Task Scheduler) 來觸發，但問題就在每次開機之後，都會閃過一下終端機視窗，看著就會有點不爽就是了。&lt;/p&gt;
&lt;p&gt;所以我就去找了一下有沒有更好的方式，去找了一些 Windows 啟動 JS 腳本的套件，爬到原始碼之後，發現竟然是用 VBScript + Regedit 的方式來達成的，而且試過之後還真的不會閃出終端機視窗，Windows 還真是麻煩啊！&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#實作自動開機腳本&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;實作自動開機腳本&lt;/h2&gt;
&lt;p&gt;首先需要將檔案放在同一個資料夾中：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;root/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;├── my-service.js   - 我們寫的，要自動執行的 JavaScript 腳本&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;├── start.vbs       - 用來執行 JS 腳本的 VBScript 腳本&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;├── install.cmd     - 註冊開機自動執行&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;└── uninstall.cmd   - 取消註冊開機自動執行&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;基本上原理就是，會在 Windows 的 Regedit 裡面註冊一個項目，會在開機啟動時執行一個 VBScript 腳本 &lt;code&gt;start.vbs&lt;/code&gt;，裡面會透過 Node.js 來執行傳給該 VBScript 的腳本：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;start.vbs&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;CreateObject&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;WScript.Shell&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;).Run &lt;/span&gt;&lt;span&gt;&quot;&quot;&quot;&lt;/span&gt;&lt;span&gt;C:\Program Files\nodejs\node.exe&lt;/span&gt;&lt;span&gt;&quot;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&quot;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;amp;&lt;/span&gt;&lt;span&gt; WScript.&lt;/span&gt;&lt;span&gt;Arguments&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;&amp;amp;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&quot;&quot;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; False&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;接著我們需要 &lt;code&gt;install.cmd&lt;/code&gt; 和 &lt;code&gt;uninstall.cmd&lt;/code&gt; 兩個批次檔，分別用來註冊和取消註冊這個開機自動執行的功能。註冊時需要提供一個名字，可以將 &lt;code&gt;MyService&lt;/code&gt; 替換成你想要的名字，還有要執行的 JS 腳本路徑 &lt;code&gt;my-service.js&lt;/code&gt;，&lt;code&gt;%~dp0&lt;/code&gt; 是批次檔所在的資料夾路徑，這樣就不需要擔心路徑問題了：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;install.cmd&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;off&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;reg&lt;/span&gt;&lt;span&gt; add &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;HKCU\Software\Microsoft\Windows\CurrentVersion\Run&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; /v &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;MyService&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; /t REG_SZ /d &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;wscript.exe \&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;%~dp0&lt;/span&gt;&lt;span&gt;start.vbs\&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;%~dp0&lt;/span&gt;&lt;span&gt;my-service.js\&lt;/span&gt;&lt;span&gt;&quot;&quot;&lt;/span&gt;&lt;span&gt; /f&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; MyService installed successfully!&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;pause&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;然後對應的取消註冊的批次檔，做刪除註冊項目的動作就可以了：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;uninstall.cmd&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;off&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;reg&lt;/span&gt;&lt;span&gt; delete &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;HKCU\Software\Microsoft\Windows\CurrentVersion\Run&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; /v &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;MyService&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; /f&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; MyService uninstalled successfully!&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;pause&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;</content:encoded></item><item><title>試用 ui.sh：Tailwind 的 AI 設計工具</title><link>https://star-note-lucas.me/posts/uidotsh-early-access</link><guid isPermaLink="true">https://star-note-lucas.me/posts/uidotsh-early-access</guid><description>Tailwind 最近推出了一個 AI 前端設計服務 ui.sh，剛好搶到了早期體驗的機會。底層是一套 Tailwind CSS 設計最佳實踐資料庫，透過 skill 呼叫遠端 MCP 來使用。其中最有趣的功能是設計方案選擇器，可以一次產生 4 組方案讓你挑選。</description><pubDate>Fri, 17 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;來淺淺分享一下 Tailwind 推出的新 AI 服務 ui.sh 的體驗心得~&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#uish-是什麼&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;ui.sh 是什麼?&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://ui.sh/&quot;&gt;ui.sh&lt;/a&gt; 是 Tailwind 推出的新 AI 服務，使用 /ui 這個 skill 來改善 AI 產生的 Tailwind CSS 網站設計，底層是一套用於設計和建立 UI 的最佳實踐資料庫。目前擅長的領域是產生 Landing Page、Dashboard 等常見的網站類型。&lt;/p&gt;
&lt;p&gt;技術上是使用 skill 來呼叫遠端的 MCP，然後才能取得更詳細的設計指引內容，算是使用了漸進式揭露的設計方式，好處是有任何更新都會是即時的，不需要重新安裝任何 skill。這種設計基本上在 Claude Code 和 Codex 上都是可以使用的，不過文件寫了建議你使用 Opus 4.6 就是了哈哈哈&lt;/p&gt;
&lt;p&gt;原因是因為在建構 ui.sh 的 prompt 時，是讓 Tailwind 團隊的設計師 Steve Schoger 在學習使用 Claude Code 建構網站的過程中，將其中的經驗精煉出來而變成 ui.sh 的，然後人家又是使用 Opus 4.6… 像我這樣的 $20 使用者沒辦法大規模使用阿… 今天我嘗試用一下 Opus 4.7，做了 1.5 個功能然後就被強制休息了呵呵&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#設計方案選擇器&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;設計方案選擇器&lt;/h2&gt;
&lt;p&gt;比起設計內容是其次，我覺得最好玩的是這個選擇器XDD 沒有靈感的時候，只要跟 AI 許願說請幫我產生一些不同的設計方案，它就會在 HTML 中注入 ui-picker.js 的腳本，然後你的網頁中就會在下方出現一個黑色選擇器，可以切換 4 組方案讓你選擇~ 而且不僅限在整個頁面，網頁其中的一個小區塊一樣也可以~ 對於有時候想要重新洗牌 AI 產生 UI 的操作變得更容易了。&lt;/p&gt;
&lt;p&gt;下面是我使用 ui.sh 搭配 Opus 4.7 產生的設計方案選擇器的結果：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://star-note-lucas.me/images/2026-04-17-uidotsh-early-access-1.png&quot; alt=&quot;設計方案選擇1&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://star-note-lucas.me/images/2026-04-17-uidotsh-early-access-2.png&quot; alt=&quot;設計方案選擇2&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://star-note-lucas.me/images/2026-04-17-uidotsh-early-access-3.png&quot; alt=&quot;設計方案選擇3&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://star-note-lucas.me/images/2026-04-17-uidotsh-early-access-4.png&quot; alt=&quot;設計方案選擇4&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#產品狀態和收費方式&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;產品狀態和收費方式&lt;/h2&gt;
&lt;p&gt;目前 ui.sh 產品狀態是封閉測試階段，需要先加入候補名單，然後才有機會收到邀請使用的機會。然後收費方式是每年 USD$120 ($10/月)。我是覺得不算太貴，又急著想體驗，所以就刷下去了哈哈哈&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#如何選擇前端設計的-skill&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;如何選擇前端設計的 Skill?&lt;/h2&gt;
&lt;p&gt;目前我知道的前端設計相關的 Skill 大概有這幾個，簡單介紹一下差異 (只有 ui.sh 是付費的，其他都免費)：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://ui.sh/&quot;&gt;ui.sh&lt;/a&gt;：專門為 Tailwind CSS 網站設計的，如果你是 Tailwind 的使用者，這個 Skill 可能會非常適合你&lt;/li&gt;
&lt;li&gt;Anthropic 的 &lt;a href=&quot;https://github.com/anthropics/claude-code/blob/main/plugins/frontend-design/skills/frontend-design/SKILL.md&quot;&gt;frontend-design skill&lt;/a&gt;：算是比較通用的前端設計工具，適用於想要快速美化前端設計的開發者，如果你想要快速產出一個有個性的頁面，可以直接使用這個 Skill&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://impeccable.style/&quot;&gt;impeccable&lt;/a&gt;：一個更全面的前端設計工具，提供了從設計到實現的完整流程，適合有一定前端設計經驗的開發者，尤其是那些需要嚴格避免 AI slop 設計模式的團隊&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ui-ux-pro-max-skill.nextlevelbuilder.io/&quot;&gt;ui-ux-pro-max&lt;/a&gt;：一個大型的知識庫驅動的設計系統自動化工廠，適合需要快速生成比較成熟的設計系統的團隊&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;定位上都是有些不同，可以根據自己的需求來選擇適合的工具~&lt;/p&gt;</content:encoded></item><item><title>2025 年終回顧</title><link>https://star-note-lucas.me/posts/2025-review</link><guid isPermaLink="true">https://star-note-lucas.me/posts/2025-review</guid><description>回顧了 Lucas 忙碌的 2025，有大學快畢業、部落格改版、做了開源專案、學習 AI 協作，以及對新一年的期許</description><pubDate>Wed, 31 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;其實以前是沒有寫回顧的習慣的，但今年突然有了寫的慾望XD 想說至少整理一下自己的這一年的經歷和以後的目標吧！&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#渾渾噩噩的度過&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;渾渾噩噩的度過&lt;/h2&gt;
&lt;p&gt;最近在聽 尖不想寫扣 這個 Podcast 的時候，Anthony 有分享說他今年會常常在想：「自己的人生想要什麼？要怎麼平衡工作與生活？」，我自己是滿有同感的。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://star-note-lucas.me/images/2025-12-31-2025-review-nocodingtoday.jpg&quot; alt=&quot;尖不想寫扣 No Coding Today&quot; /&gt;&lt;/p&gt;
&lt;p&gt;我讀了6~7年的大學終於快要畢業了，為了保證明年年初就可以畢業，今年我選的課都幾乎是塞滿的說，同時還有在做接案，生活節奏上是讓我有點喘不過氣來，也會常常想說我以後要怎麼平衡工作與生活？就會想希望未來自己可以做一些改變。聽到 Anthony 說他也在想這種事情，我自己也覺得或許沒那麼孤單了XD&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#今年部落格做了什麼改變&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;今年部落格做了什麼改變?&lt;/h2&gt;
&lt;p&gt;回首望去 2025 年，我自己幾乎都被讀書和接案工作佔滿了，總覺得時間過得太快太快，有種自己快要變成只會重複讀書和工作的機器了呵呵。話雖是那麼說，但總歸還是有做了一些事情可以拿來分享的呵呵~&lt;/p&gt;
&lt;p&gt;今年我終於幫自己的部落格買了一個獨立的域名(star-note-lucas.me)了~ 對於我自己來說也是一個很重要的里程碑，像是解鎖了一個人生成就了一樣~ 以前只為了圖方便，都直接用 Vercel 預設的免費域名呵呵。&lt;/p&gt;
&lt;p&gt;然後今年終於對部落格進行了大改版，最近接案中有使用了 Astro 來試做了幾個專案，覺得他特別適合這種小型靜態網站，再加上原本的 VuePress 的靈活度不大夠，這就足以有動力來重寫了XDD 除了直接重寫部落格外，也對專案架構、畫面設計等都做了大量優化，最後現在看到的版本就是我自己很滿意的一版啦~&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#被迫學習-ai但是為了偷懶xd&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;被迫學習 AI，但是為了偷懶XD&lt;/h2&gt;
&lt;p&gt;這兩年 AI 突然開始爆紅，讓我這個手寫派也被迫開始學習新工具了，找了一些 GitHub Copilot、Claude Code 的免費、付費課程開始學，學了半年至少可以在實際寫程式中一起協做，可以省了一些做有重複性剪剪貼貼的工作了~&lt;/p&gt;
&lt;p&gt;但不得說還是無法完全替代人，有滿多專案的大方向還是得自己把空住才行，畢竟我是真的不那麼信任 AI 就是了。&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#優化-terminal-體驗&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;優化 Terminal 體驗&lt;/h2&gt;
&lt;p&gt;在開始用了 Claude Code 之後，就萌生了一些優化跟 Terminal 相關的想法。&lt;/p&gt;
&lt;p&gt;原本我的 Windows Terminal 有設定了一組背景圖片，每天登入電腦後就會自動輪換下一張，這些程式設定原本是和我常用的設定檔放在一起，但最近迷上了「魔法少女的魔女審判」XDD 想要可以快速替換不同的背景圖片，且為了好管理，就做了 &lt;a href=&quot;https://github.com/ycs77/terminal-wallpaper&quot;&gt;Terminal Wallpaper&lt;/a&gt; 呵呵~&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://star-note-lucas.me/images/2025-12-31-2025-review-terminal.jpg&quot; alt=&quot;Terminal 背景圖片&quot; /&gt;&lt;/p&gt;
&lt;p&gt;然後 Claude Code 每次派發完任務之後，有任何問題或執行完成後都靜悄悄，完全不理人QQ 因此就爬了一下網路找播放提示音的作法，並做了 &lt;a href=&quot;https://github.com/ycs77/claude-code-notifications&quot;&gt;Claude Code 提示音合集&lt;/a&gt;，雖然只放了一個簡單的聲音，來不及做「魔法少女的魔女審判」的版本，等以後補上吧~&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#寫了人生第一個-eslint-套件&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;寫了人生第一個 ESLint 套件&lt;/h2&gt;
&lt;p&gt;自從 3 年前 Anthony Fu 釋出了 eslint-config，我才有使用 ESLint 的習慣直到現在。但最近 Astro 的專案中有個較為特殊的 JSX 寫法，雖然編譯器可以過網頁也能跑，但會在 ESLint 檢查中造成衝突。&lt;/p&gt;
&lt;p&gt;為了避免專案中出現此寫法，我就寫了人生中第一個 ESLint 的套件 &lt;a href=&quot;https://github.com/ycs77/eslint-plugin-astro-explicit-wrapper&quot;&gt;eslint-plugin-astro-explicit-wrapper&lt;/a&gt;~&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://star-note-lucas.me/images/2025-12-31-2025-review-eslint-plugin.jpg&quot; alt=&quot;我的第一個 ESLint 套件&quot; /&gt;&lt;/p&gt;
&lt;p&gt;又達成一個人生小成就了~&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#修了一個-laravel-上游套件的-bug&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;修了一個 Laravel 上游套件的 bug&lt;/h2&gt;
&lt;p&gt;當然也是因為今年一直做 Astro 前端的專案，反倒 Laravel 就比較少碰了。&lt;/p&gt;
&lt;p&gt;在去年寫 Laravel 時其實就遇到了一個不大不小的問題，Laravel 為了讓 Console 顯示的內容可以設計更漂亮，使用了 Termwind 這個套件，雖然英文可以顯示正常，但如果寫了中文等 CJK 文字就會出現排版問題，雖然不影響程式運作，但看了會不大爽就是呵呵~&lt;/p&gt;
&lt;p&gt;於是就去提交了一個 PR：&lt;a href=&quot;https://github.com/nunomaduro/termwind/pull/186&quot;&gt;Fix incorrect space count of CJK characters&lt;/a&gt;，但 Review 速度非常緩慢，後來有一位網友 James 也對這個 PR 進度感興趣，我們一起跟進了一年後終於成功 merge 了~ 之後在 Laravel 和 Pest 都可以享受到正常的中文排版了！可喜可賀~&lt;/p&gt;
&lt;p&gt;Laravel Console 排版問題修復前：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://star-note-lucas.me/images/2025-12-31-2025-review-termwind-cjk-bug-1.jpg&quot; alt=&quot;Laravel Termwind 排版問題修復前&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Laravel Console 排版問題修復後：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://star-note-lucas.me/images/2025-12-31-2025-review-termwind-cjk-bug-2.jpg&quot; alt=&quot;Laravel Termwind 排版問題修復後&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Pest 測試畫面排版問題修復前：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://star-note-lucas.me/images/2025-12-31-2025-review-termwind-cjk-bug-3.jpg&quot; alt=&quot;Pest 測試畫面排版修復前&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Pest 測試畫面排版問題修復後：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://star-note-lucas.me/images/2025-12-31-2025-review-termwind-cjk-bug-4.jpg&quot; alt=&quot;Pest 測試畫面排版修復後&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#開源-laravel-ezpay-電子發票套件&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;開源 Laravel ezPay 電子發票套件&lt;/h2&gt;
&lt;p&gt;不過也不是完全和 Laravel 絕緣，暑假的時候 阿龜微氣候 有想要串接 ezPay電子發票 API，他們後端也是使用 Laravel 寫，但現在市面上沒有好用的 ezPay電子發票 的 Laravel 套件，因此我們就合作開源了 &lt;a href=&quot;https://github.com/Agriweather/laravel-ezpay-invoice&quot;&gt;Laravel ezPay Invoice&lt;/a&gt; 套件，我負責開發，阿龜微氣候 團隊會繼續維護，希望大家多多支持開源~&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://star-note-lucas.me/images/2025-12-31-2025-review-laravel-ezpay-invoice.jpg&quot; alt=&quot;Laravel ezPay Invoice 套件&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#2025-最喜歡的動漫&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;2025 最喜歡的動漫&lt;/h2&gt;
&lt;p&gt;當然今年也有看不少動漫，以下是我今年最喜歡的幾部：&lt;/p&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;li&gt;琉璃的寶石&lt;/li&gt;
&lt;li&gt;GNOSIA&lt;/li&gt;
&lt;li&gt;前橋魔女&lt;/li&gt;
&lt;li&gt;妻子變成小學生。&lt;/li&gt;
&lt;/ul&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;聽說 2026 年又會有好幾部熱門續作，期待明年可以繼續追番啦~&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#新一年的目標&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;新一年的目標&lt;/h2&gt;
&lt;p&gt;對於 2026 年的目標，我雖然不敢訂定一個很具體的目標，但至少想要多學一點日文，畢竟還是想要直接看生肉的動漫XD，以及希望可以多去和不同的人交流，畢竟宅在家裡太久，都不大敢和人交流了呵呵，跨出一點舒適圈應該就是我的小目標啦~&lt;/p&gt;
&lt;p&gt;願 2026 年有個美好的一年。&lt;/p&gt;</content:encoded></item><item><title>Inertia v2.2 的 Infinite Scroll (無限滾動) 新功能</title><link>https://star-note-lucas.me/posts/inertia-v2-infinite-scroll</link><guid isPermaLink="true">https://star-note-lucas.me/posts/inertia-v2-infinite-scroll</guid><description>原本在 Inertia v2.0 的時候，應該就要一起推出的 Infinite Scroll (無限滾動) 功能，拖了一年之後，終於在 Inertia v2.2 正式推出了！無限滾動通常常見在聊天室訊息、社交媒體動態、照片牆等場景，可以讓使用者無限滑動載入資料，而不需要點擊分頁按鈕。而且我滿喜歡這個 API 的設計，使用起來還滿簡單的說~</description><pubDate>Fri, 03 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;原本在 Inertia v2.0 的時候，應該就要一起推出的 Infinite Scroll (無限滾動) 功能，拖了一年之後，終於在 Inertia v2.2 正式推出了！無限滾動通常常見在聊天室訊息、社交媒體動態、照片牆等場景，可以讓使用者無限滑動載入資料，而不需要點擊分頁按鈕。而且我滿喜歡這個 API 的設計，使用起來還滿簡單的說~&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#infinite-scroll-無限滾動&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;Infinite Scroll (無限滾動)&lt;/h2&gt;
&lt;p&gt;通常在 Laravel 中都是使用 Model 來取得資料，因此我們可以使用 &lt;code&gt;Inertia::scroll()&lt;/code&gt; 來包裝 Eloquent 的分頁結果，它會自動合併分頁參數，並回傳 Inertia 所需的資料格式：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;app/Http/Controllers/UserController.php&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Inertia&lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt;render&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Users/Index&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;users&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Inertia&lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt;scroll&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;fn&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt; =&amp;gt; &lt;/span&gt;&lt;span&gt;User&lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt;paginate&lt;/span&gt;&lt;span&gt;()),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;]);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;然後在前端 Vue.js 中，我們就可以使用 &lt;code&gt;&amp;lt;InfiniteScroll&amp;gt;&lt;/code&gt; 元件來實現無限滾動功能，以及在 props 中傳入資料來源 &lt;code&gt;users&lt;/code&gt;：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;resources/js/Pages/Users/Index.vue&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;template&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;InfiniteScroll&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;users&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;as&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;ul&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;v-for&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;user in users.data&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:key&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;user.id&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{{ user.name }}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;InfiniteScroll&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;template&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;setup&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { InfiniteScroll } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;@inertiajs/vue3&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;props&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;defineProps&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;users&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Object&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;InfiniteScroll&amp;gt;&lt;/code&gt; 元件背後使用了 Intersection Observer API 來偵測使用者是否快要滾動到底部，當快到底部時就會自動載入下一頁的資料，並且會自動合併到目前的資料中。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;InfiniteScroll&amp;gt;&lt;/code&gt; 元件也有一些其他的 props 可以使用。比如 &lt;code&gt;buffer&lt;/code&gt; 可以設定在距離底部多少像素時候開始載入下一頁，預設是 0 像素：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;InfiniteScroll&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;users&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;as&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;ul&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:buffer&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;1000&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;InfiniteScroll&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;InfiniteScroll&amp;gt;&lt;/code&gt; 元件預設的網址處理方式是，當載入下一頁資料時會更新瀏覽器網址列的分頁參數，如果不想要更新網址列，可以加上 &lt;code&gt;preserve-url&lt;/code&gt; prop：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;InfiniteScroll&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;users&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;as&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;ul&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;preserve-url&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;InfiniteScroll&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;當然也可以使用 &lt;code&gt;loading&lt;/code&gt; slot 來自訂載入中的顯示內容：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;InfiniteScroll&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;users&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;as&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;ul&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;template&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;#loading&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;載入中...&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;template&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;InfiniteScroll&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;最後完整範例如下：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;template&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;InfiniteScroll&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;users&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;as&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;ul&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;:buffer&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;1000&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;preserve-url&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;v-for&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;user in users.data&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:key&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;user.id&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{{ user.name }}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;template&lt;/span&gt;&lt;span&gt; #&lt;/span&gt;&lt;span&gt;loading&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;載入中...&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;template&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;InfiniteScroll&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;template&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;setup&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { InfiniteScroll } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;@inertiajs/vue3&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;props&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;defineProps&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;users&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Object&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;如果想要更詳細的說明，可以參考 Inertia 文件：&lt;a href=&quot;https://inertiajs.com/infinite-scroll&quot;&gt;Infinite scroll - Inertia.js&lt;/a&gt;。&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#demo&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;Demo&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://star-note-lucas.me/images/2025-10-03-inertia-v2-infinite-scroll-demo.jpg&quot; alt=&quot;Inertia v2.2 Demo 預覽&quot; /&gt;&lt;/p&gt;
&lt;p&gt;這次的無限滾動功能，可以在 &lt;a href=&quot;https://github.com/ycs77/inertia-v2-demo&quot;&gt;inertia-v2-demo&lt;/a&gt; 裡面試用喔~&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#參考資料&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;參考資料&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://inertiajs.com/infinite-scroll&quot;&gt;Infinite scroll - Inertia.js&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>部署 Astro 專案到低配置的 Ubuntu 伺服器</title><link>https://star-note-lucas.me/posts/deploy-astro-to-budget-ubuntu-server</link><guid isPermaLink="true">https://star-note-lucas.me/posts/deploy-astro-to-budget-ubuntu-server</guid><pubDate>Fri, 12 Sep 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;這回 Astro 專案的開發，一開始選定了部署到 Netlify 上，結果正式站在上線之時卻意外掛掉，查了之後發現是 Astro 在 Netlify 使用 Session 時，依賴的 Netlify Blobs 卻有問題，無奈之下決定改用自架的 Ubuntu 伺服器來部署。&lt;/p&gt;
&lt;p&gt;為了節省成本，這次專案給了超小的 Ubuntu 伺服器，只有 1G RAM，可是專案不是小專案，那不出意外的自然無法部署成功，因為跑 &lt;code&gt;yarn build&lt;/code&gt; 時報了記憶體不足的錯誤 (這還是我頭一回見到 Node.js 中的記憶體不足的錯誤XDD)。後來我就研究了一個方法，先在本地 WSL 內編譯好，再把編譯好的檔案用 SFTP 傳到伺服器上，這樣就可以成功部署了~&lt;/p&gt;
&lt;p&gt;在 Ubuntu 伺服器上可以直接用 Node.js 的部署方式，使用 Nginx 作反向代理，並用 pm2 來管理 Astro 的 Node.js 服務。&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#nginx-反向代理設定&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;Nginx 反向代理設定&lt;/h2&gt;
&lt;p&gt;Nginx 只要負責反向代理的部分就行了，以下是 Nginx 設定範例：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;/etc/nginx/sites-available/your-domain.conf&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;server&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;   &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;listen &lt;/span&gt;&lt;span&gt;80&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;   &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;listen &lt;/span&gt;&lt;span&gt;[::]:80;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;   &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;server_name &lt;/span&gt;&lt;span&gt;your-domain;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;   &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;server_tokens &lt;/span&gt;&lt;span&gt;off&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;   &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;root &lt;/span&gt;&lt;span&gt;/var/www/html;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;   &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;index &lt;/span&gt;&lt;span&gt;index.html;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;   &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;charset &lt;/span&gt;&lt;span&gt;utf-8;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;location&lt;/span&gt;&lt;span&gt; / {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;       &lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;proxy_pass &lt;/span&gt;&lt;span&gt;http://localhost:4321;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;       &lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;proxy_http_version &lt;/span&gt;&lt;span&gt;1.1&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;       &lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;proxy_set_header &lt;/span&gt;&lt;span&gt;Upgrade $&lt;/span&gt;&lt;span&gt;http_upgrade&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;       &lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;proxy_set_header &lt;/span&gt;&lt;span&gt;Connection &lt;/span&gt;&lt;span&gt;&apos;upgrade&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;       &lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;proxy_set_header &lt;/span&gt;&lt;span&gt;Host $&lt;/span&gt;&lt;span&gt;host&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;       &lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;proxy_cache_bypass &lt;/span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt;http_upgrade&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;# Security headers&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;   &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;add_header &lt;/span&gt;&lt;span&gt;X-Frame-Options &lt;/span&gt;&lt;span&gt;&quot;SAMEORIGIN&quot;&lt;/span&gt;&lt;span&gt; always;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;   &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;add_header &lt;/span&gt;&lt;span&gt;X-XSS-Protection &lt;/span&gt;&lt;span&gt;&quot;1; mode=block&quot;&lt;/span&gt;&lt;span&gt; always;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;   &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;add_header &lt;/span&gt;&lt;span&gt;X-Content-Type-Options &lt;/span&gt;&lt;span&gt;&quot;nosniff&quot;&lt;/span&gt;&lt;span&gt; always;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;   &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;add_header &lt;/span&gt;&lt;span&gt;Referrer-Policy &lt;/span&gt;&lt;span&gt;&quot;no-referrer-when-downgrade&quot;&lt;/span&gt;&lt;span&gt; always;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;&lt;a href=&quot;#手動部署專案&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;手動部署專案&lt;/h2&gt;
&lt;p&gt;在本地編譯和上傳檔案：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# remote: 重建 dist 資料夾&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;rm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;dist&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-rf&lt;/span&gt;&lt;span&gt; &amp;amp;&amp;amp; &lt;/span&gt;&lt;span&gt;mkdir&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;dist&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# local: 編譯和上傳檔案&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;yarn&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;yarn&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;build&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# 記得要設定全局變數&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;cp&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;.env.example&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;.env&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sftp&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;user@remote_host&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; cd /var/www/html&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; put -r dist&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; bye&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;然後在伺服器上用 pm2 來啟動服務：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# remote: 安裝 pm2 並啟動服務&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;npm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-g&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pm2&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;pm2&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;start&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;./dist/server/entry.mjs&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--name&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;app_name&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;pm2&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;startup&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;pm2&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;save&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# remote: 重啟服務&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;pm2&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;list&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;pm2&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;restart&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;app_name&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--update-env&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# 或者是完全刪除再啟動&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;pm2&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;delete&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;app_name&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;pm2&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;start&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;./dist/server/entry.mjs&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--name&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;app_name&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;&lt;a href=&quot;#解除限制自動化部署&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;解除限制！自動化部署&lt;/h2&gt;
&lt;p&gt;後來這個專案合作的後端工程師找到了編譯時記憶體不足的解法，在執行 &lt;code&gt;yarn build&lt;/code&gt; 時，增加 Node.js 的記憶體限制，這樣就可以直接在伺服器上編譯了，但這個數值還是不能超過實際上的記憶體上限呵呵：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;NODE_OPTIONS&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;--max-old-space-size=1024&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;yarn&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;build&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;如果可以在伺服器上直接編譯的話，就可以寫部署腳本來自動化部署了。這樣每次要更新網站時，只要把程式碼推到 GitHub 後，在伺服器上執行這個腳本就可以了：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;deploy.sh&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;#!/bin/bash&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pull&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;origin&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;main&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;yarn&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;NODE_OPTIONS&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;--max-old-space-size=1024&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;yarn&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;build&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;pm2&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;restart&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;app_name&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--update-env&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;然後就可以 ssh 到伺服器上執行這個腳本來部署了：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# 使用 ssh 登入伺服器&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ssh&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;user@remote_host&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;cd&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/var/www/html&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# 設定 git 不要處理檔案權限的變更，並給予 deploy.sh 執行權限&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;config&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;core.fileMode&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;chmod&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+x&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;deploy.sh&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# 執行部署腳本&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;./deploy.sh&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;</content:encoded></item><item><title>Astro 排除部分路徑不檢查 CSRF Origin</title><link>https://star-note-lucas.me/posts/astro-except-origin-check</link><guid isPermaLink="true">https://star-note-lucas.me/posts/astro-except-origin-check</guid><pubDate>Sun, 27 Jul 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Astro 預設會檢查提交表單的 Origin 是否與網站的 Origin 相同，目的是為了防止 CSRF 攻擊，但是在串第三方金流 API 的時候，回傳的請求就會被擋下來，因為第三方金流的 Origin 不會是我們的網站 Origin。&lt;/p&gt;
&lt;p&gt;既然 Astro 沒有提供排除部分路徑的選項，那就自己複製出來改吧！&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#修改-astro-的-origincheck-middleware&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;修改 Astro 的 &lt;code&gt;originCheck&lt;/code&gt; Middleware&lt;/h2&gt;
&lt;p&gt;先把 Astro 預設的 &lt;code&gt;checkOrigin&lt;/code&gt; 給關掉：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;astro.config.ts&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;defineConfig&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;security: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;// Change built-in origin check to custom function&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;// @see src/middleware.ts&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;checkOrigin: &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;})&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;然後在 &lt;code&gt;src/middleware/originCheck.ts&lt;/code&gt; 新增一個 &lt;code&gt;originCheck&lt;/code&gt; 的 middleware，裡面只新增了 &lt;code&gt;EXCLUDE_PATHS&lt;/code&gt; 陣列，放入需要排除的路徑，這樣就可以讓 Astro 在這些路徑上不檢查 CSRF 的 Origin 了。&lt;/p&gt;
&lt;p&gt;比如我們這邊就放了金流的回傳請求路徑 &lt;code&gt;/checkout/done&lt;/code&gt;：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;src/middleware/originCheck.ts&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;/**&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* &lt;/span&gt;&lt;span&gt;@reference&lt;/span&gt;&lt;span&gt; https://github.com/withastro/astro/blob/d2d04b02773f82103db75076fcdd1f089503770a/packages/astro/src/core/app/middlewares.ts&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { defineMiddleware } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;astro:middleware&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;/**&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* Content types that can be passed when sending a request via a form&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/enctype&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* &lt;/span&gt;&lt;span&gt;@private&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;FORM_CONTENT_TYPES&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;application/x-www-form-urlencoded&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;multipart/form-data&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;text/plain&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// Note: TRACE is unsupported by undici/Node.js&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;SAFE_METHODS&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;GET&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;HEAD&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;OPTIONS&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// Paths that should be excluded from origin check&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;EXCLUDE_PATHS&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;/checkout/done&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;/**&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* Returns a middleware function in charge to check the `origin` header.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;export &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;originCheck&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;defineMiddleware&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;context&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;next&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;request&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;url&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;isPrerendered&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;context&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;// Prerendered pages should be excluded&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;isPrerendered&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return &lt;/span&gt;&lt;span&gt;next&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;// Safe methods don&apos;t require origin check&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;SAFE_METHODS&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;includes&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;request&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;method&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return &lt;/span&gt;&lt;span&gt;next&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;// Exclude specific paths from origin check&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;EXCLUDE_PATHS&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;includes&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;url&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;pathname&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return &lt;/span&gt;&lt;/span&gt;&lt;span&gt;next&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;isSameOrigin&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;request&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;headers&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;get&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;origin&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;url&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;origin&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;hasContentType&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;request&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;headers&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;has&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;content-type&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;hasContentType&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;formLikeHeader&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;hasFormLikeHeader&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;request&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;headers&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;get&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;content-type&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;formLikeHeader&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;isSameOrigin&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Response&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;Cross-site &lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;request&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;method&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; form submissions are forbidden&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;status&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;403&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;isSameOrigin&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Response&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;Cross-site &lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;request&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;method&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; form submissions are forbidden&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;status&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;403&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return &lt;/span&gt;&lt;span&gt;next&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;hasFormLikeHeader&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;contentType&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;boolean&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (contentType) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;for&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;FORM_CONTENT_TYPE&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;of&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;FORM_CONTENT_TYPES&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (contentType&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;toLowerCase&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;includes&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;FORM_CONTENT_TYPE&lt;/span&gt;&lt;span&gt;)) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;最後在 &lt;code&gt;src/middleware/index.ts&lt;/code&gt; 中套用這個 middleware 就可以了：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;src/middleware/index.ts&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { sequence } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;astro:middleware&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { originCheck } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;./originCheck&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;export &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;onRequest&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sequence&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;// 必須將 `originCheck` 放在 middleware 的第一個&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;originCheck&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;</content:encoded></item><item><title>在 Astro 中用 nanostores 共享全局變數</title><link>https://star-note-lucas.me/posts/astro-nanostores</link><guid isPermaLink="true">https://star-note-lucas.me/posts/astro-nanostores</guid><pubDate>Sat, 08 Mar 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Astro 本身是沒有共享全局變數的功能，不過文件中建議使用 nanostores 這個套件，可以在純 JS 和各種前端框架中使用，以及支援 TypeScript，且使用上也很簡單。&lt;/p&gt;
&lt;p&gt;在 Astro 中我對 nanostores 使用方式的理解是，因為 Astro 預設每個頁面都會重新初始化，因此狀態管理就可以非常簡單，只需管理跨元件的共用狀態，不用管切換路由後如何清理資料。&lt;/p&gt;
&lt;p&gt;這篇文章記錄了一下我第一次用 nanostores 做的範例，是訊息提示元件的狀態管理，可以呼叫顯示訊息提示的函數，並在 2 秒後自動消失。&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#安裝-nanostores&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;安裝 nanostores&lt;/h2&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;yarn&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;nanostores&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;&lt;a href=&quot;#實作成功訊息功能&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;實作成功訊息功能&lt;/h2&gt;
&lt;p&gt;首先先定義一個 &lt;code&gt;$messageState&lt;/code&gt; 在 &lt;code&gt;src/stores/message.ts&lt;/code&gt; 中，&lt;code&gt;$messageState&lt;/code&gt; 保存了顯示訊息類型和訊息內容，然後寫一個 &lt;code&gt;showMessage()&lt;/code&gt; 函數來設定呼叫時要顯示的類型和內容，並在 2 秒後會自己消失：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;src/stores/message.ts&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { atom } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;nanostores&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt; MessageType &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;success&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;info&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;export &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;$messageState&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;atom&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt; as &lt;/span&gt;&lt;span&gt;MessageType&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;content&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;showMessage&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;MessageType&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;content&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;$messageState&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt;({ type&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; content })&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;setTimeout&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;$messageState&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt;({ type: &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; content: &lt;/span&gt;&lt;span&gt;&apos;&apos;&lt;/span&gt;&lt;span&gt; })&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;2000&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;然後需要增加一個 &lt;code&gt;src/components/MessagesContainer.astro&lt;/code&gt; 用來顯示訊息提示，會監聽剛才的 &lt;code&gt;$messageState&lt;/code&gt; 更新訊息內容，有內容存在就顯示出來：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;src/components/MessagesContainer.astro&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;messages-container&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { $messageState } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;@/stores/message&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;$messageState&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;subscribe&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;messagesContainer&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;document&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;querySelector&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;.messages-container&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; as &lt;/span&gt;&lt;span&gt;HTMLElement&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;messagesContainer&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;innerHTML&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;div class=&quot;message message-&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&quot;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;content&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;} &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;messagesContainer&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;innerHTML&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;})&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;這樣一個簡易的訊息提示功能就完成了~ 現在用一個複製連結按鈕當範例，用法就是綁一個點擊事件，然後呼叫 &lt;code&gt;showMessage()&lt;/code&gt; 函數即可：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;src/pages/index.astro&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { showMessage } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;@/stores/message&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;btnCopy&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;document&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;querySelector&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;.btn-copy&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; as &lt;/span&gt;&lt;span&gt;HTMLButtonElement&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;btnCopy&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;addEventListener&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;click&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;// 複製連結...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;showMessage&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;success&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;連結已複製&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;})&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;只是現在如果短時間內觸發多次的話，舊的 &lt;code&gt;setTimeout()&lt;/code&gt; 會干擾到新的狀態呈現，這邊我們可以加一個 &lt;code&gt;timer&lt;/code&gt;，在設定 &lt;code&gt;setTimeout()&lt;/code&gt; 之前先清除掉舊的 &lt;code&gt;timer&lt;/code&gt;：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;src/stores/message.ts&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;timer&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt; as &lt;/span&gt;&lt;span&gt;ReturnType&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;typeof&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;setTimeout&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;showMessage&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;MessageType&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;content&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;$messageState&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt;({ type&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; content })&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;typeof&lt;/span&gt;&lt;span&gt; timer &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;number&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;clearTimeout&lt;/span&gt;&lt;span&gt;(timer)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;timer &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;setTimeout&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;$messageState&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt;({ type: &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; content: &lt;/span&gt;&lt;span&gt;&apos;&apos;&lt;/span&gt;&lt;span&gt; })&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;2000&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;這個寫法是使用 Astro 元件和純 JS 來實作，基於 nanostores 可以跨框架的特性，也可以用在 React、Vue、Svelte 等等框架的元件中~&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#參考資料&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;參考資料&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/nanostores/nanostores&quot;&gt;nanostores&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>在 Laravel Forge 配置的服務器上用 Docker 部署 Vite 專案</title><link>https://star-note-lucas.me/posts/laravel-forge-dockerize-vite-app</link><guid isPermaLink="true">https://star-note-lucas.me/posts/laravel-forge-dockerize-vite-app</guid><description>第一次試 Docker 部署專案，以及使用 Nginx 設定反向代理，紀錄一下筆記。</description><pubDate>Fri, 21 Feb 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;第一次試 Docker 部署專案，以及使用 Nginx 設定反向代理，紀錄一下筆記。&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#docker-設定&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;Docker 設定&lt;/h2&gt;
&lt;p&gt;Docker 安裝基本是參考官方文件，這邊就不贅述了。&lt;/p&gt;
&lt;p&gt;這次測試用的專案是 Vite 6.1 版本，image 使用 &lt;code&gt;node:22-alpine&lt;/code&gt; 版本，單純的前端專案部署。下面是一個簡單的 &lt;code&gt;Dockerfile&lt;/code&gt; 設定：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Dockerfile&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;FROM&lt;/span&gt;&lt;span&gt; node:22-alpine&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;WORKDIR&lt;/span&gt;&lt;span&gt; /app&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;COPY&lt;/span&gt;&lt;span&gt; package.json yarn.lock ./&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;RUN&lt;/span&gt;&lt;span&gt; yarn&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;COPY&lt;/span&gt;&lt;span&gt; . .&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;RUN&lt;/span&gt;&lt;span&gt; yarn build&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;EXPOSE&lt;/span&gt;&lt;span&gt; 4173&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;CMD&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;&quot;yarn&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;preview&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;--host&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;0.0.0.0&quot;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;為了可以使用 Docker Compose 來管理，新增一個 &lt;code&gt;compose.yaml&lt;/code&gt;：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;compose.yaml&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;services&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;web&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;build&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;ports&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;4173:4173&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;然後就可以使用 Docker Compose 來啟動容器了：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;docker&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;compose&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;up&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-d&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;關閉容器，可以使用：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;docker&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;compose&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;down&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;&lt;a href=&quot;#nginx-反向代理設定&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;Nginx 反向代理設定&lt;/h2&gt;
&lt;p&gt;Forge 預設使用 Nginx 來做網站伺服器，因此我們可以直接使用 Nginx 來做反向代理，讓請求可以轉發到 Docker 容器上。&lt;/p&gt;
&lt;p&gt;在 Forge 的網站設定中，點 &lt;code&gt;Edit Files&lt;/code&gt; &amp;gt; &lt;code&gt;Edit Nginx Configuration&lt;/code&gt; 開啟 Nginx 設定，並根據以下設定調整：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# FORGE CONFIG (DO NOT REMOVE!)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;include &lt;/span&gt;&lt;span&gt;forge-conf/your-domain.com/before/*;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;server&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;   &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;listen &lt;/span&gt;&lt;span&gt;80&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;   &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;listen &lt;/span&gt;&lt;span&gt;[::]:80;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;   &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;server_name &lt;/span&gt;&lt;span&gt;your-domain.com;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;   &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;server_tokens &lt;/span&gt;&lt;span&gt;off&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;   &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;root &lt;/span&gt;&lt;span&gt;/home/forge/your-domain.com;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;# FORGE SSL (DO NOT REMOVE!)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;# ssl_certificate&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;# ssl_certificate_key&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;   &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;ssl_protocols &lt;/span&gt;&lt;span&gt;TLSv1.2 TLSv1.3;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;# ssl_ciphers XXXXXXX&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;   &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;ssl_prefer_server_ciphers &lt;/span&gt;&lt;span&gt;off&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;   &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;ssl_dhparam &lt;/span&gt;&lt;span&gt;/etc/nginx/dhparams.pem;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;   &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;add_header &lt;/span&gt;&lt;span&gt;X-Frame-Options &lt;/span&gt;&lt;span&gt;&quot;SAMEORIGIN&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;   &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;add_header &lt;/span&gt;&lt;span&gt;X-XSS-Protection &lt;/span&gt;&lt;span&gt;&quot;1; mode=block&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;   &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;add_header &lt;/span&gt;&lt;span&gt;X-Content-Type-Options &lt;/span&gt;&lt;span&gt;&quot;nosniff&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;   &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;charset &lt;/span&gt;&lt;span&gt;utf-8;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;# FORGE CONFIG (DO NOT REMOVE!)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;   &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;include &lt;/span&gt;&lt;span&gt;forge-conf/your-domain.com/server/*;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;location&lt;/span&gt;&lt;span&gt; / {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;       &lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;proxy_pass &lt;/span&gt;&lt;span&gt;http://127.0.0.1:4173;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;       &lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;proxy_redirect &lt;/span&gt;&lt;span&gt;off&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;       &lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;proxy_set_header &lt;/span&gt;&lt;span&gt;Host $&lt;/span&gt;&lt;span&gt;proxy_host&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;       &lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;proxy_set_header &lt;/span&gt;&lt;span&gt;X-Real-IP $&lt;/span&gt;&lt;span&gt;remote_addr&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;       &lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;proxy_set_header &lt;/span&gt;&lt;span&gt;X-Forwarded-For $&lt;/span&gt;&lt;span&gt;proxy_add_x_forwarded_for&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;       &lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;proxy_set_header &lt;/span&gt;&lt;span&gt;X-Forwarded-Proto $&lt;/span&gt;&lt;span&gt;scheme&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;location&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/favicon.ico &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt; access_log &lt;/span&gt;&lt;span&gt;off&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span&gt; log_not_found &lt;/span&gt;&lt;span&gt;off&lt;/span&gt;&lt;span&gt;; }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;location&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/robots.txt  &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt; access_log &lt;/span&gt;&lt;span&gt;off&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span&gt; log_not_found &lt;/span&gt;&lt;span&gt;off&lt;/span&gt;&lt;span&gt;; }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;   &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;access_log &lt;/span&gt;&lt;span&gt;off&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;   &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;error_log &lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;off&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;location&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/\.(?!well-known).* &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;       &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;deny &lt;/span&gt;&lt;span&gt;all&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# FORGE CONFIG (DO NOT REMOVE!)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;include &lt;/span&gt;&lt;span&gt;forge-conf/your-domain.com/after/*;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;主要是 &lt;code&gt;proxy_pass&lt;/code&gt; 等相關設定比較重要，其他的設定可以根據需求調整。&lt;/p&gt;
&lt;p&gt;雖然折騰了滿久的，不過瞭解了細節後還是挺簡單的嘛！(哼！看你下次撞完牆後還敢這麼說😂)&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#參考資料&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;參考資料&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.docker.com/engine/install/ubuntu/&quot;&gt;Install Docker Engine on Ubuntu&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.docker.com/engine/install/linux-postinstall/&quot;&gt;Linux post-installation steps for Docker Engine&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.digitalocean.com/community/tutorials/how-to-install-and-use-docker-on-ubuntu-22-04&quot;&gt;How To Install and Use Docker on Ubuntu 22.04&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.digitalocean.com/community/tutorials/how-to-install-and-use-docker-compose-on-ubuntu-22-04&quot;&gt;How To Install and Use Docker Compose on Ubuntu 22.04&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>Astro 使用 Zod 驗證網址 Query String 格式</title><link>https://star-note-lucas.me/posts/astro-zod-querystring</link><guid isPermaLink="true">https://star-note-lucas.me/posts/astro-zod-querystring</guid><pubDate>Sun, 09 Feb 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;網址中的 Query String 參數驗證是一個還滿常見的需求，通常都會在需要做搜尋或過濾器的頁面中使用，傳統的作法都會是使用 if 判斷式來驗證參數是否符合預期的格式，但這樣的寫法我是覺得還滿醜的說。後來我就嘗試使用 &lt;a href=&quot;https://github.com/colinhacks/zod&quot;&gt;zod&lt;/a&gt; 來解析驗證查詢參數，程式碼的確是看起來更簡潔也更好維護了~ 雖然需要稍微了解一下 zod 的用法，但其實也不難上手。&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#安裝套件&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;安裝套件&lt;/h2&gt;
&lt;p&gt;安裝 zod 和 query-string 這兩個套件，因為要解析網址 Query String，因此也一併安裝了 query-string：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;npm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;zod&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;query-string&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# or&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;yarn&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;zod&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;query-string&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;&lt;a href=&quot;#解析-query-string-參數&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;解析 Query String 參數&lt;/h2&gt;
&lt;p&gt;這邊先看一個解析 Query String 參數的範例：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { z } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;zod&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; qs &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;query-string&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;defaultParams&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;page&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;priceFrom&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;priceTo&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1000&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;sort&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;latest&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; as &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;latest&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;oldest&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;PRICE_MIN&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;PRICE_MAX&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1000&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;querySchema&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;z&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;object&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;page&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;z&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;coerce&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;number&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;positive&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;catch&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;z&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;enum&lt;/span&gt;&lt;span&gt;([&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;b&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;c&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;])&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;nullish&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;catch&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;search&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;z&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;coerce&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;nullish&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;catch&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;category&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;z&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;coerce&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;number&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;nullish&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;catch&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;priceFrom&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;z&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;coerce&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;number&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;min&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;PRICE_MIN&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;max&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;PRICE_MAX&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;defaultParams&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;priceFrom&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;catch&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;defaultParams&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;priceFrom&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;priceTo&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;z&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;coerce&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;number&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;min&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;PRICE_MIN&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;max&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;PRICE_MAX&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;defaultParams&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;priceTo&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;catch&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;defaultParams&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;priceTo&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;tags&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;z&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;preprocess&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;val &lt;/span&gt;&lt;span&gt;||&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[]]&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;flat&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;map&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Number&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;filter&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Boolean&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;z&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;array&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;z&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;number&lt;/span&gt;&lt;span&gt;()))&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;catch&lt;/span&gt;&lt;span&gt;([])&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;sort&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;z&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;enum&lt;/span&gt;&lt;span&gt;([&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;latest&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;oldest&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;])&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;defaultParams&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;sort&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;catch&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;defaultParams&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;sort&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;params&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;querySchema&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;parse&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;qs&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;parse&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Astro&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;url&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;search&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;以下是 zod 常見驗證查詢參數的範例：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// string 類型&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;z&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;coerce&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;nullish&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;catch&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// enum 類型&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// 預設值為 null&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;z&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;enum&lt;/span&gt;&lt;span&gt;([&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;product&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;service&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;])&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;nullish&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;catch&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// 預設值不為 null&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;z&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;enum&lt;/span&gt;&lt;span&gt;([&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;latest&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;oldest&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;])&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;latest&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;catch&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;latest&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// number 類型&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// 預設值為 null&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;z&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;coerce&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;number&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;nullish&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;catch&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// 預設值不為 null&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;z&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;coerce&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;number&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;catch&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// 限制範圍&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;z&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;coerce&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;number&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;min&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;max&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;100&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;catch&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// page 分頁頁數&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;z&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;coerce&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;number&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;positive&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;catch&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// array 類型&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// 例如 tags 是 number[] 類型&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;//&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// array 類型比較複雜，可以先使用 preprocess() 來處理輸入的值，&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// 將輸入的值轉換成內部都是 Number 的陣列，並且過濾掉非數字的值，&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// 最後再使用 z.array(z.number()) 來驗證陣列中的值都是數字。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;z&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;preprocess&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; [val &lt;/span&gt;&lt;span&gt;||&lt;/span&gt;&lt;span&gt; []]&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;flat&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;map&lt;/span&gt;&lt;span&gt;(Number)&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;filter&lt;/span&gt;&lt;span&gt;(Boolean), z&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;array&lt;/span&gt;&lt;span&gt;(z&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;number&lt;/span&gt;&lt;span&gt;()))&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;catch&lt;/span&gt;&lt;span&gt;([])&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;這邊說明一下上面範例中使用到的 zod 方法：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;coerce&lt;/code&gt;：將值強制轉換為指定的類型。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;enum&lt;/code&gt;：限制值必須是列舉中的其中一個。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;min&lt;/code&gt;：限制值必須大於等於指定的最小值。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;max&lt;/code&gt;：限制值必須小於等於指定的最大值。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;positive&lt;/code&gt;：限制值必須大於 0。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;default&lt;/code&gt;：設定預設值。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;nullish&lt;/code&gt;：允許值為 null 或 undefined。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;catch&lt;/code&gt;：當值無法通過驗證時，使用預設值。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;preprocess&lt;/code&gt;：對值進行預處理。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;使用 zod 來驗證查詢參數的關鍵是 &lt;code&gt;coerce&lt;/code&gt;、&lt;code&gt;default&lt;/code&gt; 和 &lt;code&gt;catch&lt;/code&gt; 這三個方法：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;coerce&lt;/code&gt; 用來將值轉換為指定的類型，比如解析數字時非常關鍵&lt;/li&gt;
&lt;li&gt;而 &lt;code&gt;default&lt;/code&gt; 和 &lt;code&gt;catch&lt;/code&gt; 的組合可以無論是沒有輸入、或是錯誤的輸入，都可以套用預設值，不會因為錯誤的輸入而導致程式出錯。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;最後就可以在 Astro 頁面中使用解析後的參數了，而且參數一定會是符合預期的格式和型別：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;ul&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;page: &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;params&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;page&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;type: &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;params&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;search: &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;params&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;search&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;category: &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;params&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;category&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;priceFrom: &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;params&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;priceFrom&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;priceTo: &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;params&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;priceTo&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;tags: &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;params&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;tags&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;sort: &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;params&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;sort&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;ul&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;&lt;a href=&quot;#參考資料&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;參考資料&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/colinhacks/zod&quot;&gt;zod 說明文件&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>Astro + @astrolicious/i18n 實作 i18n 多語系功能</title><link>https://star-note-lucas.me/posts/astro-i18n-astrolicious</link><guid isPermaLink="true">https://star-note-lucas.me/posts/astro-i18n-astrolicious</guid><pubDate>Sat, 08 Feb 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;之前有使用過 &lt;a href=&quot;https://star-note-lucas.me/posts/astro-i18n-paraglide&quot;&gt;用 Paraglide 做 Astro i18n&lt;/a&gt;，但做起來就是很卡。後來發現 &lt;a href=&quot;https://astro-i18n.netlify.app/&quot;&gt;I18n for Astro (@astrolicious/i18n)&lt;/a&gt; 這個套件更好用，於是就改用這個套件來做多語系功能。&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#安裝-astroliciousi18n&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;安裝 @astrolicious/i18n&lt;/h2&gt;
&lt;p&gt;@astrolicious/i18n 是基於 i18next 這個套件來做的，因此也需要安裝 i18next：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;yarn&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;i18next&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;@astrolicious/i18n&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;然後註冊 i18n 套件：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;astro.config.ts&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; i18n &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;@astrolicious/i18n&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;defineConfig&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;integrations: [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;i18n&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;defaultLocale: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;zh-TW&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;locales: [&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;zh-TW&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;en&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;})&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;在 Layout 中加入 I18nHead 元件來處理 SEO 相關的 meta：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;src/layouts/Layout.astro&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; I18nHead &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;@astrolicious/i18n/components/I18nHead.astro&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { getHtmlAttrs, getLocale } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;i18n:astro&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;locale&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;getLocale&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;htmlLangMap&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Record&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;zh-TW&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;zh-Hant-TW&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;htmlAttrs&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;getHtmlAttrs&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;htmlAttrs&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;lang&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; htmlLangMap[locale] &lt;/span&gt;&lt;span&gt;||&lt;/span&gt;&lt;span&gt; locale&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;!&lt;/span&gt;&lt;span&gt;doctype&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt; {...&lt;/span&gt;&lt;span&gt;htmlAttrs&lt;/span&gt;&lt;span&gt;}&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;head&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;meta&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;charset&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;UTF-8&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;meta&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;viewport&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;content&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;width=device-width&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;I18nHead&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;head&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;&lt;a href=&quot;#設定多語系檔案&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;設定多語系檔案&lt;/h2&gt;
&lt;p&gt;在 &lt;code&gt;src/locales/&lt;/code&gt; 目錄下建立語系檔案，檔案格式為 JSON：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;src/locales/en/common.json&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;nav_about&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;About&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;nav_home&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Home&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;nav_posts&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Posts&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;src/locales/zh-TW/common.json&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;nav_about&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;關於&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;nav_home&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;首頁&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;nav_posts&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;文章&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;&lt;a href=&quot;#使用翻譯&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;使用翻譯&lt;/h2&gt;
&lt;p&gt;然後在元件或頁面中使用 &lt;code&gt;t&lt;/code&gt; 函式來取得翻譯：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;src/layouts/Layout.astro&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { t, getHtmlAttrs, getLocale, getLocalePath } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;i18n:astro&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;!&lt;/span&gt;&lt;span&gt;doctype&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt; {...&lt;/span&gt;&lt;span&gt;htmlAttrs&lt;/span&gt;&lt;span&gt;}&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;head&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;head&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;ul&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;menu&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class:list&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;menu-item&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, pathname &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;getLocalePath&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;active&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;getLocalePath&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;t&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;nav_home&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class:list&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;menu-item&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, pathname&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;startsWith&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;getLocalePath&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;/posts/&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;)) &lt;/span&gt;&lt;span&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;active&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;getLocalePath&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;/posts/&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;t&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;nav_posts&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class:list&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;menu-item&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, pathname &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;getLocalePath&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;/about/&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;active&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;getLocalePath&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;/about/&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;t&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;nav_about&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;ul&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;&lt;a href=&quot;#新增語言選單&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;新增語言選單&lt;/h2&gt;
&lt;p&gt;可以顯示語系文字後，就可以新增語言選單切換語系：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;src/layouts/Layout.astro&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { t, getHtmlAttrs, getLocale, getLocalePath, getSwitcherData } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;i18n:astro&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;switcherData&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;getSwitcherData&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;switcherLabels&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Record&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;en&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;English&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;zh-TW&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;繁體中文&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;!&lt;/span&gt;&lt;span&gt;doctype&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt; {...&lt;/span&gt;&lt;span&gt;htmlAttrs&lt;/span&gt;&lt;span&gt;}&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;head&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;head&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;ul&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;menu&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;menu-item menu-item-lang menu-item-has-children&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;#&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;fas fa-globe&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;switcherLabels[locale]&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;ul&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;sub-menu&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;switcherData&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;map&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;item&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;list&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;menu-item&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, item&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;locale&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; locale &lt;/span&gt;&lt;span&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;active&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;              &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;item&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;replace&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;\/&lt;/span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;switcherLabels[item&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;locale&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;ul&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;ul&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;&lt;a href=&quot;#完整程式碼&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;完整程式碼&lt;/h2&gt;
&lt;p&gt;以下是完整的 Layout 程式碼：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;src/layouts/Layout.astro&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { t, getHtmlAttrs, getLocale, getLocalePath, getSwitcherData } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;i18n:astro&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;locale&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;getLocale&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;switcherData&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;getSwitcherData&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;switcherLabels&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Record&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;en&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;English&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;zh-TW&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;繁體中文&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;htmlLangMap&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Record&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;zh-TW&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;zh-Hant-TW&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;htmlAttrs&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;getHtmlAttrs&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;htmlAttrs&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;lang&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; htmlLangMap[locale] &lt;/span&gt;&lt;span&gt;||&lt;/span&gt;&lt;span&gt; locale&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;!&lt;/span&gt;&lt;span&gt;doctype&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt; {...&lt;/span&gt;&lt;span&gt;htmlAttrs&lt;/span&gt;&lt;span&gt;}&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;head&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;meta&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;charset&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;UTF-8&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;meta&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;viewport&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;content&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;width=device-width&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;I18nHead&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;head&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;ul&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;menu&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class:list&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;menu-item&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, pathname &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;getLocalePath&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;active&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;getLocalePath&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;t&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;nav_home&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class:list&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;menu-item&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, pathname&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;startsWith&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;getLocalePath&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;/posts/&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;)) &lt;/span&gt;&lt;span&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;active&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;getLocalePath&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;/posts/&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;t&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;nav_posts&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class:list&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;menu-item&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, pathname &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;getLocalePath&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;/about/&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;active&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;getLocalePath&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;/about/&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;t&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;nav_about&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;menu-item menu-item-lang menu-item-has-children&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;#&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;fas fa-globe&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;switcherLabels[locale]&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;ul&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;sub-menu&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;switcherData&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;map&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;item&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;list&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;menu-item&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, item&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;locale&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; locale &lt;/span&gt;&lt;span&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;active&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;              &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;item&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;replace&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;\/&lt;/span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;switcherLabels[item&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;locale&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;ul&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;ul&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;</content:encoded></item><item><title>WebConf 2024 心得</title><link>https://star-note-lucas.me/posts/webconf-2024</link><guid isPermaLink="true">https://star-note-lucas.me/posts/webconf-2024</guid><description>趁記憶還在的時候記一下聽完的小小心得</description><pubDate>Thu, 09 Jan 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;紀錄一下去聽 WebConf 2024 的小小心得~&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#如何撰寫具彈性的測試程式&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;如何撰寫具彈性的測試程式&lt;/h2&gt;
&lt;p&gt;第一次看到 Summer 的真面目XDDD 知道 Summer 這個人還是從部落格「Summer。桑莫。夏天」了解的。這次議程也是從測試的一些心法概念開始講起，也有講到許多測試的 Best practice。雖然舉例是用 React，不過 Vue 應該也是同樣適用吧~&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#設計很豐滿開發很骨感--淺談設計交付&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;設計很豐滿，開發很骨感 — 淺談設計交付&lt;/h2&gt;
&lt;p&gt;第一次了解到 Figma 有推出 Design Tokens 的功能！！！畢竟我平常沒有在關注 Figma 的新功能，反而是這兩天，在不同議程都有聽到講者提到這個東西，才知道說這是一個神器！之後可以來玩玩看~&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#淺談-llm-based-ai-agents-應用開發&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;淺談 LLM-based AI Agents 應用開發&lt;/h2&gt;
&lt;p&gt;我以前只有稍微了解 LLM 而已，對於 Function-Call 等之類的概念都不熟，而這個議程剛好就提到這些 LLM 的基礎內容，也算是有學到新知識了~&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#天下如何思考數位敘事從流程到技術大公開&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;《天下》如何思考數位敘事？從流程到技術大公開&lt;/h2&gt;
&lt;p&gt;這個議程從公佈開始我就超期待的~ 或許我就很喜歡這種解說幕後性質的吧！這回採用 PM + 工程師的兩人搭配還滿新奇的，因為我就很喜歡這種動態網頁的效果，而他們卻可以把動態網頁融入進整個數位報導之中。&lt;/p&gt;
&lt;p&gt;且技術並不是為用而用，而是輔助整個報導可以呈現出完整的故事。最喜歡提到的一句「數位敘事，並不是報導的華麗裝飾」。技術上各個專案雖不盡相同，但大體上都是前端圖表、Three.js 3D 建模等方式來做呈現，甚至在需要呈現衛星 3D 畫面時，還需要使用專屬的套件和請教專家來完成，好厲害啊~&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#網站專案時程的挑戰跟威脅&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;網站專案時程的挑戰跟威脅&lt;/h2&gt;
&lt;p&gt;這個議程 TonyQ 講得我覺得超有趣的~ 分享了滿多鬼故事和管理團隊的建議，感覺一整場都是精華，有談到不少帶團隊的內容，但離我現在還比較遠，可以以後再來慢慢研究~ 這場如果有公開錄影的話還會想再聽一次~&lt;/p&gt;
&lt;p&gt;其中對於員工的一條建議記得最清楚：「個人工作時如果有遇到問題要先說出來，去煩死你的主管！」與其等事情爆發，還不如提出問題可以提前做處理。&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#zeabur雲端部署平台-paas-核心技術大揭秘&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;Zeabur：雲端部署平台 PaaS 核心技術大揭秘&lt;/h2&gt;
&lt;p&gt;當時看到這個議程我都傻了！因為平常只會寫一點簡單的前/後端的程式碼，對雲、K8S 都沒什麼研究，這回沅霖講的靈感來自 Vercel，從零開始基於 K8S 做的部署平台 Zeabur，我基本上都聽不大懂，只覺得超厲害的說~&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#實戰以-web-3d-技術開發商品客製編輯器&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;實戰！以 Web 3D 技術開發商品客製編輯器&lt;/h2&gt;
&lt;p&gt;當時一看到 3D 編輯器馬上就選定一定要看了~ 剛好也是因為最近也是有接做 3D 編輯器的案子，也是用 Three.js 開發，就想來了解一下有沒有有趣的分享~ 這個議程主要是分享講者 GUGU 的一些 3D 案子的故事，也有網頁做 3D 的一些小 Tips，還滿有趣的~&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#eslint-one-for-all-made-easy&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;ESLint One for All Made Easy&lt;/h2&gt;
&lt;p&gt;哇！終於可以在現場看到我的偶像 Anthony Fu 的演講了~ 這回講的 ESLint Flat config 我也是有先了解過的，當時也是想要進入 ESLint 的世界，奈何設定太複雜，不想自己做，後來就基於 Anthony 的 ESLint Config 來擴充的。這回也聽到了一些有趣的小技巧，又在現場看到了 Anthony，滿足~&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#從零到頂尖無懈可擊的網頁設計&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;從零到頂尖：無懈可擊的網頁設計&lt;/h2&gt;
&lt;p&gt;這場勞哥真的算是把設計的精華都分享出來了~ 而對於在場聽眾的前端開發者們，我最印象深刻的是「前端要超越設計」，不是完美復刻設計稿，而是要超越設計稿，哇！感覺前端的工作開始變得高大上起來的😂 其他細節推薦再去看勞哥的簡報了~ (唯一一個小缺點是勞哥的 VTuber 大頭時不時會擋到字就是了XD)&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#從生活啟程的網站設計體驗&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;從生活啟程的網站設計體驗&lt;/h2&gt;
&lt;p&gt;這個我最有印象的就是「台北白晝之夜」了！第一次知道原來2年前台北有辦過這個活動，而且這個網站最有趣的就是3D的台北地圖了！真的佩服講者 Henry 可以策畫出這麼高互動性的展覽~ 而且讓線上線下之間都可以互動，看起來超好玩的~&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#當-vue-與-view-分手之後&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;當 Vue 與 View 分手之後⋯&lt;/h2&gt;
&lt;p&gt;其實這個議程的主題是 Vue Vapor Mode，看起來是對於底層的優化，而一般開發者不會太感覺到差異。&lt;/p&gt;
&lt;p&gt;我看的時候一直都在想，如果專案切成 Vapor Mode 的時候，是不是套件會先壞一半以上啊？不過現在還不急著著急，因為還沒正式推出，等之後 Vue 團隊應該也會為套件開發者寫 Vapor Mode 的文件吧~&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#結語&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;結語&lt;/h2&gt;
&lt;p&gt;實話說剛聽完兩天是真的累爆了！每場議程中間只有 10 分鐘的休息時間，如果上個議程有拖到時間的還會少休息到，但為了不浪費門票還是堅持聽完了呵呵呵😭&lt;/p&gt;
&lt;p&gt;第一次聽 PHP/Laravel 以外的研討會，聽到了平常不是很熟的設計相關議程，也算是增加了見識的廣度吧！當然，這次門票是有值回票價的😆~&lt;/p&gt;</content:encoded></item><item><title>Inertia v2.0 新功能介紹</title><link>https://star-note-lucas.me/posts/inertia-v2</link><guid isPermaLink="true">https://star-note-lucas.me/posts/inertia-v2</guid><description>介紹 Inertia v2.0 推出的新功能，包含 Async Requests (異步載入)、Polling (輪巡)、Prefetching (預先載入)、Deferred props (延遲載入 Prop)、Lazy loading (惰性載入) 等</description><pubDate>Wed, 25 Dec 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Inertia 是一個像膠水一樣的東西，將前後端的框架黏合在一起。在這裡你可以看到用 Laravel 的路由 + Vue.js/React 前端頁面來開發 SPA 網頁應用。而在 2024 年年末，終於 Inertia 推出了 v2.0！我等了好久啊😭！！！下面就一起來看看有哪些新功能吧~&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#async-requests-異步載入&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;Async Requests (異步載入)&lt;/h2&gt;
&lt;p&gt;在 Inertia v1.0 的時候，預設載入頁面和 Partial reload (部分重載) 的時候都是同步載入 (Synchronous) 的，機制上是需要等到請求完成後才能發送下個請求，而畫面上則是都會有 Loading 載入的進度條。&lt;/p&gt;
&lt;p&gt;這個機制在載入頁面是沒有問題的，畢竟是第一次載入，理所當然一次只能發一個請求，但是在 Partial reload 的時候就變得不大美觀。因為 Partial reload 就是想要模擬打 API 請求的效果，但一次只能發一個請求，且在發送請求時還會出現進度條，就看起來很奇怪。&lt;/p&gt;
&lt;p&gt;因為這些原因，Inertia 團隊重寫了載入請求的程式碼，從 Inertia v2.0 開始，預設對當前頁面 Reload 的請求使用了異步載入 (Asynchronous) 的方式。異步載入可以同時發送多個請求的時候，不會影響當前頁面的請求，也不會出現上方的載入進度條，但要注意異步載入無法取消。&lt;/p&gt;

























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;&lt;/th&gt;&lt;th&gt;同步載入 (Synchronous)&lt;/th&gt;&lt;th&gt;異步載入 (Asynchronous)&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;同時請求數&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;Infinity (無限制)&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;/tr&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;/tbody&gt;&lt;/table&gt;
&lt;h2&gt;&lt;a href=&quot;#polling-輪巡&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;Polling (輪巡)&lt;/h2&gt;
&lt;p&gt;Polling (輪巡) 是可以固定時間重新拉取新資料的功能，通常用於需要即時資料的情境，比如比賽計分、天氣資訊、資訊看板等。&lt;/p&gt;
&lt;p&gt;雖然說要手寫也不是什麼難事，不過 Inertia 還是新增了此輔助函數，簡化了需要寫的程式量，讓定時更新資料可以更方便。而且在預設情況下，會在比如縮小視窗、切換到其他頁籤等背景執行時候，限制輪巡的請求。&lt;/p&gt;
&lt;p&gt;要使用輪巡，只要呼叫 &lt;code&gt;usePoll()&lt;/code&gt; 就可以了，而參數是給每次輪巡間隔的毫秒數：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { usePoll } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;@inertiajs/vue3&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// 2秒後重新拉資料&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;usePoll&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;2000&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;效果差不多長這樣：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://star-note-lucas.me/images/2024-12-25-inertia-v2-polling.gif&quot; alt=&quot;Polling 效果&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#prefetching-預先載入&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;Prefetching (預先載入)&lt;/h2&gt;
&lt;p&gt;Prefetching (預先載入) 可以在使用者尚未點擊連結時，預先載入該連結的資料，這樣使用者真正點下此連結時就不用等待載入時間，可以直接看到資料，提升使用者體驗。&lt;/p&gt;
&lt;p&gt;要使用 Prefetching 很簡單，只要在 &lt;code&gt;&amp;lt;Link&amp;gt;&lt;/code&gt; 元件中加上 &lt;code&gt;prefetch&lt;/code&gt; 屬性就可以了，這樣就會在滑鼠滑入連連結 75 毫秒後在背景載入該頁面：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;Link&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;/users&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;prefetch&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;Users&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;Link&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;setup&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { Link } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;@inertiajs/vue3&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;效果差不多長這樣：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://star-note-lucas.me/images/2024-12-25-inertia-v2-prefetching.gif&quot; alt=&quot;Prefetching 效果&quot; /&gt;&lt;/p&gt;
&lt;p&gt;也可以改變載入的時機，比如設定 &lt;code&gt;click&lt;/code&gt; 是在點擊連結時預載：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;Link&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;/users&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;prefetch&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;click&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;Users&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;Link&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;如果確定使用者一定會點擊連結，可以設定 &lt;code&gt;mount&lt;/code&gt; 在頁面載入時就開始預載：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;Link&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;/users&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;prefetch&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;mount&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;Users&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;Link&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;預先載入的資料預設會快取 30 秒，快取時間結束後的下次請求，就會強制重新發一次請求。可以透過 &lt;code&gt;cache-for&lt;/code&gt; 屬性來設定快取時間，比如 &lt;code&gt;500ms&lt;/code&gt; 是 500 毫秒，&lt;code&gt;6s&lt;/code&gt; 是 6 秒，&lt;code&gt;1m&lt;/code&gt; 是一分鐘等類推，也可以直接傳整數進去設定毫秒：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;Link&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;/users&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;prefetch&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cache-for&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;6s&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;Users&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;Link&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;Link&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;/users&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;prefetch&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cache-for&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;1m&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;Users&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;Link&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;Link&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;/users&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;prefetch&lt;/span&gt;&lt;span&gt; :&lt;/span&gt;&lt;span&gt;cache-for&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;5000&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;Users&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;Link&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;cache-for&lt;/code&gt; 屬性可以設定兩組時間，第一組是快取資料的有效時間，第二組是從伺服器取得新資料之前，還可以作為舊資料提供使用的時間：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;Link&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;/users&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;prefetch&lt;/span&gt;&lt;span&gt; :&lt;/span&gt;&lt;span&gt;cache-for&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;30s&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;1m&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;Users&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;Link&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;嗯… 是的，聽完之後一定是霧煞煞的，這兩個不是差不多嗎？，因為我一開始也沒太看懂，後來研究了之後才慢慢搞懂。下面我們一個一個來看：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;第一組時間   第二組時間&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;--------- * --------- * ---------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;有效期間    資料過時     完全過期&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;如果是在快取的有效期間 (第一組時間之前) 發出請求，會直接回傳快取資料，不會真的發送請求。&lt;/p&gt;
&lt;p&gt;如果是在資料過時 (兩組時間之間) 發出新請求，會先回傳舊資料，並且在背景發送請求，等到新資料回來後，就會把資料更新上去。&lt;/p&gt;
&lt;p&gt;如果是在第二組時間之後發出新請求，會將快取資料視為過期，就會像往常規的請求方式一樣，發送新請求來取得新資料。&lt;/p&gt;
&lt;p&gt;這個 Cache 的機制，要找一個接近的技術的話，應該是借鑑了 HTTP Cache-Control 的 stale-while-revalidate 機制吧？這是我後來才知道的XD&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;如果想要更細粒度的控制的話，現在官網有記載一個 &lt;code&gt;usePrefetch()&lt;/code&gt; 的 hook，但現在似乎是壞掉的狀態，不建議現在使用。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;&lt;a href=&quot;#deferred-props-延遲載入資料&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;Deferred Props (延遲載入資料)&lt;/h2&gt;
&lt;p&gt;Deferred Props (延遲載入資料)，可以讓當前頁面 HTML 先載入後，再去取得資料，體感上又更接近 SPA 的效果。通常如果有以下情況，可以考慮使用 Deferred Props：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;資料庫查詢時間較長&lt;/li&gt;
&lt;li&gt;載入大量資料&lt;/li&gt;
&lt;li&gt;呼叫外部 API&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;使用方式上，後端部分可以使用 &lt;code&gt;Inertia::defer()&lt;/code&gt; 方法，將查詢資料的程式包在函數裡，這樣在載入頁面時就不會先去查詢資料：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Inertia&lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt;render&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Users/Index&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;users&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Inertia&lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt;defer&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;fn&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt; =&amp;gt; &lt;/span&gt;&lt;span&gt;User&lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt;all&lt;/span&gt;&lt;span&gt;()),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;]);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;預設情況下，所有的 Deferred Props 都會包在一個請求當中，不過可以透過將 Props 分不同群組來並行載入。比如下面的 &lt;code&gt;teams&lt;/code&gt;、&lt;code&gt;projects&lt;/code&gt;、&lt;code&gt;tasks&lt;/code&gt; 這三個 Props 就會在同一個請求中載入，而 &lt;code&gt;users&lt;/code&gt; 則會在另一個請求中載入，這兩個請求會同步開始執行。群組名稱是字串，可以自訂名稱：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Inertia&lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt;render&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Users/Index&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;users&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Inertia&lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt;defer&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;fn&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt; =&amp;gt; &lt;/span&gt;&lt;span&gt;User&lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt;all&lt;/span&gt;&lt;span&gt;()),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;teams&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Inertia&lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt;defer&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;fn&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt; =&amp;gt; &lt;/span&gt;&lt;span&gt;Team&lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt;all&lt;/span&gt;&lt;span&gt;(), &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;attributes&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;projects&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Inertia&lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt;defer&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;fn&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt; =&amp;gt; &lt;/span&gt;&lt;span&gt;Project&lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt;all&lt;/span&gt;&lt;span&gt;(), &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;attributes&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;tasks&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Inertia&lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt;defer&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;fn&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt; =&amp;gt; &lt;/span&gt;&lt;span&gt;Task&lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt;all&lt;/span&gt;&lt;span&gt;(), &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;attributes&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;]);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;前端部分要引入 &lt;code&gt;&amp;lt;Deferred&amp;gt;&lt;/code&gt; 元件：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { Deferred } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;@inertiajs/vue3&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;然後在 &lt;code&gt;&amp;lt;Deferred&amp;gt;&lt;/code&gt; 元件中傳入延遲載入 Prop 的名稱，內部可以增加一個 &lt;code&gt;fallback&lt;/code&gt; 的 Slot 來顯示載入中的畫面：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;Deferred&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;users&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;template&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;#fallback&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;載入中...&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;template&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;使用者列表&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;table&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;table&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;Deferred&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;效果差不多長這樣：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://star-note-lucas.me/images/2024-12-25-inertia-v2-deferred-props.gif&quot; alt=&quot;Deferred Props 效果&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#lazy-loading-惰性載入&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;Lazy Loading (惰性載入)&lt;/h2&gt;
&lt;p&gt;Lazy Loading (惰性載入) 也是一種延緩載入資料時間的功能。這個看名字乍看之下和上面的 Deferred Props 有些相像，但其實是不一樣的。Deferred Props 是在載入頁面完成後就馬上載入新資料，而 Lazy Loading 是在使用者&lt;strong&gt;滾動&lt;/strong&gt;到元素時才載入，機制上是不大一樣的。&lt;/p&gt;
&lt;p&gt;因此要判斷使用哪個功能，最簡單就是看當前頁面的長度，如果頁面不長，且新資料的位置是在頁面頂部，就使用 Deferred Props；如果頁面很長很長，新資料需要滾動後才能看到的，就使用 Lazy Loading。&lt;/p&gt;
&lt;p&gt;Lazy Loading 在以下情境，且需要滾動後才能看到的時候可以使用：&lt;/p&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;p&gt;使用方式上，後端部分可以使用 &lt;code&gt;Inertia::lazy()&lt;/code&gt; 方法，將查詢資料的程式包在函數裡，這樣在載入頁面時就不會先去查詢資料：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Inertia&lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt;render&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Users/Chart&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;chartData&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Inertia&lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt;lazy&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;fn&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt; =&amp;gt; [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;labels&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Sun&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Mon&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Tue&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Wed&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Thu&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Fri&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Sat&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;12&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;19&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;7&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;]);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;前端部分要引入 &lt;code&gt;&amp;lt;WhenVisible&amp;gt;&lt;/code&gt; 元件：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { WhenVisible } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;@inertiajs/vue3&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;然後在 &lt;code&gt;&amp;lt;WhenVisible&amp;gt;&lt;/code&gt; 元件中傳入要 Lazy Loading 的 Prop 名稱，內部可以增加一個 &lt;code&gt;fallback&lt;/code&gt; 的 Slot 來顯示載入中的畫面：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;WhenVisible&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;chartData&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;template&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;#fallback&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;載入圖表中...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;template&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;UsersChart&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:data&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;chartData&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;WhenVisible&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;效果差不多長這樣：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://star-note-lucas.me/images/2024-12-25-inertia-v2-lazy-loading.gif&quot; alt=&quot;Lazy Loading 效果&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#infinite-scroll-無限滾動&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;Infinite Scroll (無限滾動)&lt;/h2&gt;
&lt;p&gt;Infinite Scroll 在 Inertia v2.2 終於推出了~&lt;/p&gt;
&lt;p&gt;參考文章：&lt;a href=&quot;https://star-note-lucas.me/posts/inertia-v2-infinite-scroll&quot;&gt;Inertia v2.2 的 Infinite Scroll (無限滾動) 新功能&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#升級到-v2&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;升級到 v2&lt;/h2&gt;
&lt;p&gt;從 Inertia v1 升級到 v2，可以參考官方的 &lt;a href=&quot;https://inertiajs.com/upgrade-guide&quot;&gt;v2.0 升級指南&lt;/a&gt; 來進行升級。基本沒有太大的更動，主要差別是版本的調整。Laravel 的部分刪除了 8 和 9 版的支援，最低支援版本是 Laravel 10 和 PHP 8.1，Vue.js 的部分則是刪除了 Vue 2。如果依賴版本沒有太舊的話，理論上升級應該是沒有太大問題的。&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#demo&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;Demo&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://star-note-lucas.me/images/2024-12-25-inertia-v2-demo.jpg&quot; alt=&quot;Inertia v2.0 Demo 預覽&quot; /&gt;&lt;/p&gt;
&lt;p&gt;這次的文章提到的新功能，我寫了一個簡單的 Demo，可以下載這個 GitHub Repo &lt;a href=&quot;https://github.com/ycs77/inertia-v2-demo&quot;&gt;inertia-v2-demo&lt;/a&gt; 來玩玩看~&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#結語&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;結語&lt;/h2&gt;
&lt;p&gt;Inertia v2.0 推出了許多新功能，我自然是很高興的~ 只是還是有些細節並沒有完善就推出穩定版，是比較可惜的說… 不過 Inertia 對於全端開發上是很有幫助的，至少對我來說是這樣啦XD 相信未來會越來越好的😁！&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#參考資料&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;參考資料&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://inertiajs.com/upgrade-guide&quot;&gt;Upgrade guide for v2.0 - Inertia.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://inertiajs.com/polling&quot;&gt;Polling - Inertia.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://inertiajs.com/prefetching&quot;&gt;Prefetching - Inertia.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://inertiajs.com/deferred-props&quot;&gt;Deferred props - Inertia.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://inertiajs.com/load-when-visible&quot;&gt;Load when visible - Inertia.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.laravel.com/announcing-inertia-20-redefining-frontend-development-for-laravel&quot;&gt;Announcing Inertia 2.0: Redefining Frontend Development for Laravel&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>孤獨搖滾劇場版後篇心得</title><link>https://star-note-lucas.me/posts/bocchi-the-rock-soushuuhen-2</link><guid isPermaLink="true">https://star-note-lucas.me/posts/bocchi-the-rock-soushuuhen-2</guid><pubDate>Tue, 15 Oct 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;孤獨搖滾 劇場總集篇後篇 也終於看完了🥰~&lt;/p&gt;
&lt;p&gt;這次挑了個平日來電影院，人少的可以XD 除了我以外只有另外一個人在前面坐，讓我體驗一下幾乎算包場的感覺😆&lt;/p&gt;
&lt;p&gt;後篇是從第8集的 Live 沒有播到的第三首歌開始，故事主要是放了9~12集的內容。因為沒有了24分鐘的限制，後篇的波奇和喜多的情感就一氣呵成渲染得很好~ 從最開始喜多是被涼學姐吸引來，接著慢慢開始關注小波奇，為了小波奇也開始努力地練習吉他，最後到結尾文化祭 Live 的救場，波喜多真是太棒太甜啦😍😍😍&lt;/p&gt;
&lt;p&gt;這次後篇依然還是保持和前篇一樣的製作，有新歌、有快速帶過部分劇情，也有在一些地方加了新畫面和配 BGM~ 整體來說非常滿意🥰 只是在結尾的時候加了一小段時間倒帶的鏡頭，難道說會有第二季嗎？如果有那就趕快公布啊！！！我要看孤獨搖滾第二季啊！！！！！🤩&lt;/p&gt;</content:encoded></item><item><title>Astro + Paraglide 實作 i18n 多語系功能</title><link>https://star-note-lucas.me/posts/astro-i18n-paraglide</link><guid isPermaLink="true">https://star-note-lucas.me/posts/astro-i18n-paraglide</guid><description>在 Astro 中搭配 Paraglide 實作多語系功能</description><pubDate>Wed, 09 Oct 2024 00:00:00 GMT</pubDate><content:encoded>&lt;div&gt;&lt;div&gt;Warning&lt;/div&gt;&lt;div&gt;&lt;p&gt;這篇文章是我在研究 Astro i18n 寫的筆記，但內容不建議使用，現在在 Astro 做多語系請參考 &lt;a href=&quot;https://star-note-lucas.me/posts/astro-i18n-astrolicious&quot;&gt;Astro + @astrolicious/i18n 實作 i18n 多語系功能&lt;/a&gt; 來使用。&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.astro.build/en/recipes/i18n/#community-libraries&quot;&gt;Astro 可以做 i18n 的套件&lt;/a&gt;有好幾個，看完介紹後挑了個比較喜歡的 Paraglide 來做，而且還有 VSCode 套件可以搭配使用，做翻譯很舒服。&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#安裝-paraglide&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;安裝 Paraglide&lt;/h2&gt;
&lt;p&gt;初始化 Paraglide 設定：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;npx&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;@inlang/paraglide-js&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;init&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# ❯ Which languages do you want to support?&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# en, zh-TW&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# 中間的都按 Enter&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# Do you want to add the Ninja Github Action for linting translations in CI?&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# No&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;然後修改 &lt;code&gt;project.inlang/settings.json&lt;/code&gt;，把 &lt;code&gt;sourceLanguageTag&lt;/code&gt; 預設語言改成 &lt;code&gt;zh-TW&lt;/code&gt;，&lt;code&gt;languageTags&lt;/code&gt; 內的繁體中文也改成 &lt;code&gt;zh-TW&lt;/code&gt; 格式：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;project.inlang/settings.json&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;sourceLanguageTag&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;zh-TW&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;languageTags&quot;&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;en&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;zh-TW&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;以及 &lt;code&gt;messages/zh-TW.json&lt;/code&gt; 也要改名成 &lt;code&gt;messages/zh-TW.json&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;這裡提到的 &lt;code&gt;messages&lt;/code&gt; 資料夾下就是翻譯文字的內容，每個語言一個 JSON 檔。&lt;/p&gt;
&lt;p&gt;安裝 Paraglide Astro：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;yarn&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;@inlang/paraglide-astro&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;然後註冊套件：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;astro.config.ts&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { defineConfig } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;astro/config&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; paraglide &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;@inlang/paraglide-astro&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;defineConfig&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;site: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;https://my-site.dev&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;integrations: [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;paraglide&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;project: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;./project.inlang&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;outdir: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;./src/paraglide&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;i18n: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;locales: [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{ codes: [&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;zh-TW&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;], path: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;zh-tw&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; },&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;en&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;defaultLocale: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;zh-tw&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;})&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;目前 Astro 支援將語言 Code 和網址路徑分開設定，但如果這樣設定的話，會有不同的設定方式：&lt;/p&gt;

















&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;對應路徑&lt;/th&gt;&lt;th&gt;對應 Astro i18n 的選項&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;messages/zh-TW.json&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;codes&lt;/code&gt;，語言 Code 代號&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;src/pages/zh-tw/...&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;path&lt;/code&gt;，網址路徑&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;還有我平常有使用 EditorConfig，這邊附上設定檔：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;.editorconfig&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;root&lt;/span&gt;&lt;span&gt; = true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;[*]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;charset&lt;/span&gt;&lt;span&gt; = utf-8&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;indent_style&lt;/span&gt;&lt;span&gt; = space&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;indent_size&lt;/span&gt;&lt;span&gt; = 2&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end_of_line&lt;/span&gt;&lt;span&gt; = lf&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;insert_final_newline&lt;/span&gt;&lt;span&gt; = true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;trim_trailing_whitespace&lt;/span&gt;&lt;span&gt; = true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;[messages/*]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;insert_final_newline&lt;/span&gt;&lt;span&gt; = false&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;trim_trailing_whitespace&lt;/span&gt;&lt;span&gt; = false&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;[project.inlang/*]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;insert_final_newline&lt;/span&gt;&lt;span&gt; = false&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;trim_trailing_whitespace&lt;/span&gt;&lt;span&gt; = false&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;&lt;a href=&quot;#安裝-sherlock-vscode-套件&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;安裝 Sherlock VSCode 套件&lt;/h2&gt;
&lt;p&gt;Paraglide 有推出搭配使用的 VSCode 套件 Sherlock，&lt;a href=&quot;https://inlang.com/m/r7kp499g/app-inlang-ideExtension&quot;&gt;點擊安裝 Sherlock VSCode 套件&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;接著增加 VSCode 設定，確保此專案中都可以看到 Sherlock 的提取提示選項，以及關閉不需要的提示訊息：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;.vscode/settings.json&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;editor.lightbulb.enabled&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;onCode&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;sherlock.appRecommendations.ninja.enabled&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;&lt;a href=&quot;#多語系資料夾結構&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;多語系資料夾結構&lt;/h2&gt;
&lt;p&gt;如果沒有特別的需求，通常比較通用的多語系資料夾結構是這樣的，預設語言可以不加上語言前墜：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;src&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;├── pages&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;│   ├── index.astro // 預設語言&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;│   ├── about.astro // 預設語言&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;│   └── en&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;│       ├── index.astro // 英文&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;│       └── about.astro // 英文&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;通常版型都會做在預設語言的頁面裡，需要翻譯的部分可以透過後續篇章介紹的翻譯文字來做，因此其他語言就可以直接引用該頁面：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; Page &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;@/pages/about.astro&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;Page&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;而如果是需要 SSR 的頁面，需要手動加上 &lt;code&gt;export const prerender = false&lt;/code&gt;：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; Page &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;@/pages/posts/[slug].astro&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;export &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;prerender&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;Page&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;當然這是排版完全相同的情形，如果其他語言有較多更改的地方，可以選擇直接複製預設語言的版型出來改比較快。&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#翻譯文字&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;翻譯文字&lt;/h2&gt;
&lt;p&gt;記得啟動一下 Astro 的 Dev Server。&lt;/p&gt;
&lt;p&gt;然後準備好需要翻譯的文字，並在上方引入 Paraglide 多語系的模組：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;as&lt;/span&gt;&lt;span&gt; m &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;../paraglide/messages.js&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;關於我們&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;使用方式很簡單，框選需要翻譯的文字後，點左邊出現的小燈泡按鈕，最下面出現的 &lt;code&gt;Sherlock: Extract Message&lt;/code&gt; 選項，就會建立一調新的翻譯項目：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://star-note-lucas.me/images/2024-10-09-astro-i18n-paraglide-vscode1.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;接著輸入翻譯項目的 key，比如 &lt;code&gt;about_us&lt;/code&gt;，注意 Sherlock 會將標點符號等擋掉或轉換成底線，然後選擇要替換的文字。&lt;/p&gt;
&lt;p&gt;現在就可以看到 &lt;code&gt;m.about_us()&lt;/code&gt; 右側就會出現對應的翻譯文字了~ 如果現在鼠標移動到 &lt;code&gt;m.about_us()&lt;/code&gt; 上，就可以看到其他語言的翻譯文字，如果上面出現 &lt;code&gt;[missing]&lt;/code&gt; 也可以點右邊的閃光來讓 AI 自動產生翻譯文字：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://star-note-lucas.me/images/2024-10-09-astro-i18n-paraglide-vscode2.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;而在左側 Sherlock 的 i18n inspector 也可以查看所有的翻譯文字：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://star-note-lucas.me/images/2024-10-09-astro-i18n-paraglide-vscode3.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#語系設定虛擬模組&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;語系設定虛擬模組&lt;/h2&gt;
&lt;p&gt;在後面要自訂語系的時候，會有缺少一些變數以及需要取得 Astro 的 config 選項，因此這邊我們建一個 Astro Plugin，裡面用 Vite Plugin 產生一個虛擬模組 &lt;code&gt;astro:i18n:config&lt;/code&gt; 來引入需要的 config 選項：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;src/i18n/config-integration.ts&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt; { AstroIntegration, AstroConfig } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;astro&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt; { Plugin } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;vite&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;interface&lt;/span&gt;&lt;span&gt; I18nConfigOptions {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;locales&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Record&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;, { text&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;, code&lt;/span&gt;&lt;span&gt;?:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt; }&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;interface&lt;/span&gt;&lt;span&gt; I18nConfig {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;langCodeMap&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Record&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;langTextMap&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Record&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;interface&lt;/span&gt;&lt;span&gt; I18nConfigContext {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;astroConfig&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AstroConfig&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;i18nConfig&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;I18nConfig&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;VIRTUAL_MODULE_ID&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;astro:i18n:config&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;RESOLVED_VIRTUAL_MODULE_ID&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;\0&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;VIRTUAL_MODULE_ID&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;vitePluginI18nConfig&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;context&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;I18nConfigContext&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Plugin&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;astroConfig&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;i18nConfig&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;context&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;site&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;astroConfig&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;langCodeMap&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;langTextMap&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;i18nConfig&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;i18nInternal&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;site&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;name: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;astro:i18n:config&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;enforce: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;pre&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;resolveId&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (id &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;VIRTUAL_MODULE_ID&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;RESOLVED_VIRTUAL_MODULE_ID&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;load&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (id &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;RESOLVED_VIRTUAL_MODULE_ID&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;export const langCodeMap = &lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;JSON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;stringify&lt;/span&gt;&lt;span&gt;(langCodeMap)&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;export const langTextMap = &lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;JSON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;stringify&lt;/span&gt;&lt;span&gt;(langTextMap)&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;export const i18nInternal = &lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;JSON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;stringify&lt;/span&gt;&lt;span&gt;(i18nInternal)&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;options&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;I18nConfigOptions&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AstroIntegration&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;i18nConfig&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;langCodeMap&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{}&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;langTextMap&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{}&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; as &lt;/span&gt;&lt;span&gt;I18nConfig&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Object&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;entries&lt;/span&gt;&lt;span&gt;(options&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;locales&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;forEach&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;locale&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;text&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;code&lt;/span&gt;&lt;span&gt; }]&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;i18nConfig&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;langCodeMap&lt;/span&gt;&lt;span&gt;[locale] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; code &lt;/span&gt;&lt;span&gt;||&lt;/span&gt;&lt;span&gt; locale&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;i18nConfig&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;langTextMap&lt;/span&gt;&lt;span&gt;[locale] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; text&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;})&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;name: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;i18n-config&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;hooks: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;astro:config:setup&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;{ config: &lt;/span&gt;&lt;span&gt;astroConfig&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;updateConfig&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;updateConfig&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;vite: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;plugins: [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;              &lt;/span&gt;&lt;span&gt;vitePluginI18nConfig&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;astroConfig&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;i18nConfig&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;})&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;})&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;然後為虛擬模組 &lt;code&gt;astro:i18n:config&lt;/code&gt; 定義型別設定：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;src/i18n/client.d.ts&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;declare&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;module&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;astro:i18n:config&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt; AstroConfig &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;astro&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;span&gt;AstroConfig&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt; I18nConfig &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;./config-integration&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;span&gt;I18nConfig&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;export &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;langCodeMap&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;I18nConfig&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;langCodeMap&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;export &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;langTextMap&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;I18nConfig&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;langTextMap&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;export &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;i18nInternal&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;site&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AstroConfig&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;site&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;最後在 &lt;code&gt;astro.config.ts&lt;/code&gt; 引入套件即可，這邊需要注意，&lt;code&gt;locales&lt;/code&gt; 的 key 需要和 &lt;code&gt;project.inlang/settings.json&lt;/code&gt; 的 &lt;code&gt;languageTags&lt;/code&gt; 完全對應，包含大小寫都要一致：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;astro.config.ts&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { defineConfig } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;astro/config&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; i18nConfig &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;./src/i18n/config-integration&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;defineConfig&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;integrations: [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;// ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;i18nConfig&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;locales: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;zh-TW&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;: { text: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;繁體中文&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, code: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;zh-Hant-TW&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; },&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;en: { text: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;English&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; },&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;})&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;之後只要引入 &lt;code&gt;astro:i18n:config&lt;/code&gt; 就可以使用了：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { ... } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;astro:i18n:config&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;&lt;a href=&quot;#html-lang-code&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;HTML Lang Code&lt;/h2&gt;
&lt;p&gt;Paraglide 安裝完成後，需要先在 Layout 裡新增語言的 &lt;code&gt;lang&lt;/code&gt; 等 HTML 屬性。但剛才設定的 &lt;code&gt;zh-TW&lt;/code&gt; 其實並不是正規的 ISO 語言格式，繁體中文其實要使用 &lt;code&gt;zh-Hant-TW&lt;/code&gt;，因此這邊我們自訂一個 &lt;code&gt;htmlLangCode()&lt;/code&gt; 函數來做轉換：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;src/i18n/html.ts&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { languageTag } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;../paraglide/runtime.js&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { langCodeMap } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;astro:i18n:config&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;htmlLangCode&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; langCodeMap[&lt;/span&gt;&lt;span&gt;languageTag&lt;/span&gt;&lt;span&gt;()] &lt;/span&gt;&lt;span&gt;||&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;languageTag&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;然後就可以在 &lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt; 中增加正規的 ISO 語言格式了：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;src/layouts/Layout.astro&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { htmlLangCode } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;@/i18n/html&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;!&lt;/span&gt;&lt;span&gt;doctype&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;lang&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;htmlLangCode&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;dir&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;Astro&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;locals&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;paraglide&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;dir&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;slot&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;&lt;a href=&quot;#多語系輔助函數&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;多語系輔助函數&lt;/h2&gt;
&lt;p&gt;目前跟多語系相關的輔助函數有：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;astro:i18n&lt;/code&gt;：Astro 核心的產生多語系網址輔助函數&lt;/li&gt;
&lt;li&gt;&lt;code&gt;astro:i18n:config&lt;/code&gt;：自訂的 Astro 多語系輔助函數和變數&lt;/li&gt;
&lt;li&gt;&lt;code&gt;paraglide/runtime.js&lt;/code&gt;：Paraglide 的多語系輔助函數&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;但還有缺一些東西，就自己寫吧，開一個 &lt;code&gt;src/i18n/linking.ts&lt;/code&gt;：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;src/i18n/linking.ts&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { getAbsoluteLocaleUrl, getRelativeLocaleUrl } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;astro:i18n&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { i18nInternal } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;astro:i18n:config&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { availableLanguageTags, languageTag, sourceLanguageTag } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;../paraglide/runtime.js&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt; { AvailableLanguageTag } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;../paraglide/runtime.js&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt; AbsolutePathname &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;interface&lt;/span&gt;&lt;span&gt; AlternateLocale {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;locale&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AvailableLanguageTag&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;localizePathname&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;pathname&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AbsolutePathname&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;locale&lt;/span&gt;&lt;span&gt;?:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AvailableLanguageTag&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;locale &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; locale &lt;/span&gt;&lt;span&gt;||&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;languageTag&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (locale &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; sourceLanguageTag) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; pathname&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;getRelativeLocaleUrl&lt;/span&gt;&lt;span&gt;(locale&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; pathname)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;localizeUrl&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;pathname&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AbsolutePathname&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;locale&lt;/span&gt;&lt;span&gt;?:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AvailableLanguageTag&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;locale &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; locale &lt;/span&gt;&lt;span&gt;||&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;languageTag&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (locale &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; sourceLanguageTag) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;site&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;i18nInternal&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;site&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;||&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&apos;&lt;/span&gt;&lt;span&gt;}${&lt;/span&gt;&lt;span&gt;pathname&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;getAbsoluteLocaleUrl&lt;/span&gt;&lt;span&gt;(locale&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; pathname)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;unlocalizedPathname&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;pathname&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;homeLocalePath&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;getRelativeLocaleUrl&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;languageTag&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;pathname&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;replace&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;homeLocalePath&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;homeLocalePath&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;endsWith&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;||&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;as&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AbsolutePathname&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;/**&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* Get the available alternate locales for generate `&amp;lt;link rel=&quot;alternate&quot;&amp;gt;` tags.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;availableAlternateLocales&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;requestPathname&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AlternateLocale&lt;/span&gt;&lt;span&gt;[] {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pathname&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;unlocalizedPathname&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;requestPathname&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; availableLanguageTags&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;filter&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;locale&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; locale &lt;/span&gt;&lt;span&gt;!==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;languageTag&lt;/span&gt;&lt;span&gt;())&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;map&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;locale&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; ({&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;locale&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;href: &lt;/span&gt;&lt;span&gt;localizeUrl&lt;/span&gt;&lt;span&gt;(pathname&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; locale)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;最後手動把專案內所有的網址，都替換成自動加上當前語系的網址，比如 &lt;code&gt;/about/&lt;/code&gt; 替換成 &lt;code&gt;localizePathname(&apos;/about/&apos;)&lt;/code&gt;：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { languageTag } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;../paraglide/runtime.js&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { localizePathname } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;../i18n/linking&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;localizePathname&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;/about/&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;m&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;about&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;&lt;a href=&quot;#語系選單&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;語系選單&lt;/h2&gt;
&lt;p&gt;在 &lt;code&gt;src/i18n/ui.ts&lt;/code&gt; 裡寫個產生語系選單內容的輔助函數：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;src/i18n/ui.ts&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { unlocalizedPathname, localizePathname } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;./linking&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { languageTag } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;../paraglide/runtime.js&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt; { AvailableLanguageTag } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;../paraglide/runtime.js&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { langTextMap } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;astro:i18n:config&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;interface&lt;/span&gt;&lt;span&gt; LanguageSelectorItems {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;current&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;items&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;label&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;active&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;boolean&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}[]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;languageSelectorItems&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;requestPathname&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;LanguageSelectorItems&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pathname&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;unlocalizedPathname&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;requestPathname&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;current: langTextMap[&lt;/span&gt;&lt;span&gt;languageTag&lt;/span&gt;&lt;span&gt;()]&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;items: (Object&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;keys&lt;/span&gt;&lt;span&gt;(langTextMap) &lt;/span&gt;&lt;span&gt;as&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AvailableLanguageTag&lt;/span&gt;&lt;span&gt;[])&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;map&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;locale&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; ({&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;href: &lt;/span&gt;&lt;span&gt;localizePathname&lt;/span&gt;&lt;span&gt;(pathname&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; locale)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;label: langTextMap[locale]&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;active: locale &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;languageTag&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}))&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;在 Layout 中做一個下拉選單：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;src/layouts/Layout.astro&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { languageSelectorItems } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;../i18n/ui&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pathname&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;URL&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Astro&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;request&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;url&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;pathname&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;langSelector&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;languageSelectorItems&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;pathname&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;fas fa-globe&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;langSelector&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;current&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;ul&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;dropdown&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;langSelector&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;items&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;map&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;{ &lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;label&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;active&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;active&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;current-menu-item&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;label&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;ul&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;&lt;a href=&quot;#alternate-其他語系網址&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;Alternate 其他語系網址&lt;/h2&gt;
&lt;p&gt;使用上面已經寫好的 &lt;code&gt;availableAlternateLocales()&lt;/code&gt; 函數來產生 Alternate 標籤：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;src/layouts/Layout.astro&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { availableAlternateLocales } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;../../i18n/linking&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;head&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;availableAlternateLocales&lt;/span&gt;&lt;span&gt;(Astro&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;url&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;pathname&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;map&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;{ &lt;/span&gt;&lt;span&gt;locale&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;link&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rel&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;alternate&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;hreflang&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;locale&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;head&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;&lt;a href=&quot;#總結&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;總結&lt;/h2&gt;
&lt;p&gt;先說缺點好了，Astro 做多語系雖然有提供了基本的功能，但既不完整，文檔說明並不完全清楚，生態也不夠強大。不過我摸出這套流程出來，目前我自己實作下來算很滿意的，起碼來說我可以比較自由的去拼裝出我要的功能，而不會被一些預設的東西給綁住，這是我最喜歡的部分~&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#參考資料&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;參考資料&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.astro.build/en/guides/internationalization/&quot;&gt;Internationalization (i18n) Routing | Astro Docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.astro.build/en/recipes/i18n/&quot;&gt;Add i18n features | Astro Docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://inlang.com/m/iljlwzfs/paraglide-astro-i18n&quot;&gt;Paraglide-Astro&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://inlang.com/m/r7kp499g/app-inlang-ideExtension&quot;&gt;Sherlock&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>Astro 初見心得和筆記</title><link>https://star-note-lucas.me/posts/astro</link><guid isPermaLink="true">https://star-note-lucas.me/posts/astro</guid><description>記錄一下使用 Astro 的小筆記和心得</description><pubDate>Tue, 08 Oct 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;其實在幾年前就聽過 Astro 的大名，只是當時一方面覺得寫法很 React，另一方面覺得這專案還太年輕，所以就沒有打算去碰這個工具。直到最近的案子中，需要參考現成的模板來做前端，而這些模板清一色都是 Bootstrap + jQuery，但前端需要做 SSR，原本用了 Nuxt 來轉換，只是轉了一個案子之後我就快起笑了…，最後證明不要把 jQuery 專案塞到 Nuxt 裡面。&lt;/p&gt;
&lt;p&gt;但沒有 Nuxt 的話要怎麼做 SSR 呢？通常這種時候可以搭配 PHP 之類的後端語言，只是就做不到前後端分離了，所以 Pass。因此我最後選到了腦海記憶深處的 Astro 這個 SSG Framework 上，可以 SSG 模式產生出 HTML，和使用 SSR 模式串後端 API，可以直接載入模板的 jQuery 套件們，這不就是完美符合我的需求的框架嘛~ 因此最後還是投降了哈哈哈XD&lt;/p&gt;
&lt;p&gt;在實際用了之後，發現說有時候不要對未知的事物抱有太大成見，雖然我沒有在寫 React，不過在 Astro 裡寫 JSX 還是滿舒服的。而且在 &lt;code&gt;.astro&lt;/code&gt; 檔上方寫 Server Side 的 Block 也可以很好地控制每個頁面的 Request，以及 Islands 的設計真的太香~ 分區塊載入 JS 可以讓初次載入的使用者體驗更友好，也可以好好地重新思考 JS 是否是必須的這個問題上。以上這些都是實際用了之後才知道的事情。&lt;/p&gt;
&lt;p&gt;依目前的體驗來看，我應該會繼續多嘗試 Astro 來做專案~ 如果你現在正在學習使用 Astro，希望本篇可以幫到你~&lt;/p&gt;
&lt;p&gt;還有，這篇只是一個 Astro 的學習筆記，建議可以看看官網的&lt;a href=&quot;https://docs.astro.build/&quot;&gt;文檔&lt;/a&gt;和&lt;a href=&quot;https://docs.astro.build/en/tutorial/0-introduction/&quot;&gt;教學&lt;/a&gt;從頭了解 Astro 的使用方式再來看會比較好喔~&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#建立-astro-專案&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;建立 Astro 專案&lt;/h2&gt;
&lt;p&gt;初始化可以用 Astro 官方的 CLI 來建立。在建立的過程中，可以輸入專案資料夾名稱，選擇使用的 template，是否要使用 TypeScript，是否要初始化 Git 版控等等：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# 使用 npm&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;npm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;astro@latest&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# 使用 yarn&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;yarn&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;astro&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;不過試用完之後，我還是開了一個符合我使用習慣的 &lt;a href=&quot;https://github.com/ycs77/astro-starter&quot;&gt;Astro Starter&lt;/a&gt; 了XD，畢竟每個人習慣還是不大一樣。可以用以下指令安裝：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;npx&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;degit&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ycs77/astro-starter&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;my-project&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;&lt;a href=&quot;#astro-元件&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;Astro 元件&lt;/h2&gt;
&lt;p&gt;每個 &lt;code&gt;.astro&lt;/code&gt; 檔都是一個元件 Component，內部使用 JSX 的寫法，預設約定原件是放在 &lt;code&gt;components&lt;/code&gt;，Layout 佈局是放在 &lt;code&gt;layouts&lt;/code&gt;，當然其實是都可以改的。&lt;/p&gt;
&lt;p&gt;這裡先拿 Layout 元件做示範：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;src/layouts/Layout.astro&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;interface&lt;/span&gt;&lt;span&gt; Props {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Astro&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;props&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;!&lt;/span&gt;&lt;span&gt;doctype&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;lang&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;zh-Hant-TW&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;head&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;meta&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;charset&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;UTF-8&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;meta&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;viewport&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;content&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;width=device-width&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;link&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rel&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;icon&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;image/svg+xml&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;/favicon.svg&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;meta&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;generator&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;content&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;Astro&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;generator&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;slot&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;head&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;slot&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;head&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;slot&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;然後在頁面中引入使用：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;src/pages/index.astro&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; Layout &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;../layouts/Layout.astro&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;Layout&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Astro&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;Astro&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;Layout&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;或是可以使用 slot 來注入頁面的 meta 標籤等：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;src/pages/index.astro&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;Layout&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Astro&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;Fragment&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;slot&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;head&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;meta&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;content&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Astro description...&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;Fragment&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;Astro&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;Layout&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;&lt;a href=&quot;#astro-output-輸出模式&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;Astro output 輸出模式&lt;/h2&gt;
&lt;p&gt;Astro 的設定檔 &lt;code&gt;astro.config.ts&lt;/code&gt; 裡面有個設定叫 &lt;code&gt;output&lt;/code&gt;，&lt;code&gt;output&lt;/code&gt; 的兩個選項 &lt;code&gt;static&lt;/code&gt; 和 &lt;code&gt;server&lt;/code&gt; 都是可以在 SSR 模式下執行的，兩個本質都是可以跑 SSR 或 SSG 的混合模式，只是優先渲染方式不同。&lt;/p&gt;
&lt;p&gt;預設值是 &lt;code&gt;static&lt;/code&gt;，默認預先渲染 HTML，如果內容是靜態頁面就都是用這個，而如果某個頁面需要串 API 而改成 SSR 模式，只要在頁面加上 &lt;code&gt;export const prerender = false;&lt;/code&gt; 就行了。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;server&lt;/code&gt; 則和 &lt;code&gt;static&lt;/code&gt; 相反，默認 SSR 模式，不預先渲染，通常應用於幾乎每個頁面都要串 API 的情境下使用。&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;code&gt;static&lt;/code&gt;&lt;/td&gt;&lt;td&gt;開啟 &lt;code&gt;prerender&lt;/code&gt; 預先渲染&lt;/td&gt;&lt;td&gt;&lt;code&gt;export const prerender = false;&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;server&lt;/code&gt;&lt;/td&gt;&lt;td&gt;關閉 &lt;code&gt;prerender&lt;/code&gt; 預先渲染&lt;/td&gt;&lt;td&gt;&lt;code&gt;export const prerender = true;&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h2&gt;&lt;a href=&quot;#自訂輸出頁面回應碼&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;自訂輸出頁面回應碼&lt;/h2&gt;
&lt;p&gt;如果在輸出像是文章頁面時，會可能出現打的 API 返回是 404 之類的，但如果前端不做處理，就只會看到錯誤訊息，但我們只是想要讓它單純的輸出 404 頁面而已的說…，這裡就可以針對 404 的 API 直接輸出一個 404 的 Response 就行了。在 &lt;code&gt;posts/[slug].astro&lt;/code&gt; 裡面對錯誤進行處理：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;src/pages/posts/[slug].astro&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;response&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; await &lt;/span&gt;&lt;span&gt;fetch&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;response&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;status&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;404&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Response&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;, {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;status: &lt;/span&gt;&lt;span&gt;404&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;statusText: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Not found&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;})&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; await &lt;/span&gt;&lt;span&gt;response&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;json&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;當然也可以回傳 500 伺服器錯誤：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;src/pages/posts/[slug].astro&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;try&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;response&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; await &lt;/span&gt;&lt;span&gt;fetch&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; await &lt;/span&gt;&lt;span&gt;response&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;json&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;} &lt;/span&gt;&lt;span&gt;catch&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;error&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Response&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;, {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;status: &lt;/span&gt;&lt;span&gt;500&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;statusText: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Server error&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;})&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;如果每個頁面都有串 API 的話，我們可以新增一個 Middleware &lt;code&gt;src/middleware.ts&lt;/code&gt; 來統一處理 API 的回傳錯誤。這裡就可以 catch 自己串的 API 的錯誤，然後根據 Response 狀態來回傳 404 頁面：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;src/middleware.ts&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { defineMiddleware } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;astro:middleware&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;export &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;onRequest&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;defineMiddleware&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;async &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;_context&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;next&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;try &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return await &lt;/span&gt;&lt;span&gt;next&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; catch &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;// Handle 404 errors&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt; instanceof &lt;/span&gt;&lt;span&gt;ApiRequestError&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Response&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;status&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;404&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;statusText&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Not found&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;throw &lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;&lt;a href=&quot;#import-路徑別名&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;import 路徑別名&lt;/h2&gt;
&lt;p&gt;Astro 的教學中，預設都是使用相對定位來引入專案中的其他模組，但複製元件內容時總是會有定位錯誤的問題，這邊在 &lt;code&gt;tsconfig.json&lt;/code&gt; 加個 &lt;code&gt;paths&lt;/code&gt; 就解決了：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;tsconfig.json&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;extends&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;astro/tsconfigs/strict&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;compilerOptions&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;baseUrl&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;paths&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;@/*&quot;&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;./src/*&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;然後就可以在路徑開頭用 &lt;code&gt;@/..&lt;/code&gt;，以專案根目錄為絕對定位，寫引入的模組路徑了：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; Layout &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;@/layouts/Layout.astro&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;&lt;a href=&quot;#astro-check-輸出超長的-hint-提示&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;Astro check 輸出超長的 hint 提示&lt;/h2&gt;
&lt;p&gt;這題直接上結論，把 &lt;code&gt;public&lt;/code&gt; 和 &lt;code&gt;dist&lt;/code&gt; 排除掉 TypeScript 的檢查即可：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;tsconfig.json&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;exclude&quot;&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;dist&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;&lt;a href=&quot;#自訂網站常數設定&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;自訂網站常數設定&lt;/h2&gt;
&lt;p&gt;Astro 並沒有自訂設定檔的機制，不過可以自己建立一個檔案來當作設定檔，比如這裡新增 &lt;code&gt;src/site.config.js&lt;/code&gt;：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;src/site.config.js&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;email: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;astro@example.com&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;phone: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;02-1111-2222&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;address: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;台北市中山區XX路XX樓&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;真的是相當的自由~&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#netlify-或-vercel&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;Netlify 或 Vercel？&lt;/h2&gt;
&lt;p&gt;通常情況下是看習慣，畢竟 Astro 兩個平台都有支援，只是如果是靜態產生 (&lt;code&gt;static&lt;/code&gt; 模式) 的話就會有差，差在尾部斜線的處理：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Vercel 靜態產生網站支援強制移除網址尾部斜線，比如 &lt;code&gt;/about/&lt;/code&gt; 會強制跳轉到 &lt;code&gt;/about&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;而 Netlify 靜態產生網站移除尾部斜線後依然可以瀏覽，但不會且不支援強制跳轉 (只能強制尾部加斜線)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;事先對於網址的 style 選擇相當重要，要不然部署後可能會發現瀏覽網址有問題。&lt;/p&gt;
&lt;p&gt;通常情況下加斜線會比較多平台支援，因為只需在資料夾內部加一個 &lt;code&gt;index.html&lt;/code&gt;，以資料夾的方式來達到美化網址的方式，不需要伺服器有多餘的設定。但如果對網址有潔癖，連尾隨的斜線都不想要的話，這邊建議可以選用 Vercel。&lt;/p&gt;
&lt;p&gt;而在 Astro 中尾部斜線的設定，可以參考 &lt;a href=&quot;https://docs.astro.build/en/reference/configuration-reference/#trailingslash&quot;&gt;trailingSlash&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;當然如果是 &lt;code&gt;server&lt;/code&gt; 模式的話，兩個平台都可以自由設定網址的尾部斜線行為。以下是一個我比較喜歡的設定，在 &lt;code&gt;server&lt;/code&gt; 模式下不使用尾部斜線的設定：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;astro.config.ts&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;defineConfig&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;output: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;server&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;trailingSlash: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;never&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;build: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;format: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;file&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;})&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;最後這邊附上我在兩個平台部署的專案所使用的設定檔：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;netlify.toml&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;[build]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;command&lt;/span&gt;&lt;span&gt; = &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;yarn build&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;publish&lt;/span&gt;&lt;span&gt; = &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;dist&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;[build.environment]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;NODE_VERSION&lt;/span&gt;&lt;span&gt; = &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;20&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;vercel.json&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;cleanUrls&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;trailingSlash&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;github&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;silent&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;

&lt;h2&gt;&lt;a href=&quot;#結語&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;結語&lt;/h2&gt;
&lt;p&gt;強烈建議透過官網的&lt;a href=&quot;https://docs.astro.build/&quot;&gt;文檔&lt;/a&gt;和&lt;a href=&quot;https://docs.astro.build/en/tutorial/0-introduction/&quot;&gt;教學&lt;/a&gt;來學 Astro 的使用方式~&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#參考資料&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;參考資料&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.astro.build/&quot;&gt;Astro Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>Laravel 在 GitHub Actions 上用 MySQL 跑測試</title><link>https://star-note-lucas.me/posts/laravel-runs-tests-with-mysql-on-github-actions</link><guid isPermaLink="true">https://star-note-lucas.me/posts/laravel-runs-tests-with-mysql-on-github-actions</guid><description>GitHub Actions 用 MySQL 跑測試的 yaml 範例</description><pubDate>Sat, 21 Sep 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Laravel 預設測試用的資料庫是 SQLite，可是正常線上都是用 MySQL，在跑測試的時候常常會因為兩邊不一致。這時候會建議可以用 MySQL 來跑測試，讓測試和正式兩邊可以保持一致，才不會有測試明明過了，但線上卻出現不一樣行為的狀況，而且 MySQL 測試執行速度更快~ (參考 &lt;a href=&quot;https://madewithlove.com/blog/using-mysql-for-testing-on-laravel/&quot;&gt;Using MySQL for Testing (on Laravel)&lt;/a&gt;)&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#本地測試設定&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;本地測試設定&lt;/h2&gt;
&lt;p&gt;本地可以先複製好測試用的 &lt;code&gt;.env.testing&lt;/code&gt;：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;cp&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;.env.example&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;.env.testing&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;和設定測試用的 MySQL 帳密相關資料：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;.env.testing&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;DB_DATABASE&lt;/span&gt;&lt;span&gt;=laravel_test&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;DB_USERNAME&lt;/span&gt;&lt;span&gt;=root&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;DB_PASSWORD&lt;/span&gt;&lt;span&gt;=password&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;接著把 &lt;code&gt;.env.testing&lt;/code&gt; 加進 &lt;code&gt;.gitignore&lt;/code&gt; 裡：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;.env&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;.env.backup&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;.env.production&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;.env.testing&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;最後產生密鑰和執行測試：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;php&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;artisan&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;key:generate&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--env=testing&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;./vendor/bin/pest&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;&lt;a href=&quot;#設定-github-actions&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;設定 GitHub Actions&lt;/h2&gt;
&lt;p&gt;GitHub Actions 跑測試的範例 yaml：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;.github/workflows/tests.yml&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Tests&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;on&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;push&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;branches&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;span&gt;main&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;jobs&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;tests&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Tests&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;runs-on&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;ubuntu-latest&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;env&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;DB_DATABASE&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;laravel&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;DB_USERNAME&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;root&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;DB_PASSWORD&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;password&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;services&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;mysql&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;image&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;mysql:5.7&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;env&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;MYSQL_DATABASE&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;laravel&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;MYSQL_ALLOW_EMPTY_PASSWORD&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;MYSQL_ROOT_PASSWORD&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;password&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;ports&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;3306/tcp&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;options&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;--health-cmd=&quot;mysqladmin ping&quot; --health-interval=10s --health-timeout=5s --health-retries=3&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;steps&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Checkout code&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;uses&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;actions/checkout@v4&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Setup PHP&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;uses&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;shivammathur/setup-php@v2&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;with&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;php-version&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;8.3&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;extensions&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, mysql, mysqli, pdo_mysql, bcmath, intl, gd, exif, iconv, imagick, fileinfo&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;coverage&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;none&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Setup problem matchers&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;run&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;echo &quot;::add-matcher::${{ runner.tool_cache }}/php.json&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;echo &quot;::add-matcher::${{ runner.tool_cache }}/phpunit.json&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Install dependencies&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;run&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;composer install --prefer-dist --no-interaction&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Setup environment key&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;run&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;php -r &quot;file_exists(&apos;.env.testing&apos;) || copy(&apos;.env.example&apos;, &apos;.env.testing&apos;);&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;php artisan key:generate --env=testing&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Run tests&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;run&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;vendor/bin/pest&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;env&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;DB_PORT&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;${{ job.services.mysql.ports[3306] }}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;&lt;a href=&quot;#參考資料&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;參考資料&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://madewithlove.com/blog/using-mysql-for-testing-on-laravel/&quot;&gt;Using MySQL for Testing (on Laravel)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://gist.github.com/GussRw/b1808cecc8e3d517de1cfefe70061d12&quot;&gt;GitHub Action for Laravel with MySQL - GitHub Gist&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.redfern.dev/articles/laravel-testing-using-github-actions-with-mysql&quot;&gt;Laravel Testing Using GitHub Actions With MYSQL&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>解決 Laravel 回傳 Etag Header 被 Nginx 攔截的問題</title><link>https://star-note-lucas.me/posts/solve-response-etag-from-laravel-intercepted-by-nginx</link><guid isPermaLink="true">https://star-note-lucas.me/posts/solve-response-etag-from-laravel-intercepted-by-nginx</guid><description>在 Laravel 處理 `Etag` 和 304，並解決 Etag Header 被 Nginx 攔截的問題</description><pubDate>Fri, 20 Sep 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;HTTP Cache 裡面的其中一個機制是 &lt;code&gt;Etag&lt;/code&gt;，可以回傳當前資源的唯一 Hash 值，然後在之後的請求可以比對此 &lt;code&gt;Etag&lt;/code&gt; 值有沒有變化，沒變化就直接回傳 304，有改變才會重新下載新的資源。通常如果是靜態資源可以直接交給 Nginx 處理就行了，但剛好我有個需求需要在 Laravel 中處理後才回傳出去，因此就需要在 Laravel 處理 &lt;code&gt;Etag&lt;/code&gt; 和 304 了。&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#etag-和-if-none-match&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;Etag 和 If-None-Match&lt;/h2&gt;
&lt;p&gt;先上一個基本的 Controller，把檔案內容讀出來之後用 &lt;code&gt;md5()&lt;/code&gt; Hash 過傳給 &lt;code&gt;Etag&lt;/code&gt; Header。然後在第二次請求的時候，會附上 &lt;code&gt;If-None-Match&lt;/code&gt; Header，這時就可以比對 Hash 值是否一樣：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;app/Http/Controllers/AssetController.php&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;?&lt;/span&gt;&lt;span&gt;php&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;namespace&lt;/span&gt;&lt;span&gt; App\Http\Controllers;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AssetController&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;extends&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Controller&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;__invoke&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Request&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;$request&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;$cacheControl&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;no-cache&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;$content&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;$etag&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;md5&lt;/span&gt;&lt;span&gt;($&lt;/span&gt;&lt;span&gt;content&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;$this-&amp;gt;&lt;/span&gt;&lt;span&gt;matchesCache&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$etag&lt;/span&gt;&lt;span&gt;)) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;response&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;304&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Cache-Control&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt;cacheControl&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;]);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;$headers&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Content-Type&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;$contentType&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Cache-Control&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;$cacheControl&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Etag&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;$etag&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;];&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;response&lt;/span&gt;&lt;span&gt;($&lt;/span&gt;&lt;span&gt;content&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;200&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt;headers&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;protected&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;matchesCache&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$etag&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;/** &lt;/span&gt;&lt;span&gt;@var&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt; */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;$ifNoneMatch&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;request&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;-&amp;gt;&lt;/span&gt;&lt;span&gt;header&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;If-None-Match&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;$ifNoneMatch&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;!==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;$ifNoneMatch&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;$etag&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;在本地還可以正常跑，但一搬到線上就沒有效果了。經過一番調查，是 Nginx 開啟 gzip 後就會過濾掉 &lt;code&gt;etag&lt;/code&gt;，可是不完全對。它會刪掉不符合規則的強 Etag，而不會管弱 Etag。&lt;/p&gt;
&lt;p&gt;簡單來說在這裡我們只要轉成弱 Etag 就行了，弱 Etag 的格式是 &lt;code&gt;W/&quot;etag內容&quot;&lt;/code&gt;，把 &lt;code&gt;etag內容&lt;/code&gt; 前後加上 &lt;code&gt;W/&quot;...&quot;&lt;/code&gt; 變成 &lt;code&gt;W/&quot;etag內容&quot;&lt;/code&gt; 就可以了：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;$etag&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;md5&lt;/span&gt;&lt;span&gt;($&lt;/span&gt;&lt;span&gt;content&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// 原本的 Etag: abc123&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;$etag&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;W/&quot;&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;md5&lt;/span&gt;&lt;span&gt;($&lt;/span&gt;&lt;span&gt;content&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// 新的弱 Etag: W/&quot;abc123&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;&lt;a href=&quot;#參考資料&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;參考資料&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.huli.tw/2017/08/27/http-cache/&quot;&gt;循序漸進理解 HTTP Cache 機制 - Huli’s blog&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>重新編譯 Filament 的 CSS</title><link>https://star-note-lucas.me/posts/filament-css</link><guid isPermaLink="true">https://star-note-lucas.me/posts/filament-css</guid><description>在客製化 Filament 後台介面後，重新編譯完整的 Tailwind CSS</description><pubDate>Tue, 10 Sep 2024 00:00:00 GMT</pubDate><content:encoded>&lt;div&gt;&lt;div&gt;Tip&lt;/div&gt;&lt;div&gt;&lt;p&gt;或許是當時做這個功能的時候，沒有詳細看完說明文件，後來才知道其實 Filament 早就有這個功能了…😭&lt;/p&gt;&lt;p&gt;在 Filament 文件中的 &lt;a href=&quot;https://filamentphp.com/docs/3.x/panels/themes#creating-a-custom-theme&quot;&gt;自訂主題 CSS&lt;/a&gt; 就有紀錄了，而且還更簡單啊！！！不過好歹也還是研究出了我自己的方案，就留下來當作參考用的吧！或許不知道什麼時候會使用到~&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;基本上做後台時會時不時出現各種奇形怪狀的需求，Filament 預設的程式會無法負荷，在這種時候只能自己來寫 Blade 的 View。只是這時會冒出個問題…&lt;/p&gt;
&lt;p&gt;「CSS 要怎麼辦？我用了新的 Tailwind 的 class 是不是就不會出現了？」&lt;/p&gt;
&lt;p&gt;因此在客製化 Filament 後台介面後，我們需要重新編譯完整的、包含 Filament 和我們客製化後的完整的 CSS。&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#編譯-tailwind-css&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;編譯 Tailwind CSS&lt;/h2&gt;
&lt;p&gt;要客製化 CSS 的話需要一個基礎的資料夾結構，裡面都放置跟客製 Filament 前端相關的東西：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;filament/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;├── css/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;|  └── app.css&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;├── assets.sh&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;└── tailwind.config.js&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;css/app.css&lt;/code&gt; 裡面依然還是 Tailwind 預設的樣板：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;css/app.css&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;tailwind&lt;/span&gt;&lt;span&gt; base;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;tailwind&lt;/span&gt;&lt;span&gt; components;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;tailwind&lt;/span&gt;&lt;span&gt; utilities;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;然後是 Tailwind 的設定檔 &lt;code&gt;tailwind.config.js&lt;/code&gt;，這樣編譯完之後可以包含舊的和新的 CSS：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;tailwind.config.js&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; preset &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;../vendor/filament/filament/tailwind.config.preset&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;presets: [&lt;/span&gt;&lt;span&gt;preset&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;content: [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;./app/Filament/**/*.php&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;./resources/views/filament/**/*.blade.php&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;./vendor/filament/**/*.blade.php&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;最後在 &lt;code&gt;package.json&lt;/code&gt; 中加上編譯 Filament CSS 的腳本，輸出的 CSS 會直接覆蓋掉 Filament publish 出來的 CSS，這樣就達到了增加自己客製的 Tailwind class 的目的了：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;package.json&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;scripts&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;dev&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;vite&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;dev:filament-css&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;npx tailwindcss -c ./filament/tailwind.config.js -i ./filament/css/app.css -o ./public/css/filament/filament/app.css --watch&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;build&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;vite build&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;build:filament-css&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;npx tailwindcss -c ./filament/tailwind.config.js -i ./filament/css/app.css -o ./public/css/filament/filament/app.css --minify&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;&lt;a href=&quot;#自訂-css-覆蓋問題&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;自訂 CSS 覆蓋問題&lt;/h2&gt;
&lt;p&gt;但這時會有個問題，在每次呼叫完 &lt;code&gt;composer dump-autoload&lt;/code&gt; 後 Filament 都會重新覆蓋掉 CSS 等資產，這樣我們自訂的內容就又不見了，於是我們需要修改一下編譯的腳本：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;package.json&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;scripts&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;build:filament-css&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;npx tailwindcss -c ./filament/tailwind.config.js -i ./filament/css/app.css -o ./filament/dist/app.css --minify &amp;amp;&amp;amp; bash ./filament/assets.sh&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;我們先讓 CSS 輸出到 &lt;code&gt;filament/dist/app.css&lt;/code&gt;，而且&lt;strong&gt;不要&lt;/strong&gt;加到 &lt;code&gt;.gitignore&lt;/code&gt; 裡面，然後資料夾結構變成這樣：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;filament/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;├── css/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;|  └── app.css&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;├── dist/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;|  └── app.css&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;├── assets.sh&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;└── tailwind.config.js&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;然後我們再加個腳本 &lt;code&gt;assets.sh&lt;/code&gt; 來發布回自訂版的 CSS：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; [ &lt;/span&gt;&lt;span&gt;-f&lt;/span&gt;&lt;span&gt; filament/dist/app.css ]; &lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;cp&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;filament/dist/app.css&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;public/css/filament/filament/app.css&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Published assets for Filament successfully.&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;fi&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;然後我們可以在 &lt;code&gt;composer.json&lt;/code&gt; 裡面找到 &lt;code&gt;post-autoload-dump&lt;/code&gt;，發現有 &lt;code&gt;@php artisan filament:upgrade&lt;/code&gt;，只要我們在後面加上 &lt;code&gt;assets.sh&lt;/code&gt; 就可以再次覆蓋回來了：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;package.json&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;scripts&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;post-autoload-dump&quot;&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Illuminate&lt;/span&gt;&lt;span&gt;\\&lt;/span&gt;&lt;span&gt;Foundation&lt;/span&gt;&lt;span&gt;\\&lt;/span&gt;&lt;span&gt;ComposerScripts::postAutoloadDump&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;@php artisan package:discover --ansi&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;@php artisan filament:upgrade&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;bash filament/assets.sh&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;// 加上這行&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;開發用 &lt;code&gt;dev:filament-css&lt;/code&gt;，上線用 &lt;code&gt;build:filament-css&lt;/code&gt;：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# dev&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;npm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;run&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;dev:filament-css&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# production&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;npm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;run&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;build:filament-css&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;</content:encoded></item><item><title>在 Laravel Forge 配置的服務器上使用 vsftpd 設定 FTP 伺服器</title><link>https://star-note-lucas.me/posts/laravel-forge-vsftpd</link><guid isPermaLink="true">https://star-note-lucas.me/posts/laravel-forge-vsftpd</guid><description>在 Laravel Forge 配置的服務器上，使用 vsftpd 來設定 FTP 上傳檔案的方式</description><pubDate>Sat, 10 Aug 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;雖然 Laravel Forge 預設會設定好 SSH 的連線方式，也可以快速設定好 SFTP，但內建的 &lt;code&gt;sftp-server&lt;/code&gt; 要設定 &lt;code&gt;chroot&lt;/code&gt; 有點不太自由，既不能彈性設定 FTP 登入的路徑，登入後的根目錄層也不能自由上傳檔案/新增目錄 (只能事先新增好一個資料夾用於上傳)，於是就找到了一個可以建立 FTP 上傳伺服器的套件 vsftpd。vsftpd 能夠設定虛擬帳號，且每個帳號都可以設定自己的初始目錄 (也可以設定重複的資料夾)，對於需要分配 FTP 帳號給其他人使用，且安全性較低的內容，可以使用此解決方案。&lt;/p&gt;
&lt;p&gt;因此這篇來簡單紀錄一下，在 Laravel Forge 配置的服務器上設定 FTP 上傳檔案的方式。&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#安裝-vsftpd&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;安裝 vsftpd&lt;/h2&gt;
&lt;p&gt;首先先更新 apt 套件和安裝 vsftpd：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;apt&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;update&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;apt&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;vsftpd&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;&lt;a href=&quot;#開啟防火牆連接埠&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;開啟防火牆連接埠&lt;/h2&gt;
&lt;p&gt;先確認現在防火牆的狀態：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ufw&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;status&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;基本上 Forge 預設只會幫我們設定好三個 port，22 是 SSH，80 是 HTTP，443 是 HTTPS：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Status: active&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;To                         Action      From&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;--                         ------      ----&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;22                         ALLOW       Anywhere&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;80                         ALLOW       Anywhere&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;443                        ALLOW       Anywhere&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;22 (v6)                    ALLOW       Anywhere (v6)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;80 (v6)                    ALLOW       Anywhere (v6)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;443 (v6)                   ALLOW       Anywhere (v6)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;然後是規劃一下我們要在 vsftpd 中使用的 port ，vsftpd 預設的 port 是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;主動連線 port (&lt;code&gt;ftp_data_port&lt;/code&gt;)：20&lt;/li&gt;
&lt;li&gt;vsftpd 命令通道 port (&lt;code&gt;listen_port&lt;/code&gt;)：21&lt;/li&gt;
&lt;li&gt;passive mode 最小 port (&lt;code&gt;pasv_min_port&lt;/code&gt;)：0 (預設是不限制連接的 port)&lt;/li&gt;
&lt;li&gt;passive mode 最大 port (&lt;code&gt;pasv_max_port&lt;/code&gt;)：0 (預設是不限制連接的 port)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;當然盡量不要使用預設的 port ，既然可以自訂，那就換成以下的：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;主動連線 port (&lt;code&gt;ftp_data_port&lt;/code&gt;)：60020&lt;/li&gt;
&lt;li&gt;vsftpd 命令通道 port (&lt;code&gt;listen_port&lt;/code&gt;)：60021&lt;/li&gt;
&lt;li&gt;passive mode 最小 port (&lt;code&gt;pasv_min_port&lt;/code&gt;)：65400&lt;/li&gt;
&lt;li&gt;passive mode 最大 port (&lt;code&gt;pasv_max_port&lt;/code&gt;)：65420&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;其中 passive mode 會開啟 65400~65420 共 21 個 port。&lt;/p&gt;
&lt;p&gt;確定沒問題就可以更新防火牆的設定：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ufw&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;allow&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;60020,60021/tcp&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ufw&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;allow&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;65400:65420/tcp&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;

&lt;h2&gt;&lt;a href=&quot;#vsftpd-設定&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;vsftpd 設定&lt;/h2&gt;
&lt;p&gt;備份 vsftpd 設定檔：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cp&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/etc/vsftpd.conf&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/etc/vsftpd.conf.orig&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;用 vim 開啟 vsftpd 設定檔：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;vim&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/etc/vsftpd.conf&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;先確認以下設定是否正確：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;/etc/vsftpd.conf&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;anonymous_enable&lt;/span&gt;&lt;span&gt;=NO&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;local_enable&lt;/span&gt;&lt;span&gt;=YES&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;然後將以下設定刪掉註解：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;/etc/vsftpd.conf&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;write_enable&lt;/span&gt;&lt;span&gt;=YES&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;chroot_local_user&lt;/span&gt;&lt;span&gt;=YES&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;utf8_filesystem&lt;/span&gt;&lt;span&gt;=YES&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;最後將以下設定複製到設定檔最底部：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;/etc/vsftpd.conf&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# Basic settings&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ftp_data_port&lt;/span&gt;&lt;span&gt;=60020&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;listen_port&lt;/span&gt;&lt;span&gt;=60021&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;pasv_min_port&lt;/span&gt;&lt;span&gt;=65400&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;pasv_max_port&lt;/span&gt;&lt;span&gt;=65420&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;use_localtime&lt;/span&gt;&lt;span&gt;=YES&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;pasv_enable&lt;/span&gt;&lt;span&gt;=YES&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;local_umask&lt;/span&gt;&lt;span&gt;=022&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;allow_writeable_chroot&lt;/span&gt;&lt;span&gt;=YES&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;userlist_enable&lt;/span&gt;&lt;span&gt;=YES&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;userlist_file&lt;/span&gt;&lt;span&gt;=/etc/vsftpd.userlist&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;userlist_deny&lt;/span&gt;&lt;span&gt;=NO&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;tcp_wrappers&lt;/span&gt;&lt;span&gt;=YES&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;xferlog_enable&lt;/span&gt;&lt;span&gt;=YES&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;vsftpd_log_file&lt;/span&gt;&lt;span&gt;=/var/log/vsftpd.log&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# Virtual users&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;secure_chroot_dir&lt;/span&gt;&lt;span&gt;=/var/run/vsftpd/empty&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;pam_service_name&lt;/span&gt;&lt;span&gt;=vsftpd&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;hide_ids&lt;/span&gt;&lt;span&gt;=YES&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;guest_enable&lt;/span&gt;&lt;span&gt;=YES&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;guest_username&lt;/span&gt;&lt;span&gt;=forge&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;user_config_dir&lt;/span&gt;&lt;span&gt;=/etc/vsftpd/user_conf&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;one_process_model&lt;/span&gt;&lt;span&gt;=NO&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;virtual_use_local_privs&lt;/span&gt;&lt;span&gt;=YES&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;local_root&lt;/span&gt;&lt;span&gt;=/home/vsftpd &lt;/span&gt;&lt;span&gt;# 目前此目錄不存在，目的是讓每個虛擬用戶都設定不同的路徑&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;&lt;a href=&quot;#建立虛擬用戶&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;建立虛擬用戶&lt;/h2&gt;
&lt;p&gt;然後要根據上面的設定來執行一些對應的指令。先是要刪除 &lt;code&gt;/var/run/vsftpd/empty&lt;/code&gt; 的讀取權限：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;chmod&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;o-w&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/var/run/vsftpd/empty&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;然後建立虛擬用戶的帳密資料庫 &lt;code&gt;/etc/vsftpd/virtual-user&lt;/code&gt;，最終用於 FTP 登入：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;mkdir&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/etc/vsftpd&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;vim&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/etc/vsftpd/virtual-user&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;virtual-user&lt;/code&gt; 檔格式為一行帳號、一行密碼：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;user1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;password&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;user2&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;password&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;填好之後就要轉換成資料庫檔案（Berkeley DB 格式），如果出現 &lt;code&gt;The program &apos;db_load&apos; is currently not installed.&lt;/code&gt; 錯誤訊息，就執行 &lt;code&gt;sudo apt install db-util&lt;/code&gt; 指令來安裝需要的套件：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;chmod&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;600&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/etc/vsftpd/virtual-user&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;apt&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;db-util&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;db_load&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-T&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-t&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;hash&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-f&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/etc/vsftpd/virtual-user&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/etc/vsftpd/virtual-user.db&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;chmod&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;600&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/etc/vsftpd/virtual-user.db&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;接著新增 &lt;code&gt;userlist&lt;/code&gt; 檔案，設定允許使用 FTP 的帳號：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;vim&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/etc/vsftpd.userlist&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;然後把 FTP 帳號都填上去：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;user1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;user2&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;不過 &lt;code&gt;virtual-user&lt;/code&gt; 畢竟是明碼儲存帳密，建議轉換完之後就把 &lt;code&gt;virtual-user&lt;/code&gt; 檔案刪除。&lt;/p&gt;
&lt;p&gt;然後建立虛擬用戶的設定檔：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;mkdir&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/etc/vsftpd/user_conf&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;vim&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/etc/vsftpd/user_conf/user1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;vim&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/etc/vsftpd/user_conf/user2&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;user1 帳號的設定檔：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;local_root=/home/forge/example.com&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;user2 帳號的設定檔：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;local_root=/home/forge/example.com/storage/app/public&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;local_root&lt;/code&gt; 就是 FTP 登入後的主目錄，其他 &lt;code&gt;vsftpd.conf&lt;/code&gt; 的參數都可以使用。&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#pam-設定&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;PAM 設定&lt;/h2&gt;
&lt;p&gt;備份 vsftpd 的 PAM 設定檔：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cp&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/etc/pam.d/vsftpd&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/etc/pam.d/vsftpd.orig&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;開始編輯 &lt;code&gt;/etc/pam.d/vsftpd&lt;/code&gt;：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;vim&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/etc/pam.d/vsftpd&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;把預設設定全部註解掉後，在下面增加以下設定，讓 vsftpd 讀取 &lt;code&gt;virtual-user&lt;/code&gt; 資料庫中的帳密：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;/etc/pam.d/vsftpd&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;auth  required  pam_userdb.so &lt;/span&gt;&lt;span&gt;db&lt;/span&gt;&lt;span&gt;=/etc/vsftpd/virtual-user&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;account  required  pam_userdb.so &lt;/span&gt;&lt;span&gt;db&lt;/span&gt;&lt;span&gt;=/etc/vsftpd/virtual-user&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;最後重啟 &lt;code&gt;vsftpd&lt;/code&gt; 就可以連線了：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;systemctl&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;restart&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;vsftpd&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;FileZilla 連線可以參考以下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;協定：FTP&lt;/li&gt;
&lt;li&gt;主機：[你的主機IP]&lt;/li&gt;
&lt;li&gt;連接埠：60021&lt;/li&gt;
&lt;li&gt;加密：允許的話就使用透過外顯式 TLS 的 FTP&lt;/li&gt;
&lt;li&gt;使用者：user1&lt;/li&gt;
&lt;li&gt;密碼：password&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;&lt;a href=&quot;#ssl-憑證&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;SSL 憑證&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;目前還沒試過，有空再補上…&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#參考資料&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;參考資料&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://man.linuxreviews.org/man5/vsftpd.conf.5.html&quot;&gt;Manpage of VSFTPD.CONF&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.digitalocean.com/community/tutorials/how-to-set-up-vsftpd-for-a-user-s-directory-on-ubuntu-20-04&quot;&gt;How To Set Up vsftpd for a User’s Directory on Ubuntu 20.04&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://linux.vbird.org/linux_server/redhat9/0410vsftpd.php&quot;&gt;鳥哥私房菜 - 第二十四章、簡易 vsftpd 伺服器設定&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://calvertyang.github.io/2017/02/06/how-to-setup-virtual-users-for-vsftpd-in-ubuntu/&quot;&gt;如何在 Ubuntu 中為 vsftpd 設置虛擬帳號&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ishm.idv.tw/archives/607&quot;&gt;500 OOPS: vsftpd: refusing to run with writable root inside chroot()&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://community.letsencrypt.org/t/certbot-post-hook-vsftpd/187159&quot;&gt;Certbot + post hook + vsftpd&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>孤獨搖滾劇場版前篇心得</title><link>https://star-note-lucas.me/posts/bocchi-the-rock-soushuuhen-1</link><guid isPermaLink="true">https://star-note-lucas.me/posts/bocchi-the-rock-soushuuhen-1</guid><pubDate>Sat, 27 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;看完《劇場總集篇 孤獨搖滾！Re:》了~ 孤獨搖滾的劇場版前篇~&lt;/p&gt;
&lt;p&gt;雖然是總集篇，雖然都知道劇情了，但是還是覺得超好看的！！！LIVE 看得超爽的！！！付了電影票錢值的呀！！！&lt;/p&gt;
&lt;p&gt;這次劇場版內容主要是正片1~8集的劇情，不過有刪掉或快速帶過部分劇情，剪輯成了適合電影長度的節奏，這點上是有加分不少的，重點是還有新歌！在 PPT 快速切畫面的時候放新歌，簡直不要太爽😍 以及結尾的「孤獨搖滾」的點題後放 ED，全身雞皮疙瘩都起來了！！！&lt;/p&gt;
&lt;p&gt;等10月後篇出了再光臨一次電影院~&lt;/p&gt;</content:encoded></item><item><title>Nginx 簡易防止連結圖片/網頁盜連</title><link>https://star-note-lucas.me/posts/nginx-prevent-link</link><guid isPermaLink="true">https://star-note-lucas.me/posts/nginx-prevent-link</guid><description>紀錄一下使用 Nginx 檢查 referer 來實作簡易防止連結的功能</description><pubDate>Tue, 25 Jun 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;紀錄一下使用 Nginx 檢查 referer 來實作簡易防止連結的功能。&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#簡易圖片防盜連&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;簡易圖片防盜連&lt;/h2&gt;
&lt;p&gt;防止其他網站直接連入自家網頁內的圖片，不過可以在新分頁開啟：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;location&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/images &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;   &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;valid_referers &lt;/span&gt;&lt;span&gt;server_names none;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; ($&lt;/span&gt;&lt;span&gt;invalid_referer&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;403&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;&lt;a href=&quot;#簡易頁面防直連&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;簡易頁面防直連&lt;/h2&gt;
&lt;p&gt;模擬了一個情境，當自己網頁中提供了類似雲端硬碟，可以讓人下載檔案的服務，但想要讓訪客只能透過提供的主頁進入下載頁，而不想要讓其直接進入或分享下載頁的連結時使用：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;location&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/secret.html &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;   &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;valid_referers &lt;/span&gt;&lt;span&gt;server_names;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; ($&lt;/span&gt;&lt;span&gt;invalid_referer&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;rewrite&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;^/(.*)$&lt;/span&gt;&lt;span&gt; https://star-note-lucas.me/ &lt;/span&gt;&lt;span&gt;redirect&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;如果使用者分享了下載頁，或從其他分頁開啟，就會自動跳轉至一個指定的頁面。&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#參考資料&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;參考資料&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.cnblogs.com/linuxshare/p/17188974.html&quot;&gt;Nginx配置referer校验，实现简单的防盗链&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>s3cmd 和 s5cmd 安裝筆記</title><link>https://star-note-lucas.me/posts/s3cmd</link><guid isPermaLink="true">https://star-note-lucas.me/posts/s3cmd</guid><description>紀錄一下 s3cmd 和 s5cmd 的安裝步驟，和串接 DigitalOcean Spaces</description><pubDate>Fri, 26 Apr 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;原本是用 Laravel 的 S3 driver 來上傳檔案，但偏偏卻遇到需要一次上傳數百個小檔案的情境，如果按照在 Laravel 的寫法，上傳會要花非常久的時間，偏偏這數百個小檔案家起來卻又才幾 MB，搜了一番之後的結論是要透過 CLI 工具來解決。姑且是有找到老牌用 Python 寫的 &lt;a href=&quot;https://github.com/s3tools/s3cmd&quot;&gt;s3cmd&lt;/a&gt; 和用 Golang 寫的 &lt;a href=&quot;https://github.com/peak/s5cmd&quot;&gt;s5cmd&lt;/a&gt;，有支援 sync 的指令可以快速同步資料夾和檔案。本篇先記錄下在 Ubuntu 的安裝方式，以及我現在是在使用相容 S3 API 的 &lt;a href=&quot;https://www.digitalocean.com/products/spaces&quot;&gt;DigitalOcean Spaces&lt;/a&gt;，因此範例會使用 DigitalOcean Spaces 的網址設定。&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#s3cmd&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;s3cmd&lt;/h2&gt;
&lt;p&gt;老牌知名的非官方 S3 CLI 工具，基本上常見的操作指令都有。&lt;/p&gt;
&lt;p&gt;安裝方式有很多種，我選擇用最簡單的用 pip 安裝，需要先安裝 Python：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;pip&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;s3cmd&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;然後開始設定 key 之類的東西，等下要照順序填：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;s3cmd&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--configure&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;然後輸入 Access Key 和 Secret Key 兩個金鑰，可以在 &lt;a href=&quot;https://cloud.digitalocean.com/account/api/spaces&quot;&gt;Spaces access keys&lt;/a&gt; 新增，region 不需要設定，因為等下會連同 endpoint 網址一起設定：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Access Key []: EXAMPLE7UQOTHDTF3GK4&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Secret Key []: exampleb8e1ec97b97bff326955375c5&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Default Region [US]:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;然後要輸入 DigitalOcean 的 endpoint 網址，命名格是要依照 &lt;code&gt;&amp;lt;region&amp;gt;.digitaloceanspaces.com&lt;/code&gt; 來填，像這裡我設定的 region 是 &lt;code&gt;sgp1&lt;/code&gt;：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Use &quot;s3.amazonaws.com&quot; for S3 Endpoint and not modify it to the target Amazon S3.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;S3 Endpoint [s3.amazonaws.com]: sgp1.digitaloceanspaces.com&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;這邊的是要填完整的網址範本，直接輸入 &lt;code&gt;%(bucket)s.sgp1.digitaloceanspaces.com&lt;/code&gt; 就可以了 (一字不漏照打)，&lt;code&gt;%(bucket)s&lt;/code&gt; 是替換用的變數，會用於替換掉 bucket 名稱，如果你的 region 和我不同的話需要修改：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Use &quot;%(bucket)s.s3.amazonaws.com&quot; to the target Amazon S3. &quot;%(bucket)s&quot; and &quot;%(location)s&quot; vars c&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;an be used if the target S3 system supports dns based buckets.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;DNS-style bucket+hostname:port template for accessing a bucket []: %(bucket)s.sgp1.digitaloceanspaces.com&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;接下來這幾個可以視情況酌情修改，我是直接都 Enter 跳過：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Encryption password:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Path to GPG program [/usr/bin/gpg]:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Use HTTPS protocol [Yes]:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;HTTP Proxy server name:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;最後兩個如果都沒問題都輸入 &lt;code&gt;Y&lt;/code&gt; 就設定完成了：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Test access with supplied credentials? [Y/n] Y&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Save settings? [y/N] Y&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;這些設定的內容會儲存在 &lt;code&gt;~/.s3cfg&lt;/code&gt; 這個檔案裡面，有需要修改可以直接看這個檔案就行。&lt;/p&gt;
&lt;p&gt;s3cmd 的指令範例：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# 將 ~/demo/ 資料夾下的檔案上傳到 s3://demo-bucket/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;s3cmd&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--recursive&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--acl-public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--guess-mime-type&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;put&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~/demo/&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;s3://demo-bucket/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# 將 s3://demo-bucket/ 下的檔案下載到 ~/demo/ 資料夾&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;s3cmd&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--recursive&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;get&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;s3://demo-bucket/&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~/demo/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# 將 ~/demo/ 資料夾下的檔案同步到 s3://demo-bucket/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;s3cmd&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--acl-public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--guess-mime-type&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--delete-removed&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sync&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~/demo/&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;s3://demo-bucket/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# 將 s3://demo-bucket/ 下的檔案強制全部刪除 (全部清空)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;s3cmd&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--recursive&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--force&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;s3://demo-bucket/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;&lt;a href=&quot;#s5cmd&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;s5cmd&lt;/h2&gt;
&lt;p&gt;s5cmd 是用 Golang 寫的 S3 操作工具，執行速度比 s3cmd 還要快，但指令上不大相同。&lt;/p&gt;
&lt;p&gt;安裝方式我選擇直接下載 s5cmd 發布的 &lt;a href=&quot;https://github.com/peak/s5cmd/releases&quot;&gt;GitHub release&lt;/a&gt; binary，根據你的主機要改成支援的版本，以下是一個範例的腳本：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;curl&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-LO&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;https://github.com/peak/s5cmd/releases/download/v2.2.2/s5cmd_2.2.2_Linux-64bit.tar.gz&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;mkdir&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~/s5cmd_2.2.2_Linux-64bit&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;tar&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-xvzf&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;s5cmd_2.2.2_Linux-64bit.tar.gz&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-C&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~/s5cmd_2.2.2_Linux-64bit&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# 如果當前使用者有 `~/.local/bin` 資料夾則執行&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;cp&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~/s5cmd_2.2.2_Linux-64bit/s5cmd&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~/.local/bin&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# 如果可以執行 `sudo` 權限則執行&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cp&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-r&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~/s5cmd_2.2.2_Linux-64bit&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/opt/s5cmd&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ln&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-s&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/opt/s5cmd/s5cmd&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/usr/bin/s5cmd&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;rm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-rf&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~/s5cmd_2.2.2_Linux-64bit&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;s5cmd_2.2.2_Linux-64bit.tar.gz&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;s5cmd 預設是使用 AWS SDK 的 credentials 格式，下面就手動建立需要的 &lt;code&gt;.aws&lt;/code&gt; 資料夾：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;mkdir&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~/.aws&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;chmod&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;755&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~/.aws&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;touch&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~/.aws/credentials&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;chmod&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;600&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~/.aws/credentials&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;然後在 &lt;code&gt;~/.aws/credentials&lt;/code&gt; 裡輸入 Access Key 和 Secret Key 兩個金鑰，可以在 &lt;a href=&quot;https://cloud.digitalocean.com/account/api/spaces&quot;&gt;Spaces access keys&lt;/a&gt; 新增：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;~/.aws/credentials&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;[default]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;aws_access_key_id&lt;/span&gt;&lt;span&gt; = EXAMPLE7UQOTHDTF3GK4&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;aws_secret_access_key&lt;/span&gt;&lt;span&gt; = exampleb8e1ec97b97bff326955375c5&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;s5cmd 的指令範例：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# 將 ~/demo/ 資料夾下的檔案上傳到 s3://demo-bucket/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;s5cmd&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--endpoint-url&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;https://sgp1.digitaloceanspaces.com&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;cp&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--acl=public-read&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--cache-control&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;max-age=604800&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;~/demo/&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;s3://demo-bucket/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# 將 s3://demo-bucket/ 下的檔案下載到 ~/demo/ 資料夾&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;s5cmd&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--endpoint-url&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;https://sgp1.digitaloceanspaces.com&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;cp&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;s3://demo-bucket/&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~/demo/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# 將 ~/demo/ 資料夾下的檔案同步到 s3://demo-bucket/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;s5cmd&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--endpoint-url&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;https://sgp1.digitaloceanspaces.com&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;sync&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--acl=public-read&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--cache-control&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;max-age=604800&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--delete&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;~/demo/&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;s3://demo-bucket/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# 將 s3://demo-bucket/ 下的檔案強制全部刪除 (全部清空)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;s5cmd&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--endpoint-url&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;https://sgp1.digitaloceanspaces.com&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;rm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;s3://demo-bucket/&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;&lt;a href=&quot;#參考資料&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;參考資料&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://s3tools.org/s3cmd&quot;&gt;s3cmd&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.digitalocean.com/products/spaces/reference/s3cmd/&quot;&gt;Setting Up s3cmd 2.x with DigitalOcean Spaces&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/peak/s5cmd&quot;&gt;s5cmd&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://joshua-robinson.medium.com/s5cmd-for-high-performance-object-storage-7071352cc09d&quot;&gt;S5cmd for High Performance Object Storage&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>{Laravel x Vue}Conf Taiwan 2023 心得</title><link>https://star-note-lucas.me/posts/laravelvue-conf-2023</link><guid isPermaLink="true">https://star-note-lucas.me/posts/laravelvue-conf-2023</guid><description>趁記憶還在的時候記一下聽完的小小心得</description><pubDate>Sun, 20 Aug 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;剛看完 {Laravel x Vue}Conf Taiwan 2023，趁記憶還在的時候記一下聽完的小小心得。&lt;/p&gt;
&lt;p&gt;因為是雙軌制，Laravel 跟 Vue 只能二選一，雖然我兩個都很想聽就是了😭 只能忍痛選一個了…&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#重新发明-vue经验和教训&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;重新发明 Vue：经验和教训&lt;/h2&gt;
&lt;p&gt;興奮到只記得尤雨溪大大了XDD 咳… 不是&lt;/p&gt;
&lt;p&gt;至少重新的全盤了解 Vue 的發展歷史和未來，其中我還滿喜歡的是講到事後對於 Vue 2 到 3 重構的反思，的確是再次體會到維護一個影響力很大的專案是很不容易，需要時時刻刻照顧好開發者和使用者的體驗。&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#用-livewire-開發cms的甘苦談&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;用 Livewire 開發CMS的甘苦談&lt;/h2&gt;
&lt;p&gt;從這裡開始我幾乎都待在 Laravel 這軌了。Livewire 說實話我其實只有用過一點點，因為我現在鍾愛的是 Inertia XDD 阿 當然過了 2 年之後 Livewire 也有更新了不少，聽完這場之後未來可能會想在玩具專案上試試吧！而且新官網超酷炫~&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#探討-php-中的程式碼保護策略&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;探討 PHP 中的程式碼保護策略&lt;/h2&gt;
&lt;p&gt;Albert 大大每次都帶來還滿深的講題內容。對於程式碼保護我也是有去搜尋過，但出來的幾乎不外乎都是只講混淆。這場就很完整的介紹了保護方式、原理和推薦使用的工具等，收穫還滿多的。&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#從mvc到前後端分離的策略&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;從MVC到前後端分離的策略&lt;/h2&gt;
&lt;p&gt;下午就跑到 Vue 軌去聽了一場XD 因為我平常不是用 PHPStorm，而是用 VSCode~ 而這場則是學到了 .net 如何跟 Vue、Vite、Nuxt 整合的神奇用法，雖然平常用不到，但如果有需要的時候可以來試試看~&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#mysql-的權衡之道---優雅與卓越的設計心法&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;MySQL 的權衡之道 - 優雅與卓越的設計心法&lt;/h2&gt;
&lt;p&gt;講師好帥啊😍(被打&lt;/p&gt;
&lt;p&gt;撇開講師帥的部分，真的是把平常在用但幾乎不會的 MySQL 重新學了遍，而且內容循序漸進、架構清晰，聽著很舒服~ 還有提到很多豆知識都是以前不知道的。如果時間還夠，還想繼續聽完啊！(才…才不是因為…&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#運用event-storming來一起探索可觀測性&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;運用Event Storming來一起探索可觀測性&lt;/h2&gt;
&lt;p&gt;這場是離我相對比較有點距離的，聽的時候是半懂不懂，但事後回想其實重點只有兩個，其一是貼那一堆事件的便條紙，現在是看不大懂，以後有機會再懂，其二則是搭配炫酷的監控工具，這個可能比較有機會來試試看吧~&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#結語&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;結語&lt;/h2&gt;
&lt;p&gt;今年終於有機會來線下還挺開心的，因為前兩年一年是全線上、一年是有事。只差在一個人去是還有點怕怕就是了… (社恐的心理獨白) 不過還是期待明年~ 許個願希望明年的自己可以再去投個稿當講者🥰~&lt;/p&gt;</content:encoded></item><item><title>在 Laravel Forge 配置的服務器上設定 SFTP 伺服器</title><link>https://star-note-lucas.me/posts/laravel-forge-sftp</link><guid isPermaLink="true">https://star-note-lucas.me/posts/laravel-forge-sftp</guid><description>在 Laravel Forge 配置的服務器上設定 SFTP 上傳檔案的方式</description><pubDate>Sat, 22 Jul 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;以前剛學網站的時候都是用共享主機來放網站，並用 FTP 跟 FileZilla 來連線，但後來用習慣了 Git + SSH 的佈署方式之後就幾乎很少再用 FTP 了。但今天就偏偏就有了要開 FTP 帳號給人上傳檔案的需求，這篇來簡單紀錄一下，在 Laravel Forge 配置的服務器上設定 SFTP，和連結這些上傳檔案的方式。&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#建立帳號&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;建立帳號&lt;/h2&gt;
&lt;p&gt;基本上 Laravel Forge 配置過之後都會有一個 &lt;code&gt;forge&lt;/code&gt; 帳號，只要在後台設定過 SSH 的 Public Key 就可以直接連線：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ssh&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;forge@[你的主機IP]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;先建立一個 Unix 帳號，帳號名稱基本上是都可以，不過這邊我們可以在前面會冠一個 &lt;code&gt;ftp-&lt;/code&gt; 開頭來區別：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;adduser&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ftp-user&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;然後因為這個帳號目的只是用來 FTP 登入，基本上是不允許用 SSH 等來連線，因此首頁可以導向給 &lt;code&gt;/bin/false&lt;/code&gt;，只要一連上就會被斷掉：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;usermod&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-s&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/bin/&lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ftp-user&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;接下來要做 &lt;code&gt;chroot&lt;/code&gt; 的操作，可以理解成設定用 FTP 連線之後可以瀏覽使用的根目錄。&lt;/p&gt;
&lt;p&gt;比如當 ftp-user 登入的時候只能看到 &lt;code&gt;/home/ftp-user&lt;/code&gt; 目錄下的資料，無法回更上一層的目錄，相當於這就是它的根目錄了。需要注意一個點是，這個目錄的所有上層資料夾都必須是 owner 為 root，否則會無法成功設定 (如果都無法成功連線的話可以查看 &lt;code&gt;/var/log/auth.log&lt;/code&gt; 裡的錯誤原因)：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;chown&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;root:ftp-user&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/home/ftp-user&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;chmod&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;755&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/home/ftp-user&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;然後就可以在這個目錄下新增好目錄，讓用戶可以在 FileZilla 上傳了：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;mkdir&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/home/ftp-user/uploads&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;chown&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ftp-user:ftp-user&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/home/ftp-user/uploads&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;chmod&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;755&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/home/ftp-user/uploads&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;不過我一開始也在想，為什麼其他文章都教到要增加一個目錄，難道不能在 FileZilla 的根目錄直接上傳嗎？結果還真不行。因為剛才已經把根目錄 owner 設給 root，沒有 root 的權限就不能開新資料夾上傳檔案，因此需要先開好 owner 是 &lt;code&gt;ftp-user&lt;/code&gt; 的資料夾讓用戶可以上傳檔案。當然資料夾想要開幾個都可以~ 只是你開哪些用戶就只能用哪些。&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#設定-sftp&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;設定 SFTP&lt;/h2&gt;
&lt;p&gt;然後設定 &lt;code&gt;/etc/ssh/sshd_config&lt;/code&gt; 檔，先加上 &lt;code&gt;#&lt;/code&gt; 來註解掉以下：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;/etc/ssh/sshd_config&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Subsystem sftp /usr/lib/openssh/sftp-server&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;然後複製以下到該行之下，&lt;code&gt;ChrootDirectory&lt;/code&gt; 就是要鎖住 FTP 瀏覽時的根目錄：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;/etc/ssh/sshd_config&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# Enable sftp&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Subsystem sftp internal-sftp&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Match &lt;/span&gt;&lt;span&gt;User&lt;/span&gt;&lt;span&gt; ftp-user&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;# set the homedirectory as root&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;ChrootDirectory %h&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;ForceCommand internal-sftp&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;X11Forwarding no&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;AllowTCPForwarding no&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;# enable loging with a password&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;PasswordAuthentication yes&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;最後重啟 SSH 就可以連線了：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;service&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ssh&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;restart&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;FileZilla 連線可以參考以下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;協定：SFTP&lt;/li&gt;
&lt;li&gt;主機：[你的主機IP]&lt;/li&gt;
&lt;li&gt;連接埠：22&lt;/li&gt;
&lt;li&gt;使用者：ftp-user&lt;/li&gt;
&lt;li&gt;密碼：[剛才adduser時設定的密碼]&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;&lt;a href=&quot;#公開上傳檔案到-laravel-網站&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;公開上傳檔案到 Laravel 網站&lt;/h2&gt;
&lt;p&gt;當然我這次的目的是要讓這些上傳的檔案可以公開在網站中，因此參考了 Laravel 公開本地上傳檔案的方式，只需設個軟連結就行了：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ln&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-s&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/home/ftp-user/uploads&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/home/forge/[your-domain]/public/uploads&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;如果上傳了一張 &lt;code&gt;image.jpg&lt;/code&gt; 檔，就可以從 &lt;code&gt;https://[your-domain]/uploads/image.jpg&lt;/code&gt; 瀏覽了！&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#參考資料&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;參考資料&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://freek.dev/411-let-your-clients-use-sftp-on-a-forge-provisioned-server&quot;&gt;Let your clients use sftp on a Forge provisioned server&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.miniasp.com/post/2011/08/11/OpenSSH-SFTP-chroot-with-ChrootDirectory&quot;&gt;透過 OpenSSH 使用 SFTP 登入時將帳戶設為 chroot 的方法 (Linux)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>可塑性記憶心得</title><link>https://star-note-lucas.me/posts/plastic-memories</link><guid isPermaLink="true">https://star-note-lucas.me/posts/plastic-memories</guid><pubDate>Wed, 07 Jun 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;剛看完了可塑性記憶，中間哭了好幾次 QQ 艾菈好可愛~&lt;/p&gt;
&lt;p&gt;雖然以前就有聽說是很催淚的作品，就想說反正你也騙不了我的眼淚就沒去看，但真的看完之後還是覺得很感動~&lt;/p&gt;
&lt;p&gt;而之所以能感動，是因為和裡面的角色有著類似的生活方式，同樣要吃飯睡覺生活工作，同樣也都要經歷離別的時刻，這些都是我們做為人會經歷的過程。&lt;/p&gt;
&lt;p&gt;相遇是為了離別，但真到了離別之時，回憶中的點滴才顯得如此彌足珍貴。知道自己的生命注定結束的時間，剩下的時間才會盡力去不留遺憾。&lt;/p&gt;
&lt;p&gt;「願和重要的人能再次相遇」&lt;/p&gt;</content:encoded></item><item><title>使用 Chocolatey 安裝 Windows 軟體</title><link>https://star-note-lucas.me/posts/chocolatey</link><guid isPermaLink="true">https://star-note-lucas.me/posts/chocolatey</guid><description>又是某天的下午，在幫朋友的新電腦裝軟體的時候，重複著先去各個官網下載和瘋狂點下一步，我心裡想說：何不去研究用下指令來安裝軟體呢?...</description><pubDate>Fri, 31 Mar 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;又是某天的下午，在幫朋友的新電腦裝軟體的時候，重複著先去各個官網下載和瘋狂點下一步，我心裡想說：「何不去研究用下指令來安裝軟體呢？」，於是就去稍微研究了一下 Chocolatey，發現還滿好用的，連 Google Chrome、LINE 跟 K-Lite 都可以裝，這篇就來記錄一下安裝和配置我常用的軟體啦～&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#chocolatey&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;Chocolatey&lt;/h2&gt;
&lt;p&gt;以系統管理員身分執行 Powershell：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString(&apos;https://community.chocolatey.org/install.ps1&apos;))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;安裝軟體 (可以在&lt;a href=&quot;https://community.chocolatey.org/packages&quot;&gt;這裡&lt;/a&gt;搜尋軟體)：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;choco install googlechrome -y&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;choco install line -y&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;choco install git -y&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;choco install nodejs-lts -y&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;choco install vscode --params &quot;/NoDesktopIcon&quot; -y&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;choco install 7zip -y&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;choco install ffmpeg -y&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;choco install k-litecodecpackfull -y&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;查看本地安裝的軟體：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;choco list -l&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;&lt;a href=&quot;#參考資料&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;參考資料&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://chocolatey.org/&quot;&gt;Chocolatey 官網&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>ffmpeg 命令筆記</title><link>https://star-note-lucas.me/posts/ffmpeg</link><guid isPermaLink="true">https://star-note-lucas.me/posts/ffmpeg</guid><description>紀錄一下最近在用 ffmpeg 的一些常用指令，和轉檔成 Chromecast 支援的影片格式</description><pubDate>Mon, 20 Mar 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;紀錄一下最近在用 ffmpeg 的一些常用指令，和轉檔成 Chromecast 支援的影片格式。&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#轉檔&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;轉檔&lt;/h2&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-i&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;input.avi&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;output.mp4&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;更多選項&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-i&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;input.avi&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-c:v&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;libx264&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-preset&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;medium&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-crf&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;23&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-c:a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;aac&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-b:a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;160k&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-vf&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;format=yuv420p&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;output.mp4&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;&lt;a href=&quot;#壓縮&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;壓縮&lt;/h2&gt;
&lt;p&gt;壓縮影片 (&lt;code&gt;-crf&lt;/code&gt; 預設值是 23，可以調更低比如 18 來壓縮影片品質)：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-i&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;input.mp4&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-c:v&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;libx264&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-crf&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;23&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-c:a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;copy&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;output.mp4&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;壓縮音樂：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-i&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;input.mp3&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-vn&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-b:a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;192k&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;output.mp4&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;&lt;a href=&quot;#定位時間&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;定位時間&lt;/h2&gt;
&lt;p&gt;依照 &lt;code&gt;HOURS:MM:SS.MICROSECONDS&lt;/code&gt; 的格式來輸入時間。如果輸入是數字預設是秒數，比如 30 是 30 秒。&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#分割片段&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;分割片段&lt;/h2&gt;
&lt;p&gt;從 00:01:00 剪到 00:02:00：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# 如果使用 `-c copy` 來分割，且要用 Concat Demuxer 合併的話，需要使用 `-avoid_negative_ts 1` 參數&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-ss&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;00:01:00&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-to&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;00:02:00&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-accurate_seek&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-i&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;video.mp4&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-c&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;copy&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-avoid_negative_ts&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cut.mp4&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# 如果剪出來的片段有問題，可以重新編碼&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-ss&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;00:01:00&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-to&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;00:02:00&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-accurate_seek&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-i&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;video.mp4&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-c:v&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;libx264&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-crf&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;23&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-c:a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;aac&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-b:a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;192k&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cut.mp4&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;從 00:01:00 剪 2 分鐘：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# 如果使用 `-c copy` 來分割，且要用 Concat Demuxer 合併的話，需要使用 `-avoid_negative_ts 1` 參數&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-ss&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;00:01:00&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-t&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;00:02:00&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-accurate_seek&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-i&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;video.mp4&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-c&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;copy&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-avoid_negative_ts&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cut.mp4&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# 如果剪出來的片段有問題，可以重新編碼&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-ss&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;00:01:00&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-t&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;00:02:00&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-accurate_seek&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-i&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;video.mp4&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-c:v&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;libx264&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-crf&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;23&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-c:a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;aac&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-b:a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;192k&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cut.mp4&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;&lt;a href=&quot;#合併片段&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;合併片段&lt;/h2&gt;
&lt;p&gt;使用 Concat Demuxer 來合併，新增一個 &lt;code&gt;mylist.txt&lt;/code&gt; 檔案，檔案中包含要合併的檔案們的路徑 (可以只用檔名)：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;file &apos;/path/to/video1.mp4&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;file &apos;/path/to/video2.mp4&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;file &apos;/path/to/video3.mp4&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;然後就可以開始合併這些媒體檔案&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-f&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;concat&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-safe&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-i&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;mylist.txt&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-c&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;copy&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;output.mp4&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;使用 Concat Protocol 來合併，只有在合併 MPEG-2 的 ts 檔案時使用：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-i&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;concat:video1.ts|video2.ts|video3.ts&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-c&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;copy&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;output.ts&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;&lt;a href=&quot;#提取音樂影片&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;提取音樂/影片&lt;/h2&gt;
&lt;p&gt;提取音樂：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-i&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;input.mp4&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-q:a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-map&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;output.mp3&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;提取影片：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-i&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;input.mp4&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-c:v&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;copy&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-an&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;output.mp4&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;&lt;a href=&quot;#截圖&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;截圖&lt;/h2&gt;
&lt;p&gt;快速截圖：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-ss&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;01:20:29&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-i&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;video.mp4&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-frames:v&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;out1.jpg&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;精準截圖：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-i&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;video.mp4&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-ss&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;01:20:29&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-frames:v&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;out2.jpg&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;將影片中所有影格(Frame)都擷取成 JPG 圖片：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-i&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;video.mp4&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;video-screenshot-%04d.jpg&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;將影片中每1秒的畫面擷取成 JPG 圖片：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# 每1秒截1張&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-i&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;video.mp4&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-vf&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fps=&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;video-screenshot-%04d.jpg&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# 每5秒截1張&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-i&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;video.mp4&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-vf&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fps=1/5&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;video-screenshot-%04d.jpg&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;&lt;a href=&quot;#縮放畫面尺寸&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;縮放畫面尺寸&lt;/h2&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-i&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;video.mp4&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-vf&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;scale=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;1280:-1&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;output.mp4&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;&lt;a href=&quot;#碼率-bitrate&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;碼率 (bitrate)&lt;/h2&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-i&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;video.mp4&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-b:v&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1000k&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;output.mp4&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;&lt;a href=&quot;#幀率-frame-rate&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;幀率 (frame rate)&lt;/h2&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-i&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;video.mp4&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-r&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;25&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;output.mp4&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;&lt;a href=&quot;#mkv-字幕轉-mp4&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;mkv 字幕轉 mp4&lt;/h2&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-i&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;video.mkv&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-vf&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;subtitles=video.mkv:si=0&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-strict&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-2&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;video.mp4&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;字幕順序從 &lt;code&gt;si=0&lt;/code&gt; 開始。&lt;/p&gt;
&lt;p&gt;如果不知道字幕的 index，可以使用 &lt;code&gt;ffprobe&lt;/code&gt; 來查詢：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ffprobe&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-loglevel&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;error&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-select_streams&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;s&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-show_entries&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;stream=codec_name,index:stream_tags=language&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-of&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;csv=p=&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;video.mkv&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;如果以上方法不管用的話，需要使用 &lt;a href=&quot;https://mkvtoolnix.download/&quot;&gt;MKVToolNix&lt;/a&gt; 合併。&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#ass-字幕嵌入-mp4&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;ass 字幕嵌入 mp4&lt;/h2&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-i&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;video.mp4&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-vf&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;ass=subtitle.ass&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;output.mp4&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;&lt;a href=&quot;#sup-圖形字幕嵌入-mp4&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;sup 圖形字幕嵌入 mp4&lt;/h2&gt;
&lt;p&gt;如果 sup 圖形字幕在 &lt;code&gt;.mkv&lt;/code&gt; 檔案中：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-i&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;video.mkv&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-filter_complex&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;[0:v][0:s]overlay[v1]&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-map&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;[v1]&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-map&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0:a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;output.mp4&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;如果 sup 圖形字幕是額外的字幕檔：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-i&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;video.mp4&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-i&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;subtitle.sup&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-filter_complex&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;[0:v][1:s]overlay[v1]&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-map&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;[v1]&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-map&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0:a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;output.mp4&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;&lt;a href=&quot;#mkv-抽取軌道&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;mkv 抽取軌道&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://mkvtoolnix.download/&quot;&gt;MKVToolNix&lt;/a&gt; 附了一個 &lt;code&gt;mkvextract&lt;/code&gt; 可以來抽取出 &lt;code&gt;.mkv&lt;/code&gt; 的各個軌道：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;mkvextract&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tracks&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;video.mkv&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0:video.mp4&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1:audio.flac&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;2:subtitle.srt&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;&lt;a href=&quot;#轉成-8-位元深度&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;轉成 8 位元深度&lt;/h2&gt;
&lt;p&gt;為了要投放影片到 Chromecast 上面，卻發現影片是 10 位元深度而不能播，轉成 8 位元深度 (yuv420p) 就可以了：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-i&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;video.mp4&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-c:v&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;libx264&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-profile:v&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;high&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-level:v&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;5.1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-pix_fmt&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;yuv420p&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-c:a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;copy&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;output.mp4&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;&lt;a href=&quot;#音訊從-51-轉成雙聲道&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;音訊從 5.1 轉成雙聲道&lt;/h2&gt;
&lt;p&gt;為了要投放影片到 Chromecast 上面，卻發現不支援 5.1 而不能播，轉成雙聲道就可以了：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-i&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;video.mp4&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-c:v&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;copy&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-c:a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;aac&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-ac&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;output.mp4&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;&lt;a href=&quot;#下載-m3u8&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;下載 m3u8&lt;/h2&gt;
&lt;p&gt;直接丟 m3u8 連結給 ffmpeg，就可以下載影片了：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-i&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;https://example.com/720p.m3u8&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-c&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;copy&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;video.mp4&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;但也有會給 403 的時候，可以試試看加上 Cookie 和一些 headers：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ffmpeg&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-headers&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Cookie: 這裡放Cookie&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-headers&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Referer: https://example.com/&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-user_agent&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-i&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;https://example.com/720p.m3u8&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-c&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;copy&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;video.mp4&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;&lt;a href=&quot;#ffmpeg-相關資源&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;ffmpeg 相關資源&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.miniasp.com/post/2022/10/08/Useful-tool-FFmpeg&quot;&gt;介紹好用工具：FFmpeg (強大的錄影、轉檔、串流工具與函式庫)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.csdn.net/qq26983255/article/details/94590018&quot;&gt;FFmpeg：常用命令小笔记&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.huang1111.cn/index.php/archives/7/&quot;&gt;ffmpeg简单使用&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>AI 繪圖資源</title><link>https://star-note-lucas.me/posts/ai-art-resource</link><guid isPermaLink="true">https://star-note-lucas.me/posts/ai-art-resource</guid><description>玩了一小陣子 AI 繪圖，先把好用的資源記下來。</description><pubDate>Sun, 12 Mar 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;玩了一小陣子 AI 繪圖，先把好用的資源記下來。&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#軟體&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;軟體&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://mnya.tw/cc/word/1886.html&quot;&gt;Stable Diffusion web UI (處理訓練圖片、AI 繪圖用)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mnya.tw/cc/word/1939.html&quot;&gt;Kohya’s GUI (訓練 LoRA 模型)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.coolaler.com/index/%E4%BD%BF%E7%94%A8%E8%87%AA%E5%B7%B1%E7%9A%84%E9%A1%AF%E5%8D%A1%E9%80%B2%E8%A1%8C-ai-%E7%B9%AA%E5%9C%96-stable-diffusion-webui-%E5%AE%89%E8%A3%9D%E6%95%99%E5%AD%B8/&quot;&gt;使用自己的顯卡進行 AI 繪圖, Stable Diffusion webUI 安裝教學&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ivonblog.com/posts/google-colab-stable-diffusion-webui/&quot;&gt;Stable Diffusion WebUI 部署至 Google Colab 教學&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;&lt;a href=&quot;#stable-diffusion-web-ui-套件&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;Stable Diffusion web UI 套件&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://mnya.tw/cc/word/1888.html&quot;&gt;DeepDanbooru (反推 AI 繪圖標籤)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mnya.tw/cc/word/1958.html&quot;&gt;Tagger (更精準分析反推 AI 繪圖標籤)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mnya.tw/cc/word/1906.html&quot;&gt;R-ESRGAN 4x+ Anime6B (AI 放大及提高動漫圖片畫質)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mnya.tw/cc/word/1948.html&quot;&gt;OpenPose Editor (自訂骨骼動作、識別姿勢、收藏骨架)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mnya.tw/cc/word/1964.html&quot;&gt;Posex 3D (姿勢編輯器，自訂 AI 繪圖人物動作)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mnya.tw/cc/word/1961.html&quot;&gt;Depth map library and poser (自訂人物手勢)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mnya.tw/cc/word/1949.html&quot;&gt;Multi-ControlNet (多個 ControlNet 結合使用，同時指定 AI 繪圖的場景及姿勢)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mnya.tw/cc/word/1971.html&quot;&gt;圖庫瀏覽器 (瀏覽存檔的 AI 繪圖，並讀取其參數)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mnya.tw/cc/word/1962.html&quot;&gt;Extra networks 卡片&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/DominikDoom/a1111-sd-webui-tagcomplete&quot;&gt;標籤自動提示&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/butaixianran/Stable-Diffusion-Webui-Civitai-Helper&quot;&gt;管理 Civitai 模型&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;&lt;a href=&quot;#訓練模型&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;訓練模型&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://mnya.tw/cc/word/1897.html&quot;&gt;訓練超網路 (Hypernetwork) 教學，讓 AI 學習畫風&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mnya.tw/cc/word/1940.html&quot;&gt;訓練 LoRA 模型教學&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ivonblog.com/posts/stable-diffusion-webui-training/&quot;&gt;Stable Diffusion 模型訓練教學&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;&lt;a href=&quot;#提示詞參考&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;提示詞參考&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.qq.com/doc/DWHl3am5Zb05QbGVs?dver=&quot;&gt;元素法典&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.qq.com/doc/DWGh4QnZBVlJYRkly&quot;&gt;元素法典第一点五卷&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.qq.com/doc/DWEpNdERNbnBRZWNL&quot;&gt;元素法典 第二卷&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.qq.com/doc/DWHFOd2hDSFJaamFm&quot;&gt;元素法典 第二点五卷&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://aitag.top/&quot;&gt;魔咒百科词典&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://thereisnospon.github.io/NovelAiTag/&quot;&gt;NovelAI魔导书&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://strikingloo.github.io/stable-diffusion-vs-dalle-2&quot;&gt;提示詞範例 1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://decentralizedcreator.com/best-stable-diffusion-anime-prompts/&quot;&gt;提示詞範例 2&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;&lt;a href=&quot;#提示詞教學&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;提示詞教學&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://guide.novelai.dev/&quot;&gt;AiDraw&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.qq.com/doc/DWFdSTHJtQWRzYk9k&quot;&gt;元素同典：确实不完全科学的魔导书&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.qq.com/doc/DSHBGRmRUUURjVmNM&quot;&gt;Tags基本编写逻辑及三段术式入门与解析v3&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.bilibili.com/read/cv19645201&quot;&gt;元素三论，tag 排序，权重隔离渲染&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.yuque.com/longyuye/lmgcwy&quot;&gt;NovelAI 使用教程和魔咒课堂人偶教室的测试记录风格化&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.qq.com/doc/DTk1xem1OS2p5SXV1&quot;&gt;奥术构成-关于如何定向微调AI生成的T2I画面内容和风格&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://p1atdev.notion.site/p1atdev/611b109989c54ffca6219edd95d1b247&quot;&gt;魔術書&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://aiguidebook.top/&quot;&gt;AI绘图 wiki&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>Inertia.js SSR | 讓 Laravel x Vue 實現服務端渲染</title><link>https://star-note-lucas.me/posts/inertia-ssr</link><guid isPermaLink="true">https://star-note-lucas.me/posts/inertia-ssr</guid><description>介紹如何使用 Inertia.js 的 SSR (服務端渲染) 功能</description><pubDate>Wed, 12 Jan 2022 00:00:00 GMT</pubDate><content:encoded>&lt;div&gt;&lt;div&gt;Info&lt;/div&gt;&lt;div&gt;&lt;p&gt;2023/02/20 更新：將 Inertia 的使用方式更新到 v1 版，以及把 Laravel Mix 換成 Vite。&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;構建 Laravel 全端網站我最愛用的 &lt;a href=&quot;https://inertiajs.com/&quot;&gt;Inertia&lt;/a&gt;，現在終於也正式釋出 SSR 的功能，補足了 SPA 網站會有的 SEO 問題了！&lt;/p&gt;
&lt;p&gt;這一篇文章中會記錄在在一個現成的 Inertia 專案中加入 SSR 功能的過程，下面我們一起來看吧~&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#inertia-中-ssr-的運作原理&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;Inertia 中 SSR 的運作原理&lt;/h2&gt;
&lt;p&gt;先大概介紹一下運作原理，因為 Inertia 要適配 PHP 或 Ruby 等不同語言，使用了 Node.js 來實作 SSR 服務。在 SSR 模式下接收到請求後會直接將 &lt;a href=&quot;https://inertiajs.com/the-protocol#the-page-object&quot;&gt;page 物件&lt;/a&gt; 傳入 SSR Server 做解析，最後再回傳 HTML 給瀏覽器，結束這一次的請求。&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#更新-inertia&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;更新 Inertia&lt;/h2&gt;
&lt;p&gt;首先需要一個現成的 Inertia 專案，在現在新版 Laravel 預設都使用 Vite 來打包前端，因此我下面都會用 Vite 的配置。還有要更新 Inertia 到最新版才會支援 SSR 功能。&lt;/p&gt;
&lt;p&gt;安裝 Laravel 端到最新版：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;composer&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;require&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;inertiajs/inertia-laravel&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;安裝 Vue 端 (使用 NPM 或 Yarn)：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;npm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;@inertiajs/vue3&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# 或&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;yarn&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;@inertiajs/vue3&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;如果你的專案裡還有 &lt;code&gt;@inertiajs/inertia&lt;/code&gt; 和 &lt;code&gt;@inertiajs/inertia-vue3&lt;/code&gt; 的套件的話，表示還是在舊版，可以參考 &lt;a href=&quot;https://inertiajs.com/upgrade-guide&quot;&gt;升級指南&lt;/a&gt; 升級，基本上都是改改套件名字、變數名字等，升級難度應該不會太大。&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#設置-ssr-應用&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;設置 SSR 應用&lt;/h2&gt;
&lt;p&gt;現在就要開始配置 SSR 了，如果是在 Vue 3 新版可以不需要安裝官方的 SSR 套件 &lt;code&gt;@vue/server-renderer&lt;/code&gt;，因為 Vue 核心套件會連帶安裝了，可以改用 &lt;code&gt;vue/server-renderer&lt;/code&gt; 來使用。&lt;/p&gt;
&lt;p&gt;同樣 Inertia v1 版也會一起安裝 &lt;code&gt;@inertiajs/server&lt;/code&gt; 套件，裡面包含了一個執行 SSR 渲染的 HTTP Server。&lt;/p&gt;
&lt;p&gt;之後新增一個 &lt;code&gt;resources/js/ssr.js&lt;/code&gt; 檔案：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;touch&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;resources/js/ssr.js&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;這個 &lt;code&gt;ssr.js&lt;/code&gt; 檔案跟 &lt;code&gt;app.js&lt;/code&gt; 有點相似，但是個只會在服務端 (Node.js) 中執行的入口檔案，基本上不影響服務端渲染的套件都可以不用載入。而服務端/用戶端兩邊都會載入的部分就需要做兩邊兼容，比如需要顧慮到 Node.js 中沒有 &lt;code&gt;window&lt;/code&gt; 和 &lt;code&gt;document&lt;/code&gt; 的問題。&lt;/p&gt;
&lt;p&gt;下面提供 &lt;code&gt;resources/js/ssr.js&lt;/code&gt; 的範例：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;resources/js/ssr.js&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { createSSRApp, h } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;vue&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { renderToString } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;vue/server-renderer&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { createInertiaApp } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;@inertiajs/vue3&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; createServer &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;@inertiajs/vue3/server&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;createServer&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;page&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;createInertiaApp&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;page&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;render: &lt;/span&gt;&lt;span&gt;renderToString&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;resolve&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pages&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; import.&lt;/span&gt;&lt;span&gt;meta&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;glob&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;./Pages/**/*.vue&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt; eager&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pages&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;./Pages/&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;.vue&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;setup&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;{ &lt;/span&gt;&lt;span&gt;App&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;props&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;plugin&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;createSSRApp&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;render&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;h&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;App&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;props&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;})&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;use&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;plugin&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;})&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;這裡稍微提一下 Vue 的 hydration 操作，在 SSR 模式中因為已經把 HTML 渲染好了，Vue 接手的時候就不需要再重新渲染一遍，而是使用 hydration 的方式，直接把靜態的 HTML 轉換成可以與 Vue 互動的動態 DOM。但有個前提是&lt;strong&gt;服務端&lt;/strong&gt;生成的 DOM 和&lt;strong&gt;用戶端&lt;/strong&gt;的必須保持一致，如果遇到不同的話會直接中斷 hydration，不管原本的 DOM，直接重新渲染新的。&lt;/p&gt;
&lt;p&gt;在 Vue 3 要啟用 hydration 時，要將 &lt;code&gt;app.js&lt;/code&gt; 裡的 &lt;code&gt;createApp&lt;/code&gt; 替換成 &lt;code&gt;createSSRApp&lt;/code&gt;：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { createSSRApp, h } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;vue&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { createInertiaApp } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;@inertiajs/vue3&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;createInertiaApp&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;resolve&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pages&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; import.&lt;/span&gt;&lt;span&gt;meta&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;glob&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;./Pages/**/*.vue&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt; eager&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pages&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;./Pages/&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;.vue&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;setup&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;{ &lt;/span&gt;&lt;span&gt;el&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;App&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;props&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;plugin&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;createSSRApp&lt;/span&gt;&lt;span&gt;({ &lt;/span&gt;&lt;span&gt;render&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;h&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;App&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;props&lt;/span&gt;&lt;span&gt;) })&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;use&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;plugin&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;mount&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;el&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;})&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Hydration 相關更詳細的部分可以參考 &lt;a href=&quot;https://v3.cn.vuejs.org/guide/ssr/hydration.html#%E5%85%B3%E4%BA%8E%E6%BF%80%E6%B4%BB%E7%9A%84%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9&quot;&gt;Vue 的 hydration 注意事項&lt;/a&gt;。&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#設置-vite&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;設置 Vite&lt;/h2&gt;
&lt;p&gt;然後就進到 Vite 的編譯部分了，這裡需要在 &lt;code&gt;vite.config.js&lt;/code&gt; 裡增加剛才的 &lt;code&gt;ssr.js&lt;/code&gt; 路徑：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;defineConfig&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;plugins: [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;Laravel&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;input: [&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;resources/css/app.css&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;resources/js/app.js&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;ssr: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;resources/js/ssr.js&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;refresh: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;// ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;})&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;現在就可以執行 Vite 編譯 JS。原本的 &lt;code&gt;vite build&lt;/code&gt; 是編譯用戶端的，所以再加一個 &lt;code&gt;vite build --ssr&lt;/code&gt; 用於編譯 SSR 模式的指令：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;scripts&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;dev&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;vite&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;build&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;vite build &amp;amp;&amp;amp; vite build --ssr&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;之後只要執行就可以同時編譯用戶端跟 SSR 了：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;npm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;run&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;build&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;&lt;a href=&quot;#在-laravel-中啟用-ssr&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;在 Laravel 中啟用 SSR&lt;/h2&gt;
&lt;p&gt;開啟 &lt;code&gt;app.blade.php&lt;/code&gt;，也就是 Inertia 專案的進入點，把 &lt;code&gt;@inertiaHead&lt;/code&gt; 加到 &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; 的底部：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;!&lt;/span&gt;&lt;span&gt;DOCTYPE&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;head&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;@inertiaHead&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;head&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;@inertia&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;好了之後就可以來啟用 SSR 功能了！先發布 &lt;code&gt;config/inertia.php&lt;/code&gt; 設定檔到專案中：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;php&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;artisan&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;vendor:publish&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--provider=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Inertia\ServiceProvider&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;然後會看到以下設定，基本上可以先用預設的，如果要改設定可以在這邊改。&lt;code&gt;enabled&lt;/code&gt; 是一個單純的開關，&lt;code&gt;url&lt;/code&gt; 是 SSR server 的網址，&lt;code&gt;bundle&lt;/code&gt; 則是 &lt;code&gt;vite build --ssr&lt;/code&gt; 產生的檔案的位置：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;?&lt;/span&gt;&lt;span&gt;php&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;ssr&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;enabled&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;url&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;http://127.0.0.1:13714&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;// &apos;bundle&apos; =&amp;gt; base_path(&apos;bootstrap/ssr/ssr.mjs&apos;),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;畢竟編譯出來的檔案不要送進版控，當然要加到 &lt;code&gt;.gitignore&lt;/code&gt; 裡：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;/bootstrap/ssr&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;/node_modules&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;&lt;a href=&quot;#執行-ssr-應用&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;執行 SSR 應用&lt;/h2&gt;
&lt;p&gt;當以上步驟都做完之後，現在就可以來啟動 SSR 網站了，啟動基本的 Artisan Serve：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;php&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;artisan&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;serve&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;然後啟動 SSR 服務：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;php&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;artisan&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;inertia:start-ssr&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;如果在正式環境下，更新完程式碼會需要手動關閉可以執行：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;php&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;artisan&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;inertia:stop-ssr&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;現在就可以開瀏覽器看看啦~ 正常的話直接看是沒有差別的，這時候打開網頁原始碼，你就會看到差別所在。&lt;/p&gt;
&lt;p&gt;首先是沒有 SSR，單純的用戶端渲染，內容很單純：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://star-note-lucas.me/images/2022-01-12-inertia-ssr-source-csr.jpg&quot; alt=&quot;Inertia 用戶端渲染原始碼&quot; /&gt;&lt;/p&gt;
&lt;p&gt;然後是啟用 SSR 的服務端渲染，就會看到已經渲染好的 &lt;code&gt;&amp;lt;title&amp;gt;&lt;/code&gt; 以及 &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; 中的 HTML 了：&lt;/p&gt;
&lt;div&gt;&lt;div&gt;Tip&lt;/div&gt;&lt;div&gt;&lt;p&gt;可以把瀏覽器左上的 &lt;strong&gt;自動換行&lt;/strong&gt; 打開&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src=&quot;https://star-note-lucas.me/images/2022-01-12-inertia-ssr-source-ssr.jpg&quot; alt=&quot;Inertia 服務端渲染原始碼&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#結語&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;結語&lt;/h2&gt;
&lt;p&gt;其實在 2021 年 Inertia 的 SSR 功能早期釋出時我就已經體驗過了，不過當時還沒有把功能包進套件中，使用起來比較麻煩一些，現在正式釋出的版本終於可以比較容易使用了。&lt;/p&gt;
&lt;p&gt;在還沒出 SSR 功能之前，我都把 Inertia 定位成只能做後台操作的部分，需要 SEO 的頁面就只能用 blade 來頂替。SSR 功能的出現終於可以做完整的網頁功能了。可以把 Laravel 和 Vue 的功能發揮到極緻，這就是我喜歡 Inertia 的原因~ 期望之後 Inertia 可以完善更多功能，讓用 Laravel + Vue 來建立 Modern SPA 網站變得比較輕鬆~&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#參考資料&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;參考資料&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://inertiajs.com/server-side-rendering&quot;&gt;Server-side rendering (SSR) - Inertia.js&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>Tailwind CSS 小技巧之 scroll margin 篇</title><link>https://star-note-lucas.me/posts/css-scroll-margin</link><guid isPermaLink="true">https://star-note-lucas.me/posts/css-scroll-margin</guid><description>使用 CSS 的 scroll-margin 和 scroll-padding，可以解決在跳轉到次級標題時，卻因為上面留白不足，導致畫面頂部顯得緊湊，或是標題被導覽列擋住的問題。</description><pubDate>Fri, 01 Oct 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;CSS 的 &lt;code&gt;scroll-margin&lt;/code&gt; 和 &lt;code&gt;scroll-padding&lt;/code&gt; 解決了在跳轉到次級標題時，卻因為上面留白不足，導致畫面頂部顯得緊湊，或是標題被導覽列擋住的問題。而且 &lt;code&gt;scroll-margin&lt;/code&gt; 在 &lt;a href=&quot;https://caniuse.com/?search=scroll-margin&quot;&gt;caniuse 上的瀏覽器支援度&lt;/a&gt; 也已達到了 90% 左右了，當然，IE 是&lt;strong&gt;不能&lt;/strong&gt;用的喔！！&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#scroll-margin&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;scroll-margin&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;scroll-margin&lt;/code&gt; 屬性可以設定滾動時，元素相對於容器的外邊距。可以使用在像文章中的次級標題，在導覽到次級標題正常都會直接貼到最頂端，一點空間都不留。用上 &lt;code&gt;scroll-margin&lt;/code&gt; 就可以在跳轉到次級標題時增加一些邊距，留下適當的空間看起來也比較不會那麼緊湊。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://star-note-lucas.me/images/2021-10-01-css-scroll-margin-scroll-margin.jpg&quot; alt=&quot;scroll-margin&quot; /&gt;&lt;/p&gt;
&lt;p&gt;這裡看程式碼~ &lt;code&gt;scroll-margin&lt;/code&gt; 跟 &lt;code&gt;margin&lt;/code&gt; 一樣有 top/bottom/left/right 四個方向可以設定：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;h2&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;sub-title&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;style&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;scroll-margin-top: 16px&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;Sub title&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;h2&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;或是 Tailwind CSS 的使用方式，可以參考 &lt;code&gt;margin&lt;/code&gt; 的使用方式：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;h2&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;sub-title&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;scroll-mt-4&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;Sub title&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;h2&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;因為現在 Tailwind 預設沒有增加 &lt;code&gt;scroll-margin&lt;/code&gt; 相關的 utility class，所以我們要自己增加 CSS：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;layer&lt;/span&gt;&lt;span&gt; utilities {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;scroll-mt-4&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;scroll-margin-top&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;這樣就好啦！&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#scroll-padding&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;scroll-padding&lt;/h2&gt;
&lt;p&gt;而 &lt;code&gt;scroll-padding&lt;/code&gt; 則反之，是用在外層的容器元素，讓容器內部滾動時增加計算邊距。常見用法是頁面中有固定在頂部的導覽列時，跳轉到次要標題，卻被導覽列切掉了一半。因此 &lt;code&gt;scroll-padding&lt;/code&gt; 就可以設定滾動後容器本身要偏移計算的內距。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://star-note-lucas.me/images/2021-10-01-css-scroll-margin-scroll-padding.jpg&quot; alt=&quot;scroll-padding&quot; /&gt;&lt;/p&gt;
&lt;p&gt;使用方式如下 (也可以在 CSS 檔中設定)，一樣也有 top/bottom/left/right 四個方向：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;style&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;scroll-padding-top: 56px&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;header&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;navbar&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&amp;lt;!-- 頂部導覽列 --&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;header&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;或 Tailwind CSS 用法：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;scroll-pt-14&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;header&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;fixed ...&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&amp;lt;!-- 頂部導覽列 --&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;header&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;一樣 Tailwind 也要在 CSS 自訂 utility class：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;layer&lt;/span&gt;&lt;span&gt; utilities {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;scroll-pt-14&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;scroll-padding-top&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;3.5&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;不過你可能會覺得這樣在 Tailwind CSS 中設定比較麻煩 (雖然這些屬性通常是比較少使用)，這裡有消息要告訴你，在不久後的未來，Tailwind CSS 將會發布 3.0 的 Alpha 版🎉，其中會包含 &lt;code&gt;scroll-margin&lt;/code&gt;、&lt;code&gt;scroll-padding&lt;/code&gt;、&lt;code&gt;scroll-snap&lt;/code&gt; 等滾動相關的 utility class，而且 JIT 會變成預設模式，當然也會有其他的更新，讓我們拭目以待吧😍！&lt;/p&gt;</content:encoded></item><item><title>崩壞三 動畫短片「薪炎永燃」感想</title><link>https://star-note-lucas.me/posts/bh3rd-everlasting-flames</link><guid isPermaLink="true">https://star-note-lucas.me/posts/bh3rd-everlasting-flames</guid><pubDate>Sun, 11 Jul 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;前面剛開始的時候我還很平靜&lt;br /&gt;
想說沒什麼大不了的&lt;br /&gt;
就是個動畫短篇嘛…&lt;/p&gt;
&lt;p&gt;但到鴨鴨拚盡全力射出大砲後&lt;br /&gt;
此時，旋律起&lt;br /&gt;
琪亞娜跟鴨鴨並肩而戰&lt;/p&gt;
&lt;p&gt;到這裡時我的情感瞬間爆發&lt;br /&gt;
眼淚止不住&lt;br /&gt;
一直流&lt;br /&gt;
一直流&lt;/p&gt;
&lt;p&gt;動畫短篇「薪炎永燃」真的精彩&lt;br /&gt;
音樂也非常好聽&lt;br /&gt;
直到現在腦中都還是盤旋者 Moon Halo 的旋律&lt;br /&gt;
和琪亞娜奮戰時的身姿&lt;/p&gt;
&lt;p&gt;「即使這個世界依舊不完美，但還是要為世界上所有的美好而戰！」&lt;/p&gt;
&lt;div&gt;  &lt;/div&gt;</content:encoded></item><item><title>Windi CSS -  Tailwind CSS 的上位替代</title><link>https://star-note-lucas.me/posts/windicss</link><guid isPermaLink="true">https://star-note-lucas.me/posts/windicss</guid><description>Windi CSS 作為 Tailwind CSS 的上位替代，提供了更多酷炫的功能。</description><pubDate>Fri, 04 Jun 2021 00:00:00 GMT</pubDate><content:encoded>&lt;div&gt;&lt;div&gt;Warning&lt;/div&gt;&lt;div&gt;&lt;p&gt;Windi CSS 已經不再維護了，請使用 &lt;a href=&quot;https://unocss.dev/&quot;&gt;UnoCSS&lt;/a&gt; 來取代。&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href=&quot;https://windicss.org/&quot;&gt;Windi CSS&lt;/a&gt; 作為 Tailwind CSS 的上位替代，提供了更多酷炫的功能。比 Tailwind CSS &lt;strong&gt;更強大的任意值 class&lt;/strong&gt;、&lt;strong&gt;Variants 群組&lt;/strong&gt;，還可以&lt;strong&gt;在 DevTools 裡設計&lt;/strong&gt; (自動生成任意值 class~)。而且 Tailwind CSS 的 JIT 功能，是因為 Windi CSS 的關係才得以完成。這麼厲害的套件，下面我們一起來了解吧！&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#windi-css-的由來&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;Windi CSS 的由來&lt;/h2&gt;
&lt;p&gt;如果你以前都沒有聽過 Windi CSS，看到任意值 class 功能時，反應可能是：「你這不是抄 Tailwind 嗎？」。事實恰恰相反，按需加載任意值 class 的功能其實就是從 Windi CSS 出來的。&lt;/p&gt;
&lt;p&gt;事情要從 Tailwind CSS v1 時講起。雖然 Tailwind CSS 很強大，但為人所詬病的是編譯/HMR (熱更新) 的緩慢速度，當專案有幾十個組件時，初始編譯速度 3-5 秒起跳，熱更新更是超過 1 秒。為了解決這個問題，Windi CSS 應然而生。基於 TypeScript 實現了 Tailwind CSS 的功能，還可以按需加載 class，編譯和載入速度變得飛快！ (&lt;a href=&quot;https://twitter.com/antfu7/status/1361398324587163648&quot;&gt;Tailwind CSS v1 和 Windi CSS 載入速度對照影片&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;不過作者不是 Tailwind CSS 團隊的人，後來是 Tailwind CSS 的作者向 Windi CSS 團隊的人請教實現的細節，才完成了 v2 的 Just-in-Time 功能。&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#任意值-class-windi-css-版&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;任意值 class (Windi CSS 版)&lt;/h2&gt;
&lt;p&gt;在 Windi CSS 裡可以直接書寫值，不強迫加上 &lt;code&gt;[...]&lt;/code&gt;，當然你要加也可以，端看使用習慣：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;!-- 尺寸、定位 --&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;pt-0.8rem pb-[0.5rem] mx-10px&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;!-- 分數 --&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;w-9/12&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;!-- 顏色 --&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;bg-hex-b2a8bb&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;bg-[hsl(211.7,81.9%,69.6%)]&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;!-- 網格 (grid template) --&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;grid-cols-[auto,1fr,30px]&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;響應式部分，除了&lt;strong&gt;手機端優先&lt;/strong&gt;的斷點方式，Windi CSS 還多了&lt;strong&gt;大螢幕優先&lt;/strong&gt;和&lt;strong&gt;當前斷點&lt;/strong&gt;兩種斷點方式，定義斷點的彈性變大了：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;lg:p-1&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;lt;!-- 手機端優先 --&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;lg:p-2&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;lt;!-- 大螢幕優先 --&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;@lg:p-3&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;lt;!-- 當前斷點 --&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;以上 3 種斷點寫法分別會輸出以下 CSS：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;media&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;min-width&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;1024&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;lg&lt;/span&gt;&lt;span&gt;\:&lt;/span&gt;&lt;span&gt;p-1&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;padding&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;0.25&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;media&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;max-width&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;1023.9&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;\&amp;lt;&lt;/span&gt;&lt;span&gt;lg&lt;/span&gt;&lt;span&gt;\:&lt;/span&gt;&lt;span&gt;p-2&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;padding&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;0.5&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;media&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;min-width&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;1024&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;and&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;max-width&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;1279.9&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;\@&lt;/span&gt;&lt;span&gt;lg&lt;/span&gt;&lt;span&gt;\:&lt;/span&gt;&lt;span&gt;p-3&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;padding&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;0.75&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;&lt;a href=&quot;#variants-群組&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;Variants 群組&lt;/h2&gt;
&lt;p&gt;當 class 越寫越多，會發現 class 變得雜亂無章，這裡我們可以依照 Variants 來分組，變得相對簡潔且易讀，而且最後會編譯成 utility class：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;bg-white font-light hover:(bg-gray-400 font-medium) md:(text-indigo-100 bg-indigo-500) md:hover:(text-white bg-indigo-400)&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;&lt;a href=&quot;#在-devtools-裡設計&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;在 DevTools 裡設計&lt;/h2&gt;
&lt;p&gt;如果在原本 Tailwind CSS 中這樣開發還不算什麼稀有事情，畢竟所有的 class 都包在幾 MB 的 CSS 裡了，但代價就是拉垮的瀏覽器效能。在按需生成 class 的 Windi CSS 反而才覺得不可能，不過，&lt;strong&gt;它 做 到 了！&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;先看&lt;a href=&quot;https://twitter.com/antfu7/status/1372244287975387145&quot;&gt;影片示例&lt;/a&gt;~&lt;/p&gt;
&lt;p&gt;只要在 Vite 專案的入口檔案 (通常是 &lt;code&gt;main.js&lt;/code&gt;) 加上這串就可以使用了~&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;src/main.js&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;virtual:windi-devtools&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;&lt;a href=&quot;#attributify-mode&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;Attributify Mode&lt;/h2&gt;
&lt;div&gt;&lt;div&gt;Warning&lt;/div&gt;&lt;div&gt;&lt;p&gt;Attributify Mode 目前只能在 Vite 使用。&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;在 &lt;code&gt;windi.config.js&lt;/code&gt; 開啟：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;windi.config.js&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;attributify: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;開啟之後能幹嘛呢？先看原本落落長難以閱讀的 class：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;bg-blue-400 hover:bg-blue-500 text-sm text-white font-mono font-light py-2 px-4 rounded border-2 border-blue-200 dark:bg-blue-500 dark:hover:bg-blue-600&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;然後再看把 class 們變成用屬性的方式定義 (&lt;strong&gt;Attributify Mode&lt;/strong&gt;)，更加清楚整潔，而且只要看屬性馬上就可以知道定義了哪些 utility 功能，當然還是可以使用 &lt;code&gt;class&lt;/code&gt;：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;bg&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;blue-400 hover:blue-500 dark:blue-500 dark:hover:blue-600&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;text&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;sm white&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;font&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;mono light&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;y-2 x-4&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;border&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;2 rounded blue-200&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;&lt;a href=&quot;#vs-code-套件&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;VS Code 套件&lt;/h2&gt;
&lt;p&gt;Windi CSS 官方維護的 VS Code 智能提示套件 &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=voorjaar.windicss-intellisense&quot;&gt;WindiCSS IntelliSense&lt;/a&gt;，用 Windi CSS 基本上就是必備。&lt;/p&gt;
&lt;p&gt;但我裝了之後遇到一個小衝突 &lt;em&gt;(已經有人提 issue 了，而且說是小衝突但我也被卡了半小時…)&lt;/em&gt;，在安裝了 &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=formulahendry.auto-rename-tag&quot;&gt;Auto Rename Tag&lt;/a&gt; 和專案有 Windi CSS 的設定檔時，會無法運行 &lt;strong&gt;WindiCSS IntelliSense&lt;/strong&gt;，現在解方是在專案中禁用 &lt;strong&gt;Auto Rename Tag&lt;/strong&gt; 就正常了。&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#結論&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;結論&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Tailwind CSS&lt;/strong&gt; 發展到現在已經是一個大專案，4000 左右個 commits 和 170 多位貢獻者，依賴於 PostCSS 已然是不可變動，而且想要貢獻一些實驗性功能也不會那麼容易，因此作者才會自己用 &lt;strong&gt;TypeScript&lt;/strong&gt; 開發出 &lt;strong&gt;Windi CSS&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;如果你的專案還是依賴 &lt;strong&gt;PostCSS&lt;/strong&gt; 的話請繼續用 &lt;strong&gt;Tailwind CSS&lt;/strong&gt; &lt;em&gt;(Windi CSS 不建議用在 PostCSS)&lt;/em&gt;，而如果使用 &lt;strong&gt;Vite&lt;/strong&gt;、&lt;strong&gt;Rollup&lt;/strong&gt;、&lt;strong&gt;Webpack&lt;/strong&gt; 等技術，同時對上述新潮功能有興趣，不妨試試 &lt;a href=&quot;https://windicss.org/&quot;&gt;Windi CSS&lt;/a&gt;，官方也有 &lt;a href=&quot;https://windicss.org/guide/migration.html&quot;&gt;Tailwind CSS 轉換指南&lt;/a&gt;。而且 Windi CSS 是用 &lt;strong&gt;TypeScript&lt;/strong&gt; 開發，有用 TS 的專案相性非常好。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;Info&lt;/div&gt;&lt;div&gt;&lt;p&gt;當然 Windi CSS 的功能不只上面那些，我只是選了幾個我覺得很強的功能來分享給大家~&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;但同時也要記住，&lt;strong&gt;Windi CSS&lt;/strong&gt; 的出現也是因為有 &lt;strong&gt;Tailwind CSS&lt;/strong&gt;，兩者是無法從真正意義上爭論誰勝於誰的。最重要的是，選擇一個讓自己舒適的開發工具！&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#參考資料&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;參考資料&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://windicss.org/&quot;&gt;Windi CSS 官網&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>快速了解 Tailwind CSS 的 JIT 模式</title><link>https://star-note-lucas.me/posts/tailwindcss-jit</link><guid isPermaLink="true">https://star-note-lucas.me/posts/tailwindcss-jit</guid><description>Tailwind CSS 2.0 正式上線的 Just-in-Time (簡稱 JIT) 編譯器，可以在寫 HTML 時及時編譯 CSS，大幅縮短編譯時間，和縮小產生的資產大小。</description><pubDate>Mon, 24 May 2021 00:00:00 GMT</pubDate><content:encoded>&lt;div&gt;&lt;div&gt;Info&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;a href=&quot;https://hiskio.com/courses/620/about?s=tc&quot;&gt;切版技術再升級！一課覆蓋 TailwindCSS + Vue3｜打造後台管理頁面 UI&lt;/a&gt; - 課程開播中~&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div&gt;&lt;div&gt;Tip&lt;/div&gt;&lt;div&gt;&lt;p&gt;線上範例：&lt;a href=&quot;https://play.tailwindcss.com/oOiqoowOoM&quot;&gt;JIT 線上範例 - Tailwind Play&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;因為 Tailwind CSS 的設計原理，在開發時會先幫你產生好超多的 class，CSS 檔最少 &lt;strong&gt;3 MB&lt;/strong&gt; 起跳，而且預設還只有開 1-2個 Variants。但現實往往沒有那麼單純，開發下去需要的 Variants 只會越來越多，開發中產生的 CSS 檔長到 10-20 MB 都是可能的，還會連帶拖慢 Chrome  載入的時間。&lt;/p&gt;
&lt;p&gt;這時候 JIT 模式就派上用場啦！Tailwind CSS 2.0 正式上線的 &lt;strong&gt;Just-in-Time&lt;/strong&gt; (簡稱 &lt;strong&gt;JIT&lt;/strong&gt;) 編譯器，可以在寫 HTML 時及時編譯 CSS，大幅縮短編譯時間，和縮小產生的資產大小。&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#just-in-time-模式的新功能&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;Just-in-Time 模式的新功能&lt;/h2&gt;
&lt;p&gt;廢話不說，馬上來看它可以做什麼：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;超快速的編譯時間&lt;/strong&gt;。原本 Tailwind CLI 編譯需要 3-8 秒，在 &lt;strong&gt;JIT 模式&lt;/strong&gt; 下僅需 &lt;strong&gt;0.8 秒&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;直接使用任意 Variants&lt;/strong&gt;。不用再煩惱需不需要開 &lt;code&gt;active&lt;/code&gt;、&lt;code&gt;focus&lt;/code&gt; 或 &lt;code&gt;disabled&lt;/code&gt; 等。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;任意值 CSS class&lt;/strong&gt;。可以直接在 HTML 裡寫像 &lt;code&gt;top-[247px]&lt;/code&gt; 這樣的 class，將會自動生成。而且也可以使用 Variants：&lt;code&gt;lg:top-[587px]&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;在 develop 和 production 產生一樣的 CSS&lt;/strong&gt;。不需要煩惱上線後會不會有 class 靈異的消失~&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;在開發時有更好的瀏覽器效能&lt;/strong&gt;。因為 develop 和 production 的 CSS 大小一樣小，本地預覽時不會出現 10-20 MB 的 CSS 檔案，開啟相關開發工具也不會載入很久。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;&lt;a href=&quot;#啟用-jit-模式&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;啟用 JIT 模式&lt;/h2&gt;
&lt;p&gt;要開啟非常簡單，只需要在 &lt;code&gt;tailwind.config.js&lt;/code&gt; 裡把 &lt;code&gt;mode&lt;/code&gt; 設成 &lt;code&gt;&apos;jit&apos;&lt;/code&gt; 就行了：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;module&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;exports&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;mode: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;jit&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;theme: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;// ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;// ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;但因為 JIT 模式在開發時就只會打包需要的 class，所以也要配置 &lt;code&gt;purge&lt;/code&gt; 的部分：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;module&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;exports&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;mode: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;jit&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;purge: [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;./public/**/*.html&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;./src/**/*.{js,jsx,ts,tsx,vue}&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;theme: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;// ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;// ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;&lt;a href=&quot;#新功能&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;新功能&lt;/h2&gt;
&lt;p&gt;然後就可以開始玩功能了！&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;#直接使用任意-variants&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;直接使用任意 Variants&lt;/h3&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;input&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;active:bg-indigo-200&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;直接在 HTML 寫就可以了，不用在 &lt;code&gt;tailwind.config.js&lt;/code&gt; 做設定，方便許多了。其他的 &lt;code&gt;focus-visible&lt;/code&gt;、&lt;code&gt;disabled&lt;/code&gt;、&lt;code&gt;even&lt;/code&gt; 等都可以用喔！&lt;/p&gt;
&lt;p&gt;當然，還支援各種疊加的用法，像這樣 XD：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;md:dark:disabled:focus:hover:bg-gray-300&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3&gt;&lt;a href=&quot;#任意值-class&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;任意值 class&lt;/h3&gt;
&lt;p&gt;如果網站是照著設計稿刻出來的，這個功能絕對是必備。使用任意值 class 很簡單，把你想要指定的值用 &lt;code&gt;[...]&lt;/code&gt; 框起來，比如 &lt;code&gt;h-[100px]&lt;/code&gt; 可以設定高度為 &lt;code&gt;100px&lt;/code&gt;。但要注意的是不能濫用，否則 Tailwind CSS 的設計系統就失去了原本的優勢了。&lt;/p&gt;
&lt;p&gt;首先是萬惡的圖片，絕對定位+圖片寬高，現在終於都可以在 HTML 裡搞定！：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;img&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;absolute w-[165px] h-[110px] top-[-125px] left-[62px] md:top-[-30px] md:left-[180px]&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;src&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;/background-image.png&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;如果有嵌入 LINE 或 Twitter 的按鈕時，LINE/Twitter 的商標顏色也不用增加到顏色系統了，因為只會用這麼一次呀：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;bg-[#00b900]&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;加入 LINE 好友&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;或者是更客製的網格設定：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;grid-cols-[1fr,700px,2fr]&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3&gt;&lt;a href=&quot;#important-修飾符&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;!important 修飾符&lt;/h3&gt;
&lt;p&gt;還有 &lt;code&gt;!important&lt;/code&gt; 也可以使用，在特殊狀況時會用到：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;!font-bold&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;!IMPORTANT BOLD TITLE&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;如果加上 Variants 時，&lt;code&gt;!&lt;/code&gt; 要跟著樣式名字，而不是加在最前面：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;sm:hover:!font-bold&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;&lt;a href=&quot;#jit-模式的限制&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;JIT 模式的限制&lt;/h2&gt;
&lt;p&gt;當然 JIT 模式不是萬能的，它也有一些使用上的限制，請往下看…&lt;/p&gt;
&lt;p&gt;需要記住一件事情，Tailwind CSS 是靜態提取 class 的，寫 class 務必要寫完整，不然 Tailwind CSS 會無法正確的打包 CSS。&lt;/p&gt;
&lt;p&gt;❌ 這樣是不行的呦：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;`mt-[${size === &apos;lg&apos; ? &apos;22px&apos; : &apos;17px&apos; }]`&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;✅ 動態選擇完整的 class：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;size === &apos;lg&apos; ? &apos;mt-[22px]&apos; : &apos;mt-[17px]&apos;&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;但如果真的要寫完全動態的 class 的話，只能改用 Inline-style 或 CSS-in-JS 的套件了。&lt;/p&gt;
&lt;p&gt;❌ 這樣是不行的呦：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;`bg-[${userThemeColor}]`&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;✅ 改用 Inline-style&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:style&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;`background-color: ${userThemeColor}`&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;還有，JIT 模式是沒有使用 PurgeCSS，所以 PurgeCSS 的設定都不能用。不過 &lt;a href=&quot;https://github.com/tailwindlabs/tailwindcss/releases/tag/v2.2.0#built-in-safelist-transform-and-extract-support&quot;&gt;v2.2 更新後&lt;/a&gt; 增加了 &lt;code&gt;safelist&lt;/code&gt; 的選項，所以現在 JIT 模式下也可以正常使用 &lt;code&gt;safelist&lt;/code&gt; 囉！(記得要寫下完整的 class)&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;tailwind.config.js&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;module&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;exports&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;mode: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;jit&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;purge: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;content: [&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;./src/**/*.html&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;safelist: [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;bg-blue-500&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;text-center&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;hover:opacity-100&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;// ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;lg:text-right&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;&lt;a href=&quot;#結語&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;結語&lt;/h2&gt;
&lt;p&gt;結論，JIT 模式太香了！速度快不說，還有這麼多新功能，推薦新專案都要啟用，真的爽到起飛！！！&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#線上體驗&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;線上體驗&lt;/h2&gt;
&lt;p&gt;啊！別忘了來用用看 Tailwind Play，可以在本篇的範例體驗看看唷：&lt;a href=&quot;https://play.tailwindcss.com/oOiqoowOoM&quot;&gt;JIT 線上範例 - Tailwind Play&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#參考資料&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;參考資料&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://tailwindcss.com/docs/just-in-time-mode&quot;&gt;Just-in-Time Mode - Tailwind CSS&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;&lt;a href=&quot;#課程資訊&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;課程資訊&lt;/h2&gt;
&lt;p&gt;我和 HiSIO 合作開設了一堂&lt;a href=&quot;https://hiskio.com/courses/620/about?s=tc&quot;&gt;結合 Tailwind CSS + Vue3 的進階課程&lt;/a&gt;，透過一步步講解以及實作，帶大家整合使用 Tailwind CSS 和 Vue 3 來完成一個部落格的後台 UI。實作功能包含文章列表、編輯表單、富文本編輯器、用戶資料設定等，並教大家使用快速啟動又新潮的打包器 Vite 作為開發和編譯工具。一起來加入課程，提升切版技術吧！&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://hiskio.com/courses/620/about?s=tc&quot;&gt;&lt;img src=&quot;https://lucas-hiskio-2021-tailwindcss-slide.vercel.app/thumbnail.png&quot; alt=&quot;https://hiskio.com/courses/620/about?s=tc&quot; /&gt;&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title>【崩壞3MMD】要來和希兒玩捉迷藏嗎？ - 魘夜星淵|孑然妒火</title><link>https://star-note-lucas.me/posts/mmd-seele</link><guid isPermaLink="true">https://star-note-lucas.me/posts/mmd-seele</guid><description>這個月出了新的希兒裝甲「魘夜星淵」，我也做了 MMD，從5月初忙忙碌碌到快5月底才趕出來...</description><pubDate>Sun, 23 May 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;這個月出了新的希兒裝甲「魘夜星淵」，我也做了 MMD，從5月初忙忙碌碌到快5月底才趕出來，同時也覺得，我的顯卡該換了…，出個4分鐘不到的影片，要跑2小時，好久啊！！！！！&lt;/p&gt;
&lt;p&gt;還有，希兒最~~~可愛了！！！&lt;/p&gt;
&lt;div&gt;  &lt;/div&gt;
&lt;hr /&gt;
&lt;p&gt;借物表：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;模型：魘夜星淵-神帝宇 miHoYo&lt;/li&gt;
&lt;li&gt;動作：FlyingSpirits-P&lt;/li&gt;
&lt;li&gt;表情：KAZUSA/天使の潤&lt;/li&gt;
&lt;li&gt;鏡頭：乱入君/天使の潤&lt;/li&gt;
&lt;li&gt;場景：溯北P&lt;/li&gt;
&lt;li&gt;BGM：孑然妒火 (独りんぼエンヴィー) - 天神子兎音&lt;/li&gt;
&lt;li&gt;渲染：Ray MMD by Rui_cg/Toon_Shader_Material by RedialC&lt;/li&gt;
&lt;li&gt;MME：Diffusion7/XDOF/ExcellentShadow&lt;/li&gt;
&lt;li&gt;字幕翻譯：kyroslee&lt;/li&gt;
&lt;li&gt;字幕字型：ピグモ 00&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>簽章 Git 提交</title><link>https://star-note-lucas.me/posts/git-gpg</link><guid isPermaLink="true">https://star-note-lucas.me/posts/git-gpg</guid><description>對 Git 提交進行 GPG 簽章，確保是由本人提交，而非被竄改或仿冒提交。</description><pubDate>Thu, 08 Apr 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;GPG 全名為 &lt;a href=&quot;https://gnupg.org/&quot;&gt;The GNU Privacy Guard&lt;/a&gt;，又稱 &lt;a href=&quot;https://gnupg.org/&quot;&gt;GnuPG&lt;/a&gt;，可以用來對訊息或檔案進行加密(encrypt)或簽章(sign)。且可以和 Git 整合，用來簽章 Git 的提交內容，確保是由本人提交，而非被竄改或仿冒提交。&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#安裝-gpg&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;安裝 GPG&lt;/h2&gt;
&lt;h3&gt;&lt;a href=&quot;#windows&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;Windows&lt;/h3&gt;
&lt;p&gt;Windows 環境中現在在 Git Bash 已經內建了 GPG，安裝 Git 的時候就會安裝好 GPG 了。&lt;/p&gt;
&lt;p&gt;不過也可以獨立安裝 &lt;a href=&quot;https://www.gpg4win.org/&quot;&gt;Gpg4win&lt;/a&gt;，還附有一個 GUI 介面的 金鑰管理員(Kleopatra) 與 Outlook 外掛程式，如果你需要還是可以安裝的。&lt;/p&gt;
&lt;p&gt;雖然可以不用安裝 gpg，但還是需要設定 Git 的 gpg.program 選項設定，指定 gpg.exe 所在路徑：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;config&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--global&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;gpg.program&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;C:\Program Files\Git\usr\bin\gpg.exe&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3&gt;&lt;a href=&quot;#bash&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;Bash&lt;/h3&gt;
&lt;p&gt;如果有使用 Bash，需要增加以下指令到 &lt;code&gt;~/.bashrc&lt;/code&gt; 檔案中：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# GPG Agent setup for Git commit signing&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;GPG_TTY&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;span&gt;tty&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gpgconf&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--launch&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;gpg-agent&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;否則在執行 gpg 簽章時會發生以下錯誤：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;error: gpg failed to sign the data&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;fatal: failed to write commit object&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;&lt;a href=&quot;#gpg-使用方式&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;GPG 使用方式&lt;/h2&gt;
&lt;p&gt;安裝好之後就可以開始使用了。&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;#建立-gpg-金鑰組&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;建立 GPG 金鑰組&lt;/h3&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gpg&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--full-generate-key&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;建立過程中，需要輸入以下：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Please select what kind of key you want: 輸入 &lt;code&gt;Enter&lt;/code&gt; 選預設值 &lt;code&gt;(9) ECC (sign and encrypt)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Please select which elliptic curve you want: 輸入 &lt;code&gt;Enter&lt;/code&gt; 選預設值 &lt;code&gt;(1) Curve 25519&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Please specify how long the key should be valid: 輸入 &lt;code&gt;2y&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;輸入你的 GitHub 的名稱和 E-mail&lt;/li&gt;
&lt;li&gt;輸入 &lt;code&gt;O&lt;/code&gt; (即 OK)&lt;/li&gt;
&lt;li&gt;設定密碼&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;看到如下畫面就是建立成功了：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gpg: revocation certificate stored as &apos;/c/Users/Lucas/.gnupg/openpgp-revocs.d\7144F0FE70DA28DE2428982FAFA8022C0E4062EF.rev&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;public and secret key created and signed.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;pub   ed25519 2021-04-08 [SC] [expires: 2023-04-08]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;DC279878969CA746899B02FFAE57F5023B001577&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;uid                      Lucas Yang &amp;lt;yangchenshin77@gmail.com&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sub   cv25519 2021-04-08 [E] [expires: 2023-04-08]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3&gt;&lt;a href=&quot;#查詢-gpg-金鑰組&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;查詢 GPG 金鑰組&lt;/h3&gt;
&lt;p&gt;列出公開金鑰資訊：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gpg&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--list-keys&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gpg&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--list-keys&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--keyid-format&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;SHORT&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gpg&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--list-keys&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--keyid-format&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;LONG&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;列出私密金鑰資訊：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gpg&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--list-secret-keys&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gpg&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--list-secret-keys&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--keyid-format&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;SHORT&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gpg&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--list-secret-keys&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--keyid-format&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;LONG&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;如果我們要簽章 Git 提交，就必須先取得金鑰 ID：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gpg&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--list-secret-keys&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--keyid-format&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;SHORT&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;[keyboxd]&lt;/div&gt;&lt;div&gt;---------&lt;/div&gt;&lt;div&gt;sec   rsa4096/0E4062EF 2021-04-08 [SC]&lt;/div&gt;&lt;div&gt;      7144F0FE70DA28DE2428982FAFA8022C0E4062EF&lt;/div&gt;&lt;div&gt;uid         [ultimate] Lucas Yang &amp;lt;yangchenshin77@gmail.com&amp;gt;&lt;/div&gt;&lt;div&gt;ssb   cv25519/5F50EFE6 2021-04-08 [E]&lt;/div&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;上述訊息中，&lt;code&gt;sec&lt;/code&gt; 就是 &lt;code&gt;secret&lt;/code&gt; (私密金鑰)，而 &lt;code&gt;7144F0FE70DA28DE2428982FAFA8022C0E4062EF&lt;/code&gt; 就是完整的金鑰 ID，可以用簡短的金鑰代替，也就是 &lt;code&gt;0E4062EF&lt;/code&gt;，之後的操作都會用到這段 ID。&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;#設定-git-提交進行-gpg-簽章&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;設定 Git 提交進行 GPG 簽章&lt;/h3&gt;
&lt;p&gt;設定預設的 GPG 金鑰：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;config&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--global&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;user.signingkey&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0E4062EF&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;然後就可以在 Git 提交 (commit) 或標籤 (tag) 時進行 GPG 簽章：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;commit&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-S&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tag&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-s&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;當然也可以設定自動簽章：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;config&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--global&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;commit.gpgSign&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;config&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--global&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tag.gpgSign&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3&gt;&lt;a href=&quot;#顯示-git-簽章資訊&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;顯示 Git 簽章資訊&lt;/h3&gt;
&lt;p&gt;顯示含有簽章資訊的 Git 的指令：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--show-signature&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;commit aa993b7b69b47a6bf9d23cacf56e00139f3907a0 (HEAD -&amp;gt; main, origin/main)&lt;/div&gt;&lt;div&gt;gpg: Signature made 2021/4/8 &amp;lt;A4&amp;gt;U&amp;lt;A4&amp;gt;&amp;lt;C8&amp;gt; 04:54:57 &amp;lt;A5&amp;gt;x&amp;lt;A5&amp;gt;_&amp;lt;BC&amp;gt;зǮɶ&amp;lt;A1&amp;gt;&lt;/div&gt;&lt;div&gt;gpg:                using RSA key CB8E694EB798BFED6FA1C2DC88D3F62D215D5306&lt;/div&gt;&lt;div&gt;gpg: Good signature from &quot;Lucas Yang &amp;lt;yangchenshin77@gmail.com&amp;gt;&quot; [ultimate]&lt;/div&gt;&lt;div&gt;Author: Lucas Yang &amp;lt;yangchenshin77@gmail.com&amp;gt;&lt;/div&gt;&lt;div&gt;Date:   Thu Apr 8 16:54:57 2021 +0800&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;    Formatting&lt;/div&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3&gt;&lt;a href=&quot;#設定-github-帳號的-gpg-金鑰&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;設定 GitHub 帳號的 GPG 金鑰&lt;/h3&gt;
&lt;p&gt;直接將 GPG 公鑰輸出在「命令提示字元」中：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gpg&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--armor&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0E4062EF&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;並複製公鑰，到 &lt;a href=&quot;https://github.com/settings/keys&quot;&gt;GitHub 的 SSH and GPG keys 設定&lt;/a&gt;，新增一個 GPG 金鑰。&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;#設定-vs-code&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;設定 VS Code&lt;/h3&gt;
&lt;p&gt;在 VS Code 增加 &lt;code&gt;git.enableCommitSigning&lt;/code&gt;，之後 VS Code 提交 Git commit 時都會加上簽章：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;settings.json&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;git.enableCommitSigning&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3&gt;&lt;a href=&quot;#遷移-gpg-金鑰組到其他電腦&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;遷移 GPG 金鑰組到其他電腦&lt;/h3&gt;
&lt;p&gt;匯出公鑰：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gpg&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--armor&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0E4062EF&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;gpg-pub.asc&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;匯出私鑰，這裡應該會提示輸入密碼：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gpg&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--armor&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--export-secret-keys&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0E4062EF&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;gpg-sc.asc&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;確認一下公鑰和私鑰：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ls&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-l&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;gpg&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt;.asc&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;然後把金鑰組移到新電腦上。到新電腦之後就開始匯入的操作：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gpg&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--import&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;gpg-pub.asc&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;匯入私鑰，這裡應該會提示輸入密碼：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gpg&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--import&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;gpg-sc.asc&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;最後記得要在舊電腦上刪除乾淨金鑰組。&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;#刪除-gpg-金鑰組&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;刪除 GPG 金鑰組&lt;/h3&gt;
&lt;p&gt;如果公鑰私鑰都在同一台電腦，必須先刪除私鑰，才能刪除相對應的公鑰。也可以直接刪除私鑰就好，公鑰保留。&lt;/p&gt;
&lt;p&gt;刪除私鑰：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gpg&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--delete-secret-key&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0E4062EF&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;刪除公鑰：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gpg&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--delete-key&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0E4062EF&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;&lt;a href=&quot;#延長-gpg-金鑰組的有效期限&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;延長 GPG 金鑰組的有效期限&lt;/h2&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gpg&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--edit-key&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0E4062EF&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;進入互動介面後，輸入 &lt;code&gt;expire&lt;/code&gt;，然後選擇新的有效期限比如 &lt;code&gt;2y&lt;/code&gt;，最後輸入 &lt;code&gt;save&lt;/code&gt; 就完成了。&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#參考資料&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;參考資料&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.miniasp.com/post/2020/05/04/How-to-use-GPG-sign-git-commit-and-tag-object&quot;&gt;如何使用 GPG (GnuPG) 對 Git Commit 與 Tag 進行簽章&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://withblue.ink/2020/05/17/how-and-why-to-sign-git-commits.html&quot;&gt;How (and why) to sign Git commits&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>【崩壞3MMD】帥氣駭兔，在線熱舞！ [布朗尼‖擬劇論]</title><link>https://star-note-lucas.me/posts/mmd-bronnie</link><guid isPermaLink="true">https://star-note-lucas.me/posts/mmd-bronnie</guid><description>用4.7版登場的迷城駭兔-布朗尼，配上 hanser 翻唱的擬劇論，真的太讚了！</description><pubDate>Sun, 28 Mar 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;第一次做 MMD，花了4天研究，終於變出來了。&lt;/p&gt;
&lt;p&gt;用4.7版登場的迷城駭兔-布朗尼，配上 hanser 翻唱的擬劇論，真的太讚了！&lt;/p&gt;
&lt;p&gt;布朗尼：“想搞波大的嗎？老子罩著你”
艦長：“啊~~好帥的鴨鴨”&lt;/p&gt;
&lt;div&gt;  &lt;/div&gt;
&lt;hr /&gt;
&lt;p&gt;借物表：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;模型：迷城駭兔-神帝宇 miHoYo&lt;/li&gt;
&lt;li&gt;動作：moka&lt;/li&gt;
&lt;li&gt;鏡頭：ゆきねこ&lt;/li&gt;
&lt;li&gt;場景：怪獣対若大將P&lt;/li&gt;
&lt;li&gt;BGM：擬劇論（ドラマツルギー）（翻自 Eve） - hanser&lt;/li&gt;
&lt;li&gt;渲染：Ray MMD by Rui_cg，Toon_Shader_Material Ver1.3 by RedialC&lt;/li&gt;
&lt;li&gt;MME：Diffusion7，SvSSAO，SvDOF2，MMD-ColorGray&lt;/li&gt;
&lt;li&gt;Tools：MMD，小丸工具箱月兒版&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>使用本地 phpMyAdmin 連線到遠端 MySQL 資料庫</title><link>https://star-note-lucas.me/posts/local-phpmyadmin-connect-to-remote-mysql</link><guid isPermaLink="true">https://star-note-lucas.me/posts/local-phpmyadmin-connect-to-remote-mysql</guid><description>為了要管理遠端 MySQL 資料庫，還要在遠端主機安裝 phpMyAdmin 且不安全...</description><pubDate>Wed, 23 Dec 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;為了要管理遠端 MySQL 資料庫，還要在遠端主機安裝 phpMyAdmin 且不安全。本文分享如何在本地用 phpMyAdmin 連線到遠端 MySQL 資料庫。&lt;/p&gt;
&lt;p&gt;首先先確保有設定好 &lt;a href=&quot;https://star-note-lucas.me/posts/ssh-login-without-password&quot;&gt;SSH 無密碼登入&lt;/a&gt;，讓 SSH 連線可以掛在後台運行。&lt;/p&gt;
&lt;p&gt;然後執行以下 SSH 指令連線到遠端主機，使用此方式連線會將本地的 3307 port 轉發到遠端主機的 3306 port：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ssh&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-fNL&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;3307:localhost:3306&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;root@REMOTE_HOST&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;div&gt;Tip&lt;/div&gt;&lt;div&gt;&lt;p&gt;將 &lt;code&gt;root@REMOTE_HOST&lt;/code&gt; 替換為實際連線的主機名稱&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;然後確認該 SSH 連線是否已經成功掛在後台運行：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ps&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;aux&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;grep&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ssh&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;現在可以來配置 phpMyAdmin 的設定：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;vim&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/etc/phpmyadmin/config.inc.php&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;把以下設定貼到 &lt;code&gt;Servers&lt;/code&gt; 設定的底部：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;config.inc.php&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;$cfg&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Servers&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;][&lt;/span&gt;&lt;span&gt;$i&lt;/span&gt;&lt;span&gt;][&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;verbose&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;]       &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Local&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;$cfg&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Servers&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;][&lt;/span&gt;&lt;span&gt;$i&lt;/span&gt;&lt;span&gt;][&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;host&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;]          &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;localhost&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;$cfg&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Servers&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;][&lt;/span&gt;&lt;span&gt;$i&lt;/span&gt;&lt;span&gt;][&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;port&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;]          &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;3306&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;$cfg&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Servers&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;][&lt;/span&gt;&lt;span&gt;$i&lt;/span&gt;&lt;span&gt;][&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;connect_type&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;]  &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;tcp&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;$cfg&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Servers&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;][&lt;/span&gt;&lt;span&gt;$i&lt;/span&gt;&lt;span&gt;][&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;extension&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;]     &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;mysqli&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;$cfg&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Servers&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;][&lt;/span&gt;&lt;span&gt;$i&lt;/span&gt;&lt;span&gt;][&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;compress&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;]      &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;FALSE&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;$cfg&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Servers&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;][&lt;/span&gt;&lt;span&gt;$i&lt;/span&gt;&lt;span&gt;][&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;auth_type&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;]     &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;cookie&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;$i&lt;/span&gt;&lt;span&gt;++&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;$cfg&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Servers&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;][&lt;/span&gt;&lt;span&gt;$i&lt;/span&gt;&lt;span&gt;][&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;verbose&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;]       &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Remote Server 1&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;; &lt;/span&gt;&lt;span&gt;// Your Remote Server Name&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;$cfg&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Servers&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;][&lt;/span&gt;&lt;span&gt;$i&lt;/span&gt;&lt;span&gt;][&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;host&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;]          &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;127.0.0.1&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;$cfg&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Servers&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;][&lt;/span&gt;&lt;span&gt;$i&lt;/span&gt;&lt;span&gt;][&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;port&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;]          &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;3307&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;$cfg&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Servers&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;][&lt;/span&gt;&lt;span&gt;$i&lt;/span&gt;&lt;span&gt;][&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;connect_type&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;]  &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;tcp&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;$cfg&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Servers&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;][&lt;/span&gt;&lt;span&gt;$i&lt;/span&gt;&lt;span&gt;][&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;extension&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;]     &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;mysqli&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;$cfg&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Servers&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;][&lt;/span&gt;&lt;span&gt;$i&lt;/span&gt;&lt;span&gt;][&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;compress&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;]      &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;FALSE&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;$cfg&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Servers&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;][&lt;/span&gt;&lt;span&gt;$i&lt;/span&gt;&lt;span&gt;][&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;auth_type&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;]     &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;cookie&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;$i&lt;/span&gt;&lt;span&gt;++&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;正常的話現在應該就可以用 phpMyAdmin 連線到遠端主機了。但如果使用 MySQL 8 在連線時，應該會遇到一個錯誤：&lt;/p&gt;
&lt;div&gt;&lt;div&gt;Danger&lt;/div&gt;&lt;div&gt;&lt;p&gt;The server requested authentication method unknown to the client&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;當然還是有解方。先用 &lt;code&gt;root&lt;/code&gt; 帳號登入 MySQL (CLI)，然後執行以下：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ALTER&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;USER&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;root&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;localhost&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; IDENTIFIED &lt;/span&gt;&lt;span&gt;WITH&lt;/span&gt;&lt;span&gt; mysql_native_password &lt;/span&gt;&lt;span&gt;BY&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;password&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;div&gt;Tip&lt;/div&gt;&lt;div&gt;&lt;p&gt;把 &lt;code&gt;password&lt;/code&gt; 改成你的 MySQL &lt;code&gt;root&lt;/code&gt; 帳號的密碼&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;現在終於可以舒舒服服地用 phpMyAdmin 囉！&lt;/p&gt;
&lt;p&gt;啊！如果要移掉 3307 port 的話，可以執行這個指令：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;fuser&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-n&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tcp&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-k&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;3307&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;&lt;a href=&quot;#參考資料&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;參考資料&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://degreesofzero.com/article/manage-remote-mysql-servers-with-local-phpmyadmin.html&quot;&gt;Manage Remote MySQL Servers with Local phpMyAdmin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/52364415/php-with-mysql-8-0-error-the-server-requested-authentication-method-unknown-to/53881212#53881212&quot;&gt;PHP with MySQL 8.0+ error: The server requested authentication method unknown to the client - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>啟動 SSH 密碼登入 &amp; 設定 SSH 無密碼登入</title><link>https://star-note-lucas.me/posts/ssh-login-without-password</link><guid isPermaLink="true">https://star-note-lucas.me/posts/ssh-login-without-password</guid><description>啟動 SSH 密碼登入 &amp; 設定 SSH 無密碼登入</description><pubDate>Sat, 14 Nov 2020 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;&lt;a href=&quot;#啟動-ssh-密碼登入&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;啟動 SSH 密碼登入&lt;/h2&gt;
&lt;p&gt;DigitalOcean 的 SSH 登入設定我一開始沒設好，沒辦法直接用 SSH 連線，結果只能用 DigitalOcean 自帶的古怪 Web Console 連，還不能複製文字，反正就是超級難用。想改成用密碼登入時卻回 &lt;code&gt;Permission denied&lt;/code&gt;，沒法正常的使用 SSH。&lt;/p&gt;
&lt;p&gt;好在至少還有 Web Console 可以用，用 &lt;code&gt;root&lt;/code&gt; 連線後修改 &lt;code&gt;sshd_config&lt;/code&gt; 檔：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;vim&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/etc/ssh/sshd_config&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;把 &lt;code&gt;PasswordAuthentication&lt;/code&gt; 的 &lt;code&gt;no&lt;/code&gt; 改成 &lt;code&gt;yes&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;然後重新啟動 SSH 服務：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;service&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sshd&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;restart&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;現在就可以用密碼來連線做無密碼登入的設定了。&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#設定-ssh-無密碼登入&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;設定 SSH 無密碼登入&lt;/h2&gt;
&lt;p&gt;如果沒有 SSH 金鑰可以先生成：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ssh-keygen&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-t&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ed25519&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-C&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;your_email@example.com&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;然後使用 ssh-copy-id 複製公鑰到遠端主機 (還需要密碼)：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ssh-copy-id&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;root@host&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# 或指定其他金鑰&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ssh-copy-id&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-i&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~/.ssh/mykey&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;root@host&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;測試 SSH 連線，神奇似的不用了密碼：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ssh&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;root@host&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# 或指定其他金鑰&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ssh&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-i&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~/.ssh/mykey&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;root@host&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;檢查 &lt;code&gt;authorized_keys&lt;/code&gt; 會發現公鑰已經在裡面：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;cat&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~/.ssh/authorized_keys&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;記得把密碼登入的設定關掉啊！&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#參考資料&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;參考資料&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.github.com/en/free-pro-team@latest/github/authenticating-to-github/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent&quot;&gt;Generating a new SSH key and adding it to the ssh-agent&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://serverpilot.io/docs/how-to-enable-ssh-password-authentication/&quot;&gt;How to Enable SSH Password Authentication&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/%E4%B8%80%E5%80%8B%E4%BA%BA%E7%9A%84%E6%96%87%E8%97%9D%E5%BE%A9%E8%88%88/linode-ssh-key-permission-denied-publickey-%E5%95%8F%E9%A1%8C-5a58dc9fe7d2&quot;&gt;Linode SSH Key，Permission denied (publickey)問題&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>VuePress 部落格開發踩坑記</title><link>https://star-note-lucas.me/posts/new-vuepress-blog</link><guid isPermaLink="true">https://star-note-lucas.me/posts/new-vuepress-blog</guid><description>紀錄 VuePress + Tailwind CSS + Dark Mode 遇到的坑和解決方法</description><pubDate>Wed, 19 Aug 2020 00:00:00 GMT</pubDate><content:encoded>&lt;div&gt;&lt;div&gt;Warning&lt;/div&gt;&lt;div&gt;&lt;p&gt;這篇文章的內容已經過時了，請查閱 Vuepress 新版文檔使用。&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;新部落格上線，紀錄一下使用 &lt;strong&gt;VuePress&lt;/strong&gt; + &lt;strong&gt;Tailwind CSS&lt;/strong&gt; + &lt;strong&gt;Dark Mode&lt;/strong&gt; 開發時的小技巧和奇怪的坑。&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#vuepress-篇&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;VuePress 篇&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;VuePress&lt;/strong&gt;，以 &lt;strong&gt;Markdown&lt;/strong&gt; 為中心的 &lt;strong&gt;Vue.js&lt;/strong&gt; 靜態網站產生器，只要寫好 Markdown 文件和基本的設定一下，馬上就變出一個簡單的文檔網站了。但要客製化的話，就會遇到一些奇怪的坑：&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;#config-沒有更新&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;Config 沒有更新？&lt;/h3&gt;
&lt;p&gt;難道是 VuePre```js title=“ss 壞了？當然不是，只要改了 VuePress 都要重啟 VuePress，才會看到更新後的結果。如果有改 Markdown 上方的 frontmatter 最好也重啟。&lt;/p&gt;
&lt;p&gt;這個坑讓我一開始卡的超…久的，每次重啟都要浪費很多時間。本來想換以 &lt;strong&gt;Vite&lt;/strong&gt; 為基礎的 &lt;strong&gt;Vitepress&lt;/strong&gt;，本機 Dev Server 啟動超…快，更新超…快。但 Vitepress 目前尚在開發中，文檔也寫明不支持 VuePress 的生態系，也不會有&lt;strong&gt;套件 (Plugin)&lt;/strong&gt; 這東西存在，所有功能都要自己寫。出於以上原因，最後決定還是先用 VuePress。&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;#語言設定&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;語言設定&lt;/h3&gt;
&lt;p&gt;語言比較簡單，直接增加這段就好了：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;.vuepress/config.js&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;module&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;exports&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;// ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;locales: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;: { lang: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;zh-TW&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3&gt;&lt;a href=&quot;#vuepress-blog-plugin&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;VuePress Blog Plugin&lt;/h3&gt;
&lt;p&gt;看到官方有出建部落格的套件就馬上拿來用了，部落格都會有的文章區，可以使用套件的 &lt;strong&gt;Directory Classifier&lt;/strong&gt; 功能實現。先上我的 Blog Plugin 設定：&lt;/p&gt;
&lt;div&gt;&lt;div&gt;Info&lt;/div&gt;&lt;div&gt;&lt;p&gt;具體如何實現請看 &lt;a href=&quot;https://vuepress-plugin-blog.ulivz.com/guide/getting-started.html#directory-classifier&quot;&gt;VuePress Blog Plugin 文檔&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;.vuepress/config.js&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;module&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;exports&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;// ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;plugins: [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;@vuepress/blog&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;directories: [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;id: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;post&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;title: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;文章&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;dirname: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;post&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;layout: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;IndexPost&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;path: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;/post/&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;itemPermalink: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;/post/:slug&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;pagination: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;layout: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;IndexPost&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;lengthPerPage: &lt;/span&gt;&lt;span&gt;15&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;frontmatters: [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;id: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;tag&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;title: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;標籤&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;keys: [&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;tags&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;path: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;/tag/&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;layout: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Tags&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;scopeLayout: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Tag&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;我的文章檔名格式是 &lt;code&gt;2020-01-01-article-slug&lt;/code&gt;，然後再將 Blog 套件 &lt;code&gt;directories&lt;/code&gt; 內文章的 &lt;code&gt;itemPermalink&lt;/code&gt; 設為 &lt;code&gt;/post/:slug&lt;/code&gt;，其中 &lt;code&gt;:slug&lt;/code&gt; 它就會自動抓 Markdown 檔名中的 &lt;code&gt;article-slug&lt;/code&gt; 部分。這算是它比較聰明的地方。&lt;/p&gt;
&lt;p&gt;文章區做完後，想說可以在首頁顯示最新的幾篇文章，結果光就這功能要爬到源碼才找到解法。&lt;/p&gt;
&lt;p&gt;在文章列表的 Vue 組件 (&lt;code&gt;IndexPost.vue&lt;/code&gt;) 裡可以使用 &lt;code&gt;this.$pagination&lt;/code&gt; 直接取得文章的資料，以此為線索找了方法，可以在 &lt;code&gt;Home.vue&lt;/code&gt; 裡可以調用 &lt;code&gt;this.$getPagination(&apos;post&apos;, &apos;post&apos;)&lt;/code&gt; 取得文章，並用 &lt;code&gt;slice()&lt;/code&gt; 截出我需要的文章數：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;.vuepress/theme/layouts/Home.vue&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;template&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;layout&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;container&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;文章&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;h2&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;grid gap-8 sm:grid-cols-6 mt-8&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;v-for&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;(page, i) in homePosts&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:key&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;page.name&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;PostGridItem&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:page&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;page&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;layout&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;template&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;// ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;computed: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;post&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;$getPagination&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;post&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;post&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;homePosts&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;post&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;_matchedPages&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;slice&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;9&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;後來看到一些網站可以置頂文章在首頁，增加排序文章方法，因此程式碼改成以下：&lt;/p&gt;
&lt;div&gt;&lt;div&gt;Info&lt;/div&gt;&lt;div&gt;&lt;p&gt;雖然這不是坑，姑且還是紀錄一下好了&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;.vuepress/theme/layouts/Home.vue&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;// ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;computed: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;post&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;$getPagination&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;post&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;post&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;homePosts&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;post&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;_matchedPages&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;sort&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;prev&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;next&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;prevPinOrder&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;prev&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;frontmatter&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;pin&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;nextPinOrder&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;next&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;frontmatter&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;pin&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;typeof&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;prevPinOrder&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;number&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;typeof&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;nextPinOrder&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;number&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;prevPinOrder&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;nextPinOrder&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;typeof&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;prevPinOrder&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;number&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;typeof&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;nextPinOrder&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;number&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;})&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;slice&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;9&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;然後只要在想要置頂的文章設定 &lt;code&gt;pin&lt;/code&gt; 為想要顯示的順序就好了。首頁的文章會照 1234… 的順序，之後接其他的文章：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;pin&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;文章...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3&gt;&lt;a href=&quot;#vuepress-last-updated-plugin-的時間-bug&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;VuePress Last Updated Plugin 的時間 Bug&lt;/h3&gt;
&lt;p&gt;用 &lt;strong&gt;vuepress-plugin-seo&lt;/strong&gt; + &lt;strong&gt;@vuepress/plugin-last-updated&lt;/strong&gt; 這組合，連 VuePress 都啟動不了。不過爬文時還看到過別的組合但同樣問題。話不多說，先上解法：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;.vuepress/config.js&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;module&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;exports&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;// ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;plugins: [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;@vuepress/last-updated&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;transformer&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;timestamp&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;timestamp&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;改完重啟 VuePress 就恢復正常了。原因其實是 @vuepress/plugin-last-updated 預設 timestamp 的 &lt;code&gt;transformer()&lt;/code&gt; 已經轉換成文字的日期格式 (用 &lt;code&gt;toLocaleString()&lt;/code&gt;)，但在 vuepress-plugin-seo 卻把這串日期文字丟進 &lt;code&gt;new Date()&lt;/code&gt; 解析。但在台灣的我，電腦預設 &lt;code&gt;toLocaleString()&lt;/code&gt; 會回傳中文格式的日期再丟進 &lt;code&gt;new Date()&lt;/code&gt;，但 JavaScript 卻表示看不懂，才造成現在這窘境。所以只需覆寫原本的 &lt;code&gt;transformer()&lt;/code&gt;，讓它回傳原本 timestamp 格式 (自 1970年1月1日 到該時間的秒數)，再丟進 &lt;code&gt;new Date()&lt;/code&gt; 解析就正常了。&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;#移除外部連結的-icon&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;移除外部連結的 Icon&lt;/h3&gt;
&lt;p&gt;VuePress 很 &lt;code&gt;貼心&lt;/code&gt; 的幫你在連外的連結後方都加上了標示的 Icon，但我不是很喜歡，想關掉但卻沒有…。後來在 Issue 也有人提到這個問題，才有下方解決方案 (&lt;a href=&quot;https://github.com/vuejs/vuepress/pull/614#issuecomment-506507754&quot;&gt;參考自&lt;/a&gt;)：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;.vuepress/config.js&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;module&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;exports&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;// ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;chainMarkdown&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;config&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;removePlugin&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;PLUGINS&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;require&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;@vuepress/markdown&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;originalLinkPlugin&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;require&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;@vuepress/markdown/lib/link&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;removePlugin&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;config&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;PLUGINS&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;CONVERT_ROUTER_LINK&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;config&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;plugin&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;PLUGINS&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;CONVERT_ROUTER_LINK&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;use&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;md&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;externalAttrs&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;originalLinkPlugin&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;call&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;md&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;externalAttrs&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;linkClose&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;md&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;renderer&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;rules&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;link_close&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;md&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;renderer&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;rules&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;link_close&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;linkClose&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;apply&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;arguments&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;replace&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;&amp;lt;OutboundLink/&amp;gt;&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}, [{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;target: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;_blank&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;rel: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;noopener noreferrer&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}])&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;改完記得重啟 VuePress 才看得到結果。&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;#滾動至標題錨點&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;滾動至標題錨點&lt;/h3&gt;
&lt;p&gt;VuePress 預設會在每個標題左側加上 &lt;code&gt;#&lt;/code&gt; 錨點連結，但如果標題有中文，並瀏覽中文標題產生出的連結，他會留在最頂部，不會自動滾動到錨點定位的點。而且還會警告：&lt;/p&gt;
&lt;div&gt;&lt;div&gt;Danger&lt;/div&gt;&lt;div&gt;&lt;p&gt;[Vue warn]: Error in nextTick: “SyntaxError: Failed to execute ‘querySelector’ on ‘Document’: ’#%E4%B8%AD%E6%96%87%E6%A8%99%E9%A1%8C’ is not a valid selector.”&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;查了一下，這是源碼的問題，也有人開 &lt;a href=&quot;https://github.com/vuejs/vuepress/pull/2566&quot;&gt;PR&lt;/a&gt; 了，但官方還沒處理，在新版出來之前先用點硬招強行突破吧！&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;.vuepress/theme/components/Layout.vue&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;// ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;methods: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;scrollTo&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;selector&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;selector&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;||&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;selector&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;#&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;el&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;document&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;querySelector&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;decodeURIComponent&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;selector&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;el&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;el&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;offsetTop&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;window&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;scrollTo&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;el&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;offsetTop&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;mounted&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;scrollTo&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;location&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;hash&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;&lt;a href=&quot;#tailwind-css-篇&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;Tailwind CSS 篇&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Tailwind CSS&lt;/strong&gt; 是一個 Utility-first、可高度客製化的 CSS 框架。網頁刻板必備，比 Bootstrap 好用。&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;#tailwind-css-之切換色系-darklght-mode&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;Tailwind CSS 之切換色系 (Dark/Lght Mode)&lt;/h3&gt;
&lt;p&gt;Tailwind CSS 目前我遇到的問題基本只有在做雙色系 (Dark/Light Mode) 切換。原本只打算做暗色系，後來聽朋友的建議，也把亮色系加進來，做了可以切換色系的功能。&lt;/p&gt;
&lt;p&gt;首先先裝 Dark Mode 套件：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;yarn&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tailwindcss-dark-mode&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;除了註冊套件之外，還要手動加 Variants，要用什麼才開什麼，例如下面對 &lt;code&gt;backgroundColor&lt;/code&gt; 和 &lt;code&gt;textColor&lt;/code&gt; 開了 &lt;code&gt;dark&lt;/code&gt; 跟 &lt;code&gt;dark-hover&lt;/code&gt; 兩個 Variants。可以使用的 Variants 請參考 &lt;a href=&quot;https://github.com/ChanceArthur/tailwindcss-dark-mode&quot;&gt;Tailwind CSS Dark Mode&lt;/a&gt;：&lt;/p&gt;
&lt;div&gt;&lt;div&gt;Warning&lt;/div&gt;&lt;div&gt;&lt;p&gt;沒看錯！這套件註冊後面要加括號！&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;tailwind.config.js&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;module&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;exports&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;// ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;variants: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;backgroundColor: [&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;responsive&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;hover&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;focus&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;dark&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;dark-hover&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;textColor: [&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;responsive&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;hover&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;focus&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;dark&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;dark-hover&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;plugins: [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;require&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;tailwindcss-dark-mode&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;)()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3&gt;&lt;a href=&quot;#typography-和-darklght-mode&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;Typography 和 Dark/Lght Mode&lt;/h3&gt;
&lt;p&gt;Tailwind CSS 官方也剛出建立純 HTML 樣式的套件 &lt;a href=&quot;https://github.com/tailwindlabs/tailwindcss-typography&quot;&gt;Typography&lt;/a&gt;，至少有現成的樣式可以調，不需要全部自己來。但用到 Dark Mode 時也出包了。不過還好，Tailwind CSS 的作者大大提供了解法：&lt;a href=&quot;https://github.com/adamwathan/tailwindcss-dark-mode-prototype&quot;&gt;tailwindcss-dark-mode-prototype&lt;/a&gt; (&lt;a href=&quot;https://tailwindcss-dark-mode-prototype.vercel.app/&quot;&gt;DEMO&lt;/a&gt;)，參考裡面的寫法終於解決了這問題：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;tailwind.config.js&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;plugin&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;require&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;tailwindcss/plugin&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;selectorParser&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;require&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;postcss-selector-parser&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;module&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;exports&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;purge: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;content: [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;./.vuepress/**/*.vue&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;options: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;whitelist: [&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;scheme-dark&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;// ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;plugins: [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;require&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;tailwindcss-dark-mode&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;)(),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;plugin&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;{ &lt;/span&gt;&lt;span&gt;addVariant&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;theme&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;prefix&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;darkSelector&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;theme&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;darkSelector&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;.mode-dark&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;addVariant&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;dark&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;{ &lt;/span&gt;&lt;span&gt;modifySelectors&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;separator&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;modifySelectors&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;{ &lt;/span&gt;&lt;span&gt;selector&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;selectorParser&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;selectors&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;selectors&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;walkClasses&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;sel&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;              &lt;/span&gt;&lt;span&gt;sel&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;dark&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;separator&lt;/span&gt;&lt;span&gt;}${&lt;/span&gt;&lt;span&gt;sel&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;              &lt;/span&gt;&lt;span&gt;sel&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;parent&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;insertBefore&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;sel&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;selectorParser&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;astSync&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;prefix&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;darkSelector&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;)))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;})&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;})&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;processSync&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;selector&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;})&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;})&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;require&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;@tailwindcss/typography&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;除了這個之外，切換的按鈕也是直接借來用，只是它是用 React 寫的，轉換成 Vue 的格式才能使用。&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#部署網站&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;部署網站&lt;/h2&gt;
&lt;p&gt;在 &lt;a href=&quot;https://www.netlify.com/&quot;&gt;Netlify&lt;/a&gt; 和 &lt;a href=&quot;https://vercel.com/&quot;&gt;Vercel&lt;/a&gt; 中經過艱難的選擇後，最後還是決定來試試用 Vercel 部署網站。開一個新的 Vercel 專案，經過簡單的設定後，就上線了我的新網站！更新網站也需要 Push 到 GitHub，不需要串任何 CI/CD 服務，還滿簡單的。目前還沒打算買 Domain，暫時先用 Vercel 預設的。&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#參考文章&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;參考文章&lt;/h2&gt;
&lt;p&gt;建部落格時參考的文章：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.xlbd.me/posts/2020-06-01-jamstack-blog-theme.html&quot;&gt;VuePress + TailwindCss + Netlify 重写个人独立博客&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ououe.com/posts/2019/07/07/vuepress/&quot;&gt;vuepress 踩坑记录&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;&lt;a href=&quot;#結語&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;結語&lt;/h2&gt;
&lt;p&gt;其實很早就有在找建個人網站/部落格的平台，試過 Medium、GitBook 和其他的方法，最後決定以 &lt;strong&gt;VuePress&lt;/strong&gt; 為基礎、 &lt;strong&gt;Tailwind CSS&lt;/strong&gt; 刻板這個組合最滿意，花了快1個月完成，果然自己設計網站還是比較有趣。比起完成作品，更多的是在過程中吸取的經驗，一次會比一次進步。感謝你看到這裡，希望本文些許可以幫到你😁。&lt;/p&gt;</content:encoded></item><item><title>Swoole 從入門到實戰 工作坊 | Docker 環境安裝筆記</title><link>https://star-note-lucas.me/posts/swoole-workshop-note</link><guid isPermaLink="true">https://star-note-lucas.me/posts/swoole-workshop-note</guid><description>2019/10/26 Swoole 從入門到實戰 工作坊上課的筆記</description><pubDate>Sat, 26 Oct 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;上課時使用的筆電是 Win10 家用版，因 Docker for Windows 只能在旗艦版中安裝，家用版無法直接使用 Docker，因此選擇在 Homestead 中安裝 Docker 使用。&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#安裝環境&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;安裝環境&lt;/h2&gt;
&lt;p&gt;先 clone &lt;a href=&quot;https://github.com/albertcht/swoole-course-examples&quot;&gt;swoole-course-examples&lt;/a&gt; 到本地：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;clone&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;https://github.com/albertcht/swoole-course-examples.git&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;開啟 &lt;code&gt;homestead.yaml&lt;/code&gt; 設定環境，資料夾掛載、開啟 Docker、掛載 9501 Port，設定完成後就可以 vagrant up：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;homestead.yaml&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;folders&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;map&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;d:/dev/code/swoole-course-examples&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;to&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;/home/vagrant/code/swoole-course-examples&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;features&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;docker&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ports&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;send&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;9501&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;to&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;9501&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;登入 Vagrant SSH，切換到 swoole-course-examples 資料夾，執行 Docker image：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;docker&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;run&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-it&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--name&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;swoole&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-v&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;$PWD&lt;/span&gt;&lt;span&gt;:/src&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-p&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;9501:9501&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;albertcht/swoole&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;&lt;a href=&quot;#參考資料&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;參考資料&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/laraveldojo/build-a-dev-env-for-swoole-using-docker-a89fdbc9588&quot;&gt;3 分鐘快速建置可以運行 Swoole 的開發環境&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.hashicorp.com/vagrant/docs/provisioning/docker&quot;&gt;Vagrant Docker Provisioner&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>VSCode 紀錄</title><link>https://star-note-lucas.me/posts/vscode-remark</link><guid isPermaLink="true">https://star-note-lucas.me/posts/vscode-remark</guid><description>VSCode 的小技巧紀錄</description><pubDate>Tue, 04 Jun 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;&lt;a href=&quot;#設定-vscode-的終端機為-git-bash&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;設定 VSCode 的終端機為 Git Bash&lt;/h2&gt;
&lt;div&gt;&lt;div&gt;Tip&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;a href=&quot;https://git-scm.com/&quot;&gt;安裝 Git&lt;/a&gt; 就會安裝 Git Bash。&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;開啟 VSCode 設定檔，增加以下設定：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;settings.json&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;// ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;terminal.integrated.shell.windows&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;C:&lt;/span&gt;&lt;span&gt;\\&lt;/span&gt;&lt;span&gt;Program Files&lt;/span&gt;&lt;span&gt;\\&lt;/span&gt;&lt;span&gt;Git&lt;/span&gt;&lt;span&gt;\\&lt;/span&gt;&lt;span&gt;bin&lt;/span&gt;&lt;span&gt;\\&lt;/span&gt;&lt;span&gt;bash.exe&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;terminal.integrated.shellArgs.windows&quot;&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;--login&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;&lt;a href=&quot;#vscode-智能感知intellisense-異常解決方法&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;VSCode 智能感知(IntelliSense) 異常解決方法&lt;/h2&gt;
&lt;p&gt;將 &lt;code&gt;C:\Users\[使用者]\AppData\Roaming\Code\User\workspaceStorage&lt;/code&gt; 下的檔案清除，重新啟動 VS Code。&lt;/p&gt;</content:encoded></item><item><title>Git Bash 使用別名</title><link>https://star-note-lucas.me/posts/git-bash-alias</link><guid isPermaLink="true">https://star-note-lucas.me/posts/git-bash-alias</guid><description>在 Git Bash 使用別名教學</description><pubDate>Wed, 22 May 2019 00:00:00 GMT</pubDate><content:encoded>&lt;div&gt;&lt;div&gt;Tip&lt;/div&gt;&lt;div&gt;&lt;p&gt;Vim 輸入 &lt;code&gt;i&lt;/code&gt; 開始編輯模式，&lt;code&gt;Esc&lt;/code&gt; 關閉編輯模式，&lt;code&gt;:wq&lt;/code&gt; 儲存並退出。(&lt;a href=&quot;http://linux.vbird.org/linux_basic/0310vi.php&quot;&gt;Vim 教學&lt;/a&gt;)&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h3&gt;&lt;a href=&quot;#設定本機指令別名&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;設定本機指令別名&lt;/h3&gt;
&lt;p&gt;開啟 Git Bash 設定別名的檔案：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;vim&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/c/&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Program Files&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;/Git/etc/profile.d/aliases.sh&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;增加以下內容，或自訂常用指令：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;alias&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pa&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;php artisan&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;alias&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pa71&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;php71 artisan&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;alias&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pa72&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;php72 artisan&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;alias&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pa73&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;php73 artisan&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;alias&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pa74&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;php74 artisan&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;alias&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pa80&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;php80 artisan&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;alias&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pa81&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;php81 artisan&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;alias&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pa82&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;php82 artisan&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;alias&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pav&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;php artisan serve&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;alias&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pam&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;php artisan migrate&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;alias&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pams&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;php artisan migrate --seed&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;alias&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pamr&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;php artisan migrate:rollback&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;alias&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pamf&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;php artisan migrate:fresh&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;alias&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pamfs&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;php artisan migrate:fresh --seed&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;alias&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pas&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;php artisan db:seed&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;alias&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;par&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;php artisan route:list&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;alias&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;paq&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;php artisan queue:work&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;alias&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pah&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;php artisan horizon&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;alias&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pat&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;php artisan tinker&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;alias&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;phpunit&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;./vendor/bin/phpunit --colors=always&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;alias&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pu&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;phpunit&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;alias&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pest&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;./vendor/bin/pest&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;alias&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pint&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;./vendor/bin/pint&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;alias&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;stan&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;./vendor/bin/larastan&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;如果在 Laragon 有安裝多個 PHP 版本，可以增加以下別名來切換使用版本：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;alias&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;php71&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;/c/laragon/bin/php/php-7.1.33-Win32-VC14-x64/php&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;alias&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;php72&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;/c/laragon/bin/php/php-7.2.34-Win32-VC15-x64/php&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;alias&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;php73&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;/c/laragon/bin/php/php-7.3.33-Win32-VC15-x64/php&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;alias&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;php74&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;/c/laragon/bin/php/php-7.4.30-Win32-vc15-x64/php&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;alias&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;php80&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;/c/laragon/bin/php/php-8.0.14-Win32-vs16-x64/php&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;alias&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;php81&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;/c/laragon/bin/php/php-8.1.14-Win32-vs16-x64/php&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;alias&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;php82&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;/c/laragon/bin/php/php-8.2.1-Win32-vs16-x64/php&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;alias&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;composer71&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;php71 /c/Users/Lucas/AppData/Local/ComposerSetup/bin/composer.phar&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;alias&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;composer72&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;php72 /c/Users/Lucas/AppData/Local/ComposerSetup/bin/composer.phar&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;alias&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;composer73&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;php73 /c/Users/Lucas/AppData/Local/ComposerSetup/bin/composer.phar&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;alias&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;composer74&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;php74 /c/Users/Lucas/AppData/Local/ComposerSetup/bin/composer.phar&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;alias&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;composer80&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;php80 /c/Users/Lucas/AppData/Local/ComposerSetup/bin/composer.phar&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;alias&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;composer81&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;php81 /c/Users/Lucas/AppData/Local/ComposerSetup/bin/composer.phar&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;alias&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;composer82&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;php82 /c/Users/Lucas/AppData/Local/ComposerSetup/bin/composer.phar&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3&gt;&lt;a href=&quot;#設定-homestead-指令別名&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;設定 Homestead 指令別名&lt;/h3&gt;
&lt;p&gt;開啟 Git Bash 設定別名的檔案：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;vim&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/c/&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Program Files&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;/Git/etc/profile.d/aliases.sh&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;增加以下指令內容：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# 開啟虛擬機&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;vup&lt;/span&gt;&lt;span&gt;() (&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;cd&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~/Homestead&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; [[ &lt;/span&gt;&lt;span&gt;$1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;-p&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; ]]; &lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;vagrant&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;up&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--provision&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;vagrant&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;up&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;fi&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# 更新設定值&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;vp&lt;/span&gt;&lt;span&gt;() (&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;cd&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~/Homestead&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;vagrant&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;provision&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# 登入 SSH&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;vs&lt;/span&gt;&lt;span&gt;() (&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;cd&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~/Homestead&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; [[ &lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-z&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;$1&lt;/span&gt;&lt;span&gt; ]]; &lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;vagrant&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ssh&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-c&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$1&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;vagrant&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ssh&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;fi&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# 關機虛擬機&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;vh&lt;/span&gt;&lt;span&gt;() (&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;cd&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~/Homestead&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;vagrant&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;halt&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# 重新開啟虛擬機&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;vrs&lt;/span&gt;&lt;span&gt;() (&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;cd&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~/Homestead&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;vagrant&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;halt&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; [[ &lt;/span&gt;&lt;span&gt;$1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;-p&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; ]]; &lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;vagrant&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;up&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--provision&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;vagrant&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;up&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;fi&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# 查看虛擬機狀態&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;vst&lt;/span&gt;&lt;span&gt;() (&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;cd&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~/Homestead&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;vagrant&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;status&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;設定完成後，可使用以下指令操作 Vagrant：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;vup&lt;/code&gt;：開機&lt;/li&gt;
&lt;li&gt;&lt;code&gt;vp&lt;/code&gt;：更新設定值&lt;/li&gt;
&lt;li&gt;&lt;code&gt;vs&lt;/code&gt;：登入 SSH&lt;/li&gt;
&lt;li&gt;&lt;code&gt;vh&lt;/code&gt;：關機&lt;/li&gt;
&lt;li&gt;&lt;code&gt;vrs&lt;/code&gt;：重新開機&lt;/li&gt;
&lt;li&gt;&lt;code&gt;vst&lt;/code&gt;：查看虛擬機狀態&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>Homestead 安裝 phpMyAdmin</title><link>https://star-note-lucas.me/posts/install-phpmyadmin</link><guid isPermaLink="true">https://star-note-lucas.me/posts/install-phpmyadmin</guid><description>在 Homestead 裡安裝 phpMyAdmin 教學</description><pubDate>Sun, 12 May 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h3&gt;&lt;a href=&quot;#1-下載-phpmyadmin&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;1. 下載 phpMyAdmin：&lt;/h3&gt;
&lt;p&gt;到 &lt;a href=&quot;https://www.phpmyadmin.net/&quot;&gt;phpMyAdmin官網&lt;/a&gt; 下載程式碼，解壓縮後重新命名為 &lt;code&gt;phpmyadmin&lt;/code&gt;，並移至 &lt;code&gt;~/code/&lt;/code&gt; 裡。&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;#2-修改-homesteadyaml&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;2. 修改 Homestead.yaml&lt;/h3&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Homestead.yaml&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sites&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;map&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;phpmyadmin.test&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;to&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;/home/vagrant/code/phpmyadmin&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3&gt;&lt;a href=&quot;#3-修改-hosts-設定&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;3. 修改 Hosts 設定&lt;/h3&gt;
&lt;p&gt;開啟 &lt;code&gt;C:\Windows\System32\drivers\etc\hosts&lt;/code&gt;，增加以下設定：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;hosts&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;192.168.10.10     phpmyadmin.test&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3&gt;&lt;a href=&quot;#4-重新啟動虛擬機&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;4. 重新啟動虛擬機&lt;/h3&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;vagrant&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;halt&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;vagrant&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;up&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--provision&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;vagrant&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ssh&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;即可使用 &lt;a href=&quot;http://phpmyadmin.test/&quot;&gt;phpmyadmin.test&lt;/a&gt; 瀏覽 MySQL 資料庫內容了。&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;#提示訊息解決方法&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;◎提示訊息解決方法&lt;/h3&gt;
&lt;h4&gt;&lt;a href=&quot;#訊息設定檔案需要設定一組加密密碼-blowfish_secret&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;訊息：「設定檔案需要設定一組加密密碼 (blowfish_secret)。」&lt;/h4&gt;
&lt;p&gt;執行：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;cd&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~/code/phpmyadmin&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;cp&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;config.sample.inc.php&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;config.inc.php&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;vim&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;config.inc.php&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;div&gt;Tip&lt;/div&gt;&lt;div&gt;&lt;p&gt;Vim 輸入 &lt;code&gt;i&lt;/code&gt; 開始編輯模式，&lt;code&gt;Esc&lt;/code&gt; 關閉編輯模式，&lt;code&gt;:wq&lt;/code&gt; 儲存並退出。(&lt;a href=&quot;http://linux.vbird.org/linux_basic/0310vi.php&quot;&gt;Vim 教學&lt;/a&gt;)&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;將檔案內的：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;~/code/phpmyadmin/config.inc.php&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;$cfg&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;blowfish_secret&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;設定為：(隨機32個以上的亂七八糟英文數字，例：sSG5sg4sd54…)&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;~/code/phpmyadmin/config.inc.php&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;$cfg&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;blowfish_secret&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;可使用 Laravel Tinker 生成亂數字串：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;php&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;artisan&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tinker&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&amp;gt;&amp;gt;&amp;gt; \Str::random(32);&lt;/div&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h4&gt;&lt;a href=&quot;#訊息新的-phpmyadmin-已經發佈請考慮升級至最新的版本版本-xxx於-20xx-xx-xx-發佈&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;訊息：「新的 phpMyAdmin 已經發佈，請考慮升級至最新的版本（版本 xxx，於 20xx-xx-xx 發佈）。」&lt;/h4&gt;
&lt;p&gt;執行：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;cd&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;vim&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;config.inc.php&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;以及關閉 phpMyAdmin 的版本檢查：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;~/code/phpmyadmin/config.inc.php&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;$cfg&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;VersionCheck&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h4&gt;&lt;a href=&quot;#訊息設定檔權限錯誤檔案不應讓所有人可以寫入&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;訊息：「設定檔權限錯誤，檔案不應讓所有人可以寫入！」&lt;/h4&gt;
&lt;p&gt;如果 phpMyAdmin 安裝在虛擬機裡，將權限修改成 755 就可以正常執行了：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;cd&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~/code&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;chmod&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-R&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;755&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;phpmyadmin&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;但如果是安裝在 Windows 電腦裡，再映射到虛擬機中的話就改不了 (權限強制 777)，因此只能關掉 phpMyAdmin 的權限檢查了：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;~/code/phpmyadmin/config.inc.php&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;$cfg&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;CheckConfigurationPermissions&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h4&gt;&lt;a href=&quot;#增加-phpmyadmin-登入逾時時間&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;增加 phpMyAdmin 登入逾時時間&lt;/h4&gt;
&lt;p&gt;執行指令修改 &lt;code&gt;php.ini&lt;/code&gt; 檔：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sed&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-i&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;s/session.gc_maxlifetime = 1440/session.gc_maxlifetime = 86400/&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/etc/php/7.4/fpm/php.ini&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;service&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;php7.4-fpm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;restart&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;然後修改 &lt;code&gt;LoginCookieValidity&lt;/code&gt; 參數：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;~/code/phpmyadmin/config.inc.php&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;$cfg&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;LoginCookieValidity&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;86400&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;</content:encoded></item><item><title>Homestead 安裝過程記錄 (Windows)</title><link>https://star-note-lucas.me/posts/homestead-install</link><guid isPermaLink="true">https://star-note-lucas.me/posts/homestead-install</guid><description>記錄安裝 Homestead 的過程和遇到的坑</description><pubDate>Sat, 11 May 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;&lt;a href=&quot;#安裝&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;安裝&lt;/h2&gt;
&lt;h3&gt;&lt;a href=&quot;#本地軟體安裝&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;本地軟體安裝&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://www.php.net/downloads.php&quot;&gt;PHP&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://getcomposer.org/download/&quot;&gt;Composer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://nodejs.org/en/&quot;&gt;Node.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Yarn(可選)：&lt;code&gt;npm install -g yarn&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;&lt;a href=&quot;#1-安裝-git&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;1. 安裝 Git&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://git-scm.com/&quot;&gt;下載 Git&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;&lt;a href=&quot;#設定-git&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;設定 Git&lt;/h4&gt;
&lt;p&gt;初次使用 Git 需設定使用者名稱和 E-mail：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;config&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--global&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;user.name&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;John Doe&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;config&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--global&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;user.email&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;johndoe@example.com&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;若出現亂碼 &lt;code&gt;ESC[33&lt;/code&gt; 和 &lt;code&gt;ESC[m&lt;/code&gt; 可執行此指令：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;config&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--global&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;core.pager&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;less -r&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3&gt;&lt;a href=&quot;#2-終端機---git-bash&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;2. 終端機 - Git Bash&lt;/h3&gt;
&lt;p&gt;終端機建議使用 Git Bash (安裝 Git 即會自動安裝Git Bash)。在 Git Bash 中無法使用 &lt;code&gt;Ctrl&lt;/code&gt; &lt;code&gt;C/V&lt;/code&gt; 複製貼上，需要用 &lt;code&gt;Ctrl&lt;/code&gt; &lt;code&gt;Insert&lt;/code&gt; 複製、&lt;code&gt;Shift&lt;/code&gt; &lt;code&gt;Insert&lt;/code&gt; 貼上。但如果使用 Windows Terminal 的話就可以直接使用 &lt;code&gt;Ctrl&lt;/code&gt; &lt;code&gt;C/V&lt;/code&gt; 複製貼上。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://star-note-lucas.me/posts/vscode-remark#%E8%A8%AD%E5%AE%9A-vscode-%E7%9A%84%E7%B5%82%E7%AB%AF%E6%A9%9F%E7%82%BA-git-bash&quot;&gt;設定 VSCode 的終端機為 Git Bash&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://star-note-lucas.me/posts/git-bash-alias&quot;&gt;Git Bash 使用別名&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;a href=&quot;#3-設定-ssh&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;3. 設定 SSH&lt;/h3&gt;
&lt;p&gt;生成私鑰：&lt;a href=&quot;https://docs.github.com/en/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent&quot;&gt;Generating a new SSH key and adding it to the ssh-agent&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;#4-安裝-virtualbox-虛擬機&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;4. 安裝 VirtualBox (虛擬機)&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://www.virtualbox.org/wiki/Downloads&quot;&gt;下載 VirtualBox&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;#5-安裝-vagrant-虛擬機管理工具&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;5. 安裝 Vagrant (虛擬機管理工具)&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.hashicorp.com/vagrant/install&quot;&gt;下載 Vagrant&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;#6-下載-homestead&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;6. 下載 Homestead&lt;/h3&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;clone&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;https://github.com/laravel/homestead.git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~/Homestead&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;cd&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~/Homestead&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;init.bat&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3&gt;&lt;a href=&quot;#7-配置-homestead&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;7. 配置 Homestead&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://learnku.com/docs/laravel/5.7/homestead/2245#configuring-homestead&quot;&gt;(詳細、必讀) 配置 Homestead 教學&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.hashicorp.com/vagrant/docs/provisioning/file&quot;&gt;(可選) 自動複製.gitconfig到虛擬機 (配置 Vagrantfile)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://medium.com/@cedricdue/moving-vagrant-boxes-and-related-virtualbox-vms-to-another-drive-f1d7c50d20bc&quot;&gt;(可選) 移動Vagrant Box及VirtualBox VMs到其他磁碟 (固態硬碟適用)&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;#8-bios-設定&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;8. BIOS 設定&lt;/h3&gt;
&lt;div&gt;&lt;div&gt;Info&lt;/div&gt;&lt;div&gt;&lt;p&gt;重新開機，進入 BIOS，將「Intel Virtualization Technology」(Intel 虛擬化技術) 選項開啟。&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h3&gt;&lt;a href=&quot;#9-啟動虛擬機&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;9. 啟動虛擬機&lt;/h3&gt;
&lt;p&gt;執行(Windows須使用Git-bash)：&lt;/p&gt;
&lt;p&gt;開機並更新設定：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;vagrant&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;up&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--provision&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;SSH連線、操作：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;vagrant&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ssh&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;關機(虛擬機使用完畢須關機，否則下次使用會出問題)：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;vagrant&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;halt&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3&gt;&lt;a href=&quot;#10-安裝-phpmyadmin&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;10. 安裝 phpMyAdmin&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://star-note-lucas.me/posts/install-phpmyadmin&quot;&gt;安裝phpMyAdmin&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;#11-ubuntu-修改ubuntu預設編輯器&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;11. (Ubuntu) 修改Ubuntu預設編輯器&lt;/h3&gt;
&lt;p&gt;登入虛擬機，執行：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;update-alternatives&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--config&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;editor&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;出現以下畫面：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://star-note-lucas.me/images/2019-05-11-homestead-install-editor.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;輸入 &lt;code&gt;3&lt;/code&gt;，將會切換成Vim編輯器。&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;#12-配置共享資料夾&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;12. 配置共享資料夾&lt;/h3&gt;
&lt;p&gt;預設的資料夾為 &lt;code&gt;C:\Users\[使用者]\code&lt;/code&gt;，若 &lt;code&gt;code&lt;/code&gt; 資料夾不存在須創建。&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;mkdir&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~/code&lt;/span&gt;&lt;span&gt; &amp;amp;&amp;amp; &lt;/span&gt;&lt;span&gt;cd&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~/code&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3&gt;&lt;a href=&quot;#13-新增-laravel-專案&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;13. 新增 Laravel 專案&lt;/h3&gt;
&lt;p&gt;使用 Composer 下載 Laravel 安裝包：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;composer&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;global&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;require&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;laravel/installer&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;建立新 Laravel 專案 (&lt;code&gt;blog&lt;/code&gt; 為專案名稱)：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;laravel&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;blog&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;修改 Homestead.yaml：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Homestead.yaml&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;folders&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;map&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;~/code/blog&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;to&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;/home/vagrant/code/blog&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sites&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;map&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;blog.test&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;to&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;/home/vagrant/code/blog/public&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;重新啟動虛擬機：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;vagrant&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;halt&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;vagrant&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;up&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--provision&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;vagrant&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ssh&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3&gt;&lt;a href=&quot;#14-修改-hosts-設定&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;14. 修改 Hosts 設定&lt;/h3&gt;
&lt;p&gt;開啟 &lt;code&gt;C:\Windows\System32\drivers\etc\hosts&lt;/code&gt;，增加以下設定：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;192.168.10.10&lt;/span&gt;&lt;span&gt;     &lt;/span&gt;&lt;span&gt;blog.test&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3&gt;&lt;a href=&quot;#15-整合-vite&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;15. 整合 Vite&lt;/h3&gt;
&lt;p&gt;開啟 Vite Server 的 port：&lt;/p&gt;
&lt;div&gt;&lt;div&gt;Tip&lt;/div&gt;&lt;div&gt;&lt;p&gt;如果有使用 &lt;a href=&quot;https://code.visualstudio.com/docs/remote/ssh&quot;&gt;VSCode Remote SSH 套件&lt;/a&gt; 的話就不用加這段，因為 Remote SSH 套件會在啟動 dev server 時自動轉發 port。&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;Homestead.yaml&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ports&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;send&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;5173&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;# for vite server&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;to&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;5173&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;然後在 &lt;code&gt;vite.config.js&lt;/code&gt; 加上以下設定：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;vite.config.js&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; path &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;node:path&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;defineConfig&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;server: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;host: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;hmr: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;host: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;localhost&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;watch: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;ignored: [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;path&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;join&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;__dirname&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;app/**&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;path&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;join&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;__dirname&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;bootstrap/**&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;path&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;join&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;__dirname&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;config/**&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;path&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;join&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;__dirname&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;database/**&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;path&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;join&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;__dirname&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;lang/**&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;path&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;join&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;__dirname&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;public/**&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;path&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;join&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;__dirname&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;routes/**&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;path&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;join&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;__dirname&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;storage/**&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;path&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;join&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;__dirname&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;tests/**&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;path&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;join&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;__dirname&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;vendor/**&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;usePolling: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;})&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;div&gt;Warning&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;code&gt;usePolling: true&lt;/code&gt; 的設定會讓 CPU 使用率提高，如果要避免此問題，可以使用 &lt;a href=&quot;https://code.visualstudio.com/docs/remote/ssh&quot;&gt;VSCode 的 Remote SSH 套件&lt;/a&gt; 來遠端開發，只要在非 Windows 的環境編輯就不需要加上 &lt;code&gt;usePolling: true&lt;/code&gt; 了。&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;建立 Vite 開發伺服器的 HTTPS 證書：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/vagrant/scripts/create-certificate.sh&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;localhost&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;chmod&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-R&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;644&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/etc/ssl/certs/localhost.key&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;設定本地開發用證書路徑：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;.env&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;VITE_DEV_SERVER_KEY&lt;/span&gt;&lt;span&gt;=/etc/ssl/certs/localhost.key&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;VITE_DEV_SERVER_CERT&lt;/span&gt;&lt;span&gt;=/etc/ssl/certs/localhost.crt&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;然後就可以在 Vite 中讀取 HTTPS 證書了：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;vite.config.js&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; fs &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;node:fs&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { defineConfig, loadEnv } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;vite&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;defineConfig&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;{ &lt;/span&gt;&lt;span&gt;mode&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;env&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;loadEnv&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;mode&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;process&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;cwd&lt;/span&gt;&lt;span&gt;())&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;server: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;host: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;hmr: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;host: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;localhost&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;https: &lt;/span&gt;&lt;span&gt;env&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;VITE_DEV_SERVER_KEY&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;env&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;VITE_DEV_SERVER_CERT&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;key: &lt;/span&gt;&lt;span&gt;fs&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;readFileSync&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;env&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;VITE_DEV_SERVER_KEY&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;cert: &lt;/span&gt;&lt;span&gt;fs&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;readFileSync&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;env&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;VITE_DEV_SERVER_CERT&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;undefined&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;})&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3&gt;&lt;a href=&quot;#16-ngrok-內網穿透&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;16. ngrok 內網穿透&lt;/h3&gt;
&lt;p&gt;把 &lt;code&gt;example.com&lt;/code&gt; 替換成需要使用的網址，如果有用 HTTPS 可以使用 443：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ngrok&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;http&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;example.com:80&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--host-header=rewrite&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# 若使用 HTTPS&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ngrok&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;http&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;example.com:443&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--host-header=rewrite&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;&lt;a href=&quot;#其他&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;其他&lt;/h2&gt;
&lt;h3&gt;&lt;a href=&quot;#錯誤訊息timed-out-while-waiting-for-the-machine-to-boot&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;錯誤訊息：「&lt;code&gt;Timed out while waiting for the machine to boot&lt;/code&gt;」&lt;/h3&gt;
&lt;p&gt;需要關閉「容器」、「Windows 沙箱」、「虛擬機器平台」、「Windows 子系統 Linux 版」、「Hyper-V」功能，以「系統管理員」身分開啟 PowerShell 執行：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Disable-WindowsOptionalFeature&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-Online&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-FeatureName&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Containers,Containers-DisposableClientVM,VirtualMachinePlatform,Microsoft-Windows-Subsystem-Linux,Microsoft-Hyper-V-All&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;若需要使用 Docker 或 WSL 的話，可以重新開啟：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Enable-WindowsOptionalFeature&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-Online&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-FeatureName&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Containers,Containers-DisposableClientVM,VirtualMachinePlatform,Microsoft-Windows-Subsystem-Linux,Microsoft-Hyper-V-All&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;檢查功能是否開啟：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Get-WindowsOptionalFeature&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-Online&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-FeatureName&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Containers&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Get-WindowsOptionalFeature&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-Online&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-FeatureName&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Containers-DisposableClientVM&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Get-WindowsOptionalFeature&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-Online&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-FeatureName&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;VirtualMachinePlatform&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Get-WindowsOptionalFeature&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-Online&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-FeatureName&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Microsoft-Windows-Subsystem-Linux&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Get-WindowsOptionalFeature&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-Online&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-FeatureName&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Microsoft-Hyper-V-All&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3&gt;&lt;a href=&quot;#homestead-安裝問題&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;Homestead 安裝問題&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://learnku.com/laravel/t/4796/ssh-auth-method-private-key-solution-summary-when-homestead-starts&quot;&gt;Homestead 安装问题汇总&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;#在本機安裝-homestead-的安全憑證&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;在本機安裝 Homestead 的安全憑證&lt;/h3&gt;
&lt;p&gt;解決 HTTPS 顯示為不安全的問題。&lt;/p&gt;
&lt;p&gt;登入虛擬機，輸入以下指令取得憑證 (&lt;code&gt;your-dir&lt;/code&gt; 替換成任一已存在的資料夾)：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cp&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/etc/nginx/ssl/ca.homestead.homestead.crt&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~/code/your-dir&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# Ubuntu 20.04 的版本要執行下方的指令&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cp&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/etc/ssl/certs/ca.homestead.homestead.crt&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~/code/your-dir&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;開啟檔案總管，雙擊開啟憑證，點擊 &lt;strong&gt;安裝憑證&lt;/strong&gt;，憑證存放區選擇為 &lt;strong&gt;受信任的根憑證授權單位&lt;/strong&gt;，之後點擊完成即可。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;Info&lt;/div&gt;&lt;div&gt;&lt;p&gt;舊的文章可以參考：&lt;a href=&quot;https://star-note-lucas.me/posts/homestead-https&quot;&gt;使用 HTTPS 連線 Homestead 網站 (Windows)&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h3&gt;&lt;a href=&quot;#windows-cmd-設定在虛擬機中用-composer-安裝套件時新增-bat-檔&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;(Windows CMD) 設定在虛擬機中用 Composer 安裝套件時，新增 .bat 檔&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://getcomposer.org/doc/06-config.md#bin-compat&quot;&gt;Config (bin-compat) - Composer&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;於 Homestead 執行：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;composer&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;config&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-g&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;bin-compat&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;full&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3&gt;&lt;a href=&quot;#增加路徑至-path&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;增加路徑至 PATH&lt;/h3&gt;
&lt;p&gt;執行：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;vim&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~/.profile&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;輸入：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;PATH&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;/home/vagrant/.yarn/bin:&lt;/span&gt;&lt;span&gt;$PATH&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3&gt;&lt;a href=&quot;#執行-php-artisan-storagelink-時出錯-無法新增-symlinks&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;執行 php artisan storage:link 時出錯 (無法新增 symlinks)&lt;/h3&gt;
&lt;p&gt;解決方法：重新開機並以 &lt;strong&gt;系統管理員&lt;/strong&gt; 執行 Git bash，啟動虛擬機。完成後再執行一次 &lt;code&gt;php artisan storage:link&lt;/code&gt;。&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;#執行-vagrant-up-時總是出錯最終解決方法重新下載相關檔案&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;執行 vagrant up 時總是出錯，最終解決方法，重新下載相關檔案&lt;/h3&gt;
&lt;p&gt;刪除以下資料夾：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;C:\Users\[使用者]\.vagrant.d&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;C:\Users\[使用者]\VirtualBox VMs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;C:\Users\[使用者]\Homestead\.vagrant&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;再重新執行 &lt;code&gt;vagrant up&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;#執行-vagrant-ssh-並啟動任意持續執行的程式按-ctrlc-會出問題&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;執行 vagrant ssh 並啟動任意持續執行的程式，按 Ctrl+C 會出問題&lt;/h3&gt;
&lt;p&gt;導出 vagrant ssh 的設定檔：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sed&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-i&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;1i Include config.d/*&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~/.ssh/config&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;mkdir&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~/.ssh/config.d&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;vagrant&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ssh-config&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--host&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;vm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~/.ssh/config.d/vm&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;之後可以在啟動 vagrant 虛擬機後使用 ssh 來連接：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ssh&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;vm&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3&gt;&lt;a href=&quot;#vite-hmr-沒反應或很慢&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;Vite HMR 沒反應或很慢&lt;/h3&gt;
&lt;p&gt;因為在 Linux 環境下 file descriptor 和 inotify 遇到上限時會直接沒反應，因此當遇到此問題時可以檢查看看當前的 inotify 上限數量：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sysctl&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fs.inotify&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;臨時增加上限：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sysctl&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fs.inotify.max_queued_events=&lt;/span&gt;&lt;span&gt;16384&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sysctl&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fs.inotify.max_user_instances=&lt;/span&gt;&lt;span&gt;8192&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sysctl&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fs.inotify.max_user_watches=&lt;/span&gt;&lt;span&gt;524288&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sysctl&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-p&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;永久增加上限，執行完記得重啟：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fs.inotify.max_queued_events=&lt;/span&gt;&lt;span&gt;16384&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tee&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/etc/sysctl.conf&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fs.inotify.max_user_instances=&lt;/span&gt;&lt;span&gt;8192&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tee&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/etc/sysctl.conf&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fs.inotify.max_user_watches=&lt;/span&gt;&lt;span&gt;524288&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tee&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/etc/sysctl.conf&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sysctl&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-p&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;當然也有可能是 RAM 太小，同時調高虛擬機的 RAM 可以來解決。&lt;/p&gt;</content:encoded></item><item><title>使用 HTTPS 連線 Homestead 網站 (Windows)</title><link>https://star-note-lucas.me/posts/homestead-https</link><guid isPermaLink="true">https://star-note-lucas.me/posts/homestead-https</guid><description>解決瀏覽 Homestead 網站時出現的「不安全」提示</description><pubDate>Thu, 07 Feb 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;在本地使用 Homestead 開發 Laravel 專案非常方便，一開始使用 HTTP 連線時：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://star-note-lucas.me/images/2019-02-07-homestead-https-1.jpg&quot; alt=&quot;使用 HTTP 連線&quot; /&gt;&lt;/p&gt;
&lt;p&gt;但如果改用 HTTPS 連線時，就會…&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://star-note-lucas.me/images/2019-02-07-homestead-https-2.jpg&quot; alt=&quot;改用 HTTPS 連線&quot; /&gt;&lt;/p&gt;
&lt;p&gt;其實只要在本機中安裝 Homestead CA 根憑證，「不安全」的標示就會消失了。&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#在-windows-安裝-homestead-ca-根憑證&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;在 Windows 安裝 Homestead CA 根憑證&lt;/h2&gt;
&lt;p&gt;先透過 SSH 連接至 虛擬機，然後執行下列指令，複製 CA 根憑證檔：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cp&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/etc/nginx/ssl/ca.homestead.homestead.crt&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~/code/homestead&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src=&quot;https://star-note-lucas.me/images/2019-02-07-homestead-https-3.jpg&quot; alt=&quot;複製 CA 根憑證檔&quot; /&gt;&lt;/p&gt;
&lt;p&gt;然後開始安裝 Homestead CA 根憑證：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://star-note-lucas.me/images/2019-02-07-homestead-https-4.jpg&quot; alt=&quot;安裝 Homestead CA 根憑證 1&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://star-note-lucas.me/images/2019-02-07-homestead-https-5.jpg&quot; alt=&quot;安裝 Homestead CA 根憑證 2&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://star-note-lucas.me/images/2019-02-07-homestead-https-6.jpg&quot; alt=&quot;安裝 Homestead CA 根憑證 3&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://star-note-lucas.me/images/2019-02-07-homestead-https-7.jpg&quot; alt=&quot;安裝 Homestead CA 根憑證 4&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://star-note-lucas.me/images/2019-02-07-homestead-https-8.jpg&quot; alt=&quot;安裝 Homestead CA 根憑證 5&quot; /&gt;&lt;/p&gt;
&lt;p&gt;最後 &lt;strong&gt;重新開啟瀏覽器&lt;/strong&gt; 後，就可以看到 HTTPS 連線成功，也不會有「不安全」的標示了：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://star-note-lucas.me/images/2019-02-07-homestead-https-9.jpg&quot; alt=&quot;HTTPS 連線成功&quot; /&gt;&lt;/p&gt;</content:encoded></item><item><title>Laravel 登入失敗次數限制 等待時間遞增</title><link>https://star-note-lucas.me/posts/laravel-login-limiter</link><guid isPermaLink="true">https://star-note-lucas.me/posts/laravel-login-limiter</guid><description>實作進階版 Laravel 登入失敗，等待時間逐次遞增功能</description><pubDate>Sat, 20 Oct 2018 00:00:00 GMT</pubDate><content:encoded>&lt;div&gt;&lt;div&gt;Warning&lt;/div&gt;&lt;div&gt;&lt;p&gt;這篇文章的內容已經過時了，請使用 &lt;a href=&quot;https://github.com/beholdr/laravel-backoff-limiter&quot;&gt;Laravel Backoff Limiter&lt;/a&gt; 來代替。&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Laravel 本身已實現了登入失敗次數限制的功能。在使用 Laravel 的登入驗證時，登入失敗次數限制預設是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;失敗5次，&lt;code&gt;1分鐘後&lt;/code&gt; 才可再次登入&lt;/li&gt;
&lt;li&gt;之後都是每隔 &lt;code&gt;1分鐘&lt;/code&gt; 就可以再試5次&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;但這樣的設定太單純且不安全，如果要增加安全性，要實現的功能是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;　失敗3次，&lt;code&gt;1分鐘後&lt;/code&gt; 才可登入&lt;/li&gt;
&lt;li&gt;再失敗3次，&lt;code&gt;3分鐘後&lt;/code&gt; 才可登入&lt;/li&gt;
&lt;li&gt;再失敗3次，&lt;code&gt;5分鐘後&lt;/code&gt; 才可登入&lt;/li&gt;
&lt;li&gt;…&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;本文將示範此進階版登入失敗次數限制的功能。&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#版本&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;版本&lt;/h2&gt;
&lt;p&gt;Laravel 5.8 以上&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#改寫登入類別設定&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;改寫登入類別設定&lt;/h2&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;app/Http/Controllers/Auth/LoginController.php&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;?&lt;/span&gt;&lt;span&gt;php&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;use&lt;/span&gt;&lt;span&gt; Illuminate\Foundation\Auth\&lt;/span&gt;&lt;span&gt;AuthenticatesUsers&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;use&lt;/span&gt;&lt;span&gt; Illuminate\Http\&lt;/span&gt;&lt;span&gt;Request&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;use&lt;/span&gt;&lt;span&gt; App\Cache\&lt;/span&gt;&lt;span&gt;AdvancedRateLimiter&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;LoginController&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;extends&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Controller&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;use&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AuthenticatesUsers&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;/**&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* The maximum number of attempts to allow.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* &lt;/span&gt;&lt;span&gt;@var&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;protected&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;$maxAttempts&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;/**&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* The number of minutes to throttle for.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* &lt;/span&gt;&lt;span&gt;@var&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt;float&lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt;[]&lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt;float[]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;protected&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;$decayMinutes&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt;];&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;/**&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* Get the rate limiter instance.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* &lt;/span&gt;&lt;span&gt;@return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;span&gt;App&lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;span&gt;Cache&lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;span&gt;AdvancedRateLimiter&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;protected&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;limiter&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;app&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;AdvancedRateLimiter&lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;/**&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* Increment the login attempts for the user.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* &lt;/span&gt;&lt;span&gt;@param&lt;/span&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;span&gt;Illuminate&lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;span&gt;Http&lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;span&gt;Request&lt;/span&gt;&lt;span&gt;  $request&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* &lt;/span&gt;&lt;span&gt;@return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;protected&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;incrementLoginAttempts&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;Request&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;$request&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;$this-&amp;gt;&lt;/span&gt;&lt;span&gt;limiter&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;-&amp;gt;&lt;/span&gt;&lt;span&gt;hit&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;$this-&amp;gt;&lt;/span&gt;&lt;span&gt;throttleKey&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$request&lt;/span&gt;&lt;span&gt;), &lt;/span&gt;&lt;span&gt;array_map&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt;decayMinute&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;($&lt;/span&gt;&lt;span&gt;decayMinute&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;60&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;array&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt;this-&amp;gt;&lt;/span&gt;&lt;span&gt;decayMinutes&lt;/span&gt;&lt;span&gt;())&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;在 &lt;code&gt;LoginController&lt;/code&gt; 類中，增加自訂方法覆蓋 &lt;code&gt;AuthenticatesUsers&lt;/code&gt; 類原本的方法：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;limiter&lt;/code&gt; 方法是返回登入失敗次數限制的類，原本是返回 &lt;code&gt;RateLimiter&lt;/code&gt; 類(實現登入失敗次數限制的類)，但本例要擴充新方法，因此返回了我們下面創建的子類別 &lt;code&gt;AdvancedRateLimiter&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$maxAttempts&lt;/code&gt; 屬性是設定登入失敗次數。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$decayMinutes&lt;/code&gt; 屬性是登入失敗達上限後，須等待的分鐘數。但我們要實現的功能是每次都等待不一樣的時間，因此傳入一個陣列，輸入每次的等待分鐘數。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果只是要修改 Laravel 原本的錯誤次數設定，新增 &lt;code&gt;$maxAttempts&lt;/code&gt; 屬性及 &lt;code&gt;$decayMinutes&lt;/code&gt; 屬性並設定值即可完成。&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;#擴充登入失敗次數限制功能&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;擴充登入失敗次數限制功能&lt;/h2&gt;
&lt;p&gt;新增類別 &lt;code&gt;AdvancedRateLimiter&lt;/code&gt;：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;app/Cache/AdvancedRateLimiter.php&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;?&lt;/span&gt;&lt;span&gt;php&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;namespace&lt;/span&gt;&lt;span&gt; App\Cache;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;use&lt;/span&gt;&lt;span&gt; Illuminate\Cache\&lt;/span&gt;&lt;span&gt;RateLimiter&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AdvancedRateLimiter&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;extends&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;RateLimiter&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;/**&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* Increment the counter for a given key for a given decay time.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* &lt;/span&gt;&lt;span&gt;@param&lt;/span&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;  $key&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* &lt;/span&gt;&lt;span&gt;@param&lt;/span&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt;[]  $decaySeconds&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* &lt;/span&gt;&lt;span&gt;@return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;hit&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$key&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;$decaySeconds&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;60&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;is_array&lt;/span&gt;&lt;span&gt;($&lt;/span&gt;&lt;span&gt;decaySeconds&lt;/span&gt;&lt;span&gt;)) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;$this-&amp;gt;&lt;/span&gt;&lt;span&gt;cache&lt;/span&gt;&lt;span&gt;-&amp;gt;&lt;/span&gt;&lt;span&gt;has&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$key&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;:timer&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;)) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;$this-&amp;gt;&lt;/span&gt;&lt;span&gt;cache&lt;/span&gt;&lt;span&gt;-&amp;gt;&lt;/span&gt;&lt;span&gt;has&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$key&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;:step&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;)) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                    &lt;/span&gt;&lt;span&gt;$this-&amp;gt;&lt;/span&gt;&lt;span&gt;cache&lt;/span&gt;&lt;span&gt;-&amp;gt;&lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$key&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;:step&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;86400&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;} &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                    &lt;/span&gt;&lt;span&gt;$this-&amp;gt;&lt;/span&gt;&lt;span&gt;cache&lt;/span&gt;&lt;span&gt;-&amp;gt;&lt;/span&gt;&lt;span&gt;increment&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$key&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;:step&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;$step&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;$this-&amp;gt;&lt;/span&gt;&lt;span&gt;cache&lt;/span&gt;&lt;span&gt;-&amp;gt;&lt;/span&gt;&lt;span&gt;get&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$key&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;:step&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;$step&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;$step&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;count&lt;/span&gt;&lt;span&gt;($&lt;/span&gt;&lt;span&gt;decaySeconds&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;$step&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;count&lt;/span&gt;&lt;span&gt;($&lt;/span&gt;&lt;span&gt;decaySeconds&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;$decaySeconds&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;$decaySeconds&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;$step&lt;/span&gt;&lt;span&gt;];&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;parent&lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt;hit&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$key&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;$decaySeconds&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;/**&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* Clear the hits and lockout timer for the given key.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* &lt;/span&gt;&lt;span&gt;@param&lt;/span&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;  $key&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* &lt;/span&gt;&lt;span&gt;@return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;clear&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$key&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;$this-&amp;gt;&lt;/span&gt;&lt;span&gt;cache&lt;/span&gt;&lt;span&gt;-&amp;gt;&lt;/span&gt;&lt;span&gt;forget&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$key&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;:step&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;parent&lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt;clear&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$key&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;hit&lt;/code&gt; 方法是在登入錯誤後，執行登入錯誤次數記錄遞增的方法。為了實現每次登入錯誤等待的時間可以不一樣，我們讓傳入的變數 &lt;code&gt;$decayMinutes&lt;/code&gt; 可以接受傳入陣列，第一次登入錯誤等待時間為 &lt;code&gt;陣列[0]&lt;/code&gt; 的分鐘數(本例為1分鐘)，第二次為 &lt;code&gt;陣列[1]&lt;/code&gt; 的分鐘數(例：3分鐘)，而第三次為 &lt;code&gt;陣列[2]&lt;/code&gt; 的分鐘數(例：5分鐘)，之後的登入錯誤等待時間皆為陣列的最後的元素的分鐘數。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;clear&lt;/code&gt; 方法是成功登入後，將時間、次數重設，下一次再登入錯誤後，將從頭開始計數。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;此時登入失敗次數限制的功能已改寫完成，再次登入並輸入錯誤的帳號或密碼，重複數次即可看到結果。&lt;/p&gt;</content:encoded></item></channel></rss>