<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>inorisense | 部落格</title><description/><link>https://inorisense.casa/</link><language>zh-TW</language><item><title>[MCV] Taccia Hokusai Sabimidori (Rusty Turquoise) Ink</title><link>https://inorisense.casa/zh-tw/blog/taccia-hokusai-sabimidori-ink/</link><guid isPermaLink="true">https://inorisense.casa/zh-tw/blog/taccia-hokusai-sabimidori-ink/</guid><pubDate>Mon, 30 Jun 2025 00:00:00 GMT</pubDate><content:encoded>&lt;aside&gt; &lt;p&gt;This article has not been translated into English yet.&lt;/p&gt; &lt;a href=&quot;https://inorisense.casa/en/blog/taccia-hokusai-sabimidori-ink/&quot;&gt;Read the original article&lt;/a&gt; &lt;/aside&gt;</content:encoded></item><item><title>[MCV] Intro to My Calligraphy Voyage</title><link>https://inorisense.casa/zh-tw/blog/intro-to-my-calligraphy-voyage/</link><guid isPermaLink="true">https://inorisense.casa/zh-tw/blog/intro-to-my-calligraphy-voyage/</guid><pubDate>Tue, 10 Jun 2025 00:00:00 GMT</pubDate><content:encoded>&lt;aside&gt; &lt;p&gt;This article has not been translated into English yet.&lt;/p&gt; &lt;a href=&quot;https://inorisense.casa/en/blog/intro-to-my-calligraphy-voyage/&quot;&gt;Read the original article&lt;/a&gt; &lt;/aside&gt;</content:encoded></item><item><title>[MCV] My Equipment for Calligraphy</title><link>https://inorisense.casa/zh-tw/blog/my-equipment-for-calligraphy/</link><guid isPermaLink="true">https://inorisense.casa/zh-tw/blog/my-equipment-for-calligraphy/</guid><pubDate>Tue, 10 Jun 2025 00:00:00 GMT</pubDate><content:encoded>&lt;aside&gt; &lt;p&gt;This article has not been translated into English yet.&lt;/p&gt; &lt;a href=&quot;https://inorisense.casa/en/blog/my-equipment-for-calligraphy/&quot;&gt;Read the original article&lt;/a&gt; &lt;/aside&gt;</content:encoded></item><item><title>型別驗證與執行期驗證（上篇）：模型、驗證器和資料</title><link>https://inorisense.casa/zh-tw/blog/type-safety-and-runtime-safety-part-1/</link><guid isPermaLink="true">https://inorisense.casa/zh-tw/blog/type-safety-and-runtime-safety-part-1/</guid><pubDate>Tue, 12 Mar 2024 00:00:00 GMT</pubDate><content:encoded>&lt;aside&gt; &lt;p&gt;This article has not been translated into English yet.&lt;/p&gt; &lt;a href=&quot;https://inorisense.casa/en/blog/type-safety-and-runtime-safety-part-1/&quot;&gt;Read the original article&lt;/a&gt; &lt;/aside&gt;</content:encoded></item><item><title>型別驗證與執行期驗證（下篇）：頁面、路由守衛和 API</title><link>https://inorisense.casa/zh-tw/blog/type-safety-and-runtime-safety-part-2/</link><guid isPermaLink="true">https://inorisense.casa/zh-tw/blog/type-safety-and-runtime-safety-part-2/</guid><pubDate>Tue, 12 Mar 2024 00:00:00 GMT</pubDate><content:encoded>&lt;aside&gt; &lt;p&gt;This article has not been translated into English yet.&lt;/p&gt; &lt;a href=&quot;https://inorisense.casa/en/blog/type-safety-and-runtime-safety-part-2/&quot;&gt;Read the original article&lt;/a&gt; &lt;/aside&gt;</content:encoded></item><item><title>使用 NPM 腳本建立 Hugo 文章</title><link>https://inorisense.casa/zh-tw/blog/create-hugo-post-with-npm-script/</link><guid isPermaLink="true">https://inorisense.casa/zh-tw/blog/create-hugo-post-with-npm-script/</guid><pubDate>Mon, 11 Mar 2024 00:00:00 GMT</pubDate><content:encoded>&lt;div&gt;&lt;h2 id=&quot;前提&quot;&gt;前提&lt;/h2&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;基礎知識：NPM, Hugo, JavaScript, shell script&lt;/li&gt;
&lt;li&gt;預先安裝 VS Code, NPM CLI, Hugo CLI&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h2 id=&quot;前言&quot;&gt;前言&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;使用 Hugo CLI 建立文章對我而言是一項繁瑣的工作。因為我總是使用文章樣板（&lt;a href=&quot;https://gohugo.io/content-management/archetypes/&quot;&gt;archetype&lt;/a&gt;）建立文章，並將其放置在巢狀階層的資料匣中。例如，在建立本篇文章時，必須在終端機中輸入以下指令：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;hugo&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;--kind&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;develop&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;posts/_developer/create-hugo-post-with-npm-script&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;問題在於，我總是忘記擁有多少種文章樣本，以及資料匣結構目前長什麼樣子。資料匣結構可能是動態的，可能非常頻繁地調整。此外，我非常喜歡 VSCode 在側邊選單「Explorer」中提供的 NPM SCRIPTS 功能，如下所示的截圖：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;npm script in side menu&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;592&quot; height=&quot;388&quot; src=&quot;https://inorisense.casa/_astro/npm-script-in-side-menu.D-w5nwJb_ZXGxAA.webp&quot;&gt;&lt;/p&gt;
&lt;p&gt;這個功能，我個人稱之為「點擊執行腳本」，若使用者無法記住或忘記腳本怎麼寫，此時就非常方便。但據我所知，它似乎只支援 Node 套件管理，也就是 NPM。為了將「點擊執行腳本」功能與 Hugo CLI 結合使用，需要使用 NPM 作為中間件（middleware），縱然 Hugo 靜態網頁在任何時候都不需要 NPM 或任何 Node 套件。讓我們開始吧。&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;打造-hugo-dev-伺服器腳本&quot;&gt;打造 Hugo Dev 伺服器腳本&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;首先使用 &lt;code dir=&quot;auto&quot;&gt;npm init&lt;/code&gt; 初始化 NPM。&lt;/p&gt;
&lt;p&gt;然後，在將此腳本添加到您的 &lt;code dir=&quot;auto&quot;&gt;package.json&lt;/code&gt; 後，讓我們嘗試透過 NPM 啟動 Hugo 本地伺服器：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;span data-icon=&quot;i-material-icon-theme:nodejs&quot;&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;&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;span&gt;{&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;dev&lt;/span&gt;&lt;span&gt;&quot;&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;hugo serve -D&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;/figure&gt;&lt;/div&gt;
&lt;p&gt;在終端機中，輸入 &lt;code dir=&quot;auto&quot;&gt;npm run dev&lt;/code&gt;，或者只需在側邊選單點擊 NPM 腳本來執行：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;npm run dev&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1184&quot; height=&quot;320&quot; src=&quot;https://inorisense.casa/_astro/npm-run-dev.DhM4OajX_Z25KlIz.webp&quot;&gt;&lt;/p&gt;
&lt;p&gt;成功執行 Hugo CLI！✨&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;打造建立文章腳本&quot;&gt;打造建立文章腳本&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;因此，NPM 腳本完美地呼叫了 Hugo CLI。然後，讓我們嘗試實現最終目標：建立一篇文章。&lt;/p&gt;
&lt;p&gt;首先，我們需要安裝兩個套件：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/SBoudrias/Inquirer.js&quot;&gt;@inquirer/prompts&lt;/a&gt;：利用 JavaScript 腳本在終端機裡，打造容易操作的介面&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/nicksrandall/inquirer-directory&quot;&gt;inquirer-directory&lt;/a&gt;：在終端機裡也能夠輕鬆操作資料匣路徑&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;接下來，在根目錄中建立了一個 JavaScript 檔案 &lt;code dir=&quot;auto&quot;&gt;createPost.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;&quot;&lt;/span&gt;&lt;span&gt;use 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;const&lt;/span&gt;&lt;span&gt; inquirer &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;&quot;&lt;/span&gt;&lt;span&gt;inquirer&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;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt; input&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; select&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; confirm &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;&quot;&lt;/span&gt;&lt;span&gt;@inquirer/prompts&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;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt; execSync &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;&quot;&lt;/span&gt;&lt;span&gt;child_process&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;const&lt;/span&gt;&lt;span&gt; inquirerDirectory &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;&quot;&lt;/span&gt;&lt;span&gt;inquirer-directory&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;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; BASE_PATH &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;./content&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;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;inquirer&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;registerPrompt&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;directory&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; inquirerDirectory)&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;exec&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;commands&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&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;execSync&lt;/span&gt;&lt;span&gt;(commands&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;stdio&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;inherit&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;shell&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;span&gt;;&lt;/span&gt;&lt;/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;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* Create post script&lt;/span&gt;&lt;/div&gt;&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;see&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;https://github.com/SBoudrias/Inquirer.js&lt;/span&gt;&lt;/div&gt;&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;see&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;https://github.com/nicksrandall/inquirer-directory&lt;/span&gt;&lt;/div&gt;&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;async&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;{&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; archeType &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; &lt;/span&gt;&lt;span&gt;select&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;message&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;Select a archetype&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;choices&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;/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;name&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;Basic&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;value&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;basic&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;description&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;Basic post&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;/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;name&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;Dev&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;value&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;dev&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;description&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;Post for developer.&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;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;new&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;/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;name&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;Garden&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;value&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;garden&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;description&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;Note for digital garden.&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;/div&gt;&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;/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; title &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; &lt;/span&gt;&lt;span&gt;input&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;message&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;Enter your post title&lt;/span&gt;&lt;span&gt;&quot;&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;/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; directory &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; inquirer&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;prompt&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;{&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;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;directory&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;name&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;path&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;message&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;Please choose post directory.&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;basePath&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; BASE_PATH&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&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;/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; answer &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; &lt;/span&gt;&lt;span&gt;confirm&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;message&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;Confirm create the post?&lt;/span&gt;&lt;span&gt;&quot;&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; &lt;/span&gt;&lt;span&gt;false&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;/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; (answer) &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;exec&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;hugo new --kind &lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;archeType&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;directory&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;path&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;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;exec&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;open &lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;BASE_PATH&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;directory&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;path&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;/index.md&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;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;在腳本中，我提供了 3 個問題，以及一些操作：&lt;/p&gt;
&lt;ol&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;執行 Hugo 文章建立腳本。&lt;/li&gt;
&lt;li&gt;最後，打開我們建立的文件。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;完成精心打造的腳本後，然後將其加入到我們的 &lt;code dir=&quot;auto&quot;&gt;package.json&lt;/code&gt; 中：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;span data-icon=&quot;i-material-icon-theme:nodejs&quot;&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;&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;span&gt;{&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;create&lt;/span&gt;&lt;span&gt;&quot;&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;node createPost.js&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;/figure&gt;&lt;/div&gt;
&lt;p&gt;執行 &lt;code dir=&quot;auto&quot;&gt;npm run create&lt;/code&gt;，以下是執行結果：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;npm run create&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1868&quot; height=&quot;540&quot; src=&quot;https://inorisense.casa/_astro/npm-run-create.MC_aWYyA_ZmM72W.webp&quot;&gt;&lt;/p&gt;
&lt;p&gt;以上，開發愉快。&lt;/p&gt;</content:encoded><category>npm</category><category>hugo</category><category>JavaScript</category></item><item><title>[筆記] 以狀態機實作多階段表單</title><link>https://inorisense.casa/zh-tw/blog/implementation-of-state-machine-and-multi-step-form/</link><guid isPermaLink="true">https://inorisense.casa/zh-tw/blog/implementation-of-state-machine-and-multi-step-form/</guid><pubDate>Sun, 01 Oct 2023 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;基礎知識： &lt;code dir=&quot;auto&quot;&gt;Vue&lt;/code&gt;、&lt;code dir=&quot;auto&quot;&gt;TypeScript&lt;/code&gt;、&lt;code dir=&quot;auto&quot;&gt;XState&lt;/code&gt;、&lt;code dir=&quot;auto&quot;&gt;vee-validate&lt;/code&gt;、&lt;code dir=&quot;auto&quot;&gt;zod&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div&gt;&lt;h2 id=&quot;初步構想&quot;&gt;初步構想&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;時常看到「多階段」的表單流程，也就是會將一個本來很長的表單&lt;strong&gt;拆分成好幾個部分&lt;/strong&gt;來填寫，相較冗長的一整頁表單，使用者在填寫時比較沒有心理上的負擔。&lt;/p&gt;
&lt;p&gt;跟表單相關的開源套件很多，而我選用以下幾種來處理跟表單相關的任務：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;表單狀態管理：這裡因為範例使用 Vue，所以以 &lt;a href=&quot;https://vee-validate.logaretm.com/v4/&quot;&gt;vee-validate&lt;/a&gt; 管理表單所有 &lt;code dir=&quot;auto&quot;&gt;input&lt;/code&gt;、&lt;code dir=&quot;auto&quot;&gt;select&lt;/code&gt; 等元件的渲染狀態&lt;/li&gt;
&lt;li&gt;表單驗證：表單驗證的部分，搭配 &lt;a href=&quot;https://vee-validate.logaretm.com/v4/&quot;&gt;vee-validate&lt;/a&gt; 官方推薦的 &lt;a href=&quot;https://zod.dev&quot;&gt;zod&lt;/a&gt; 來處理驗證邏輯&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;以上是表單的部分，再來的重點是，&lt;strong&gt;要如何設計這個「多階段」的架構？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;若將所階段裡面的欄位都合在一個表單，如下圖：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://inorisense.casa/_astro/chart-01.W85bJ9Ft_21rDBe.webp&quot; alt=&quot;chart 01&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2438&quot; height=&quot;684&quot;&gt;&lt;/p&gt;
&lt;p&gt;在切換階段的時候決定顯示哪些欄位，然而在&lt;strong&gt;切換下一階段&lt;/strong&gt;時，又要&lt;strong&gt;只驗證&lt;/strong&gt;當下的那幾個欄位，這樣會造成複雜的驗證邏輯。&lt;/p&gt;
&lt;p&gt;於是我想到將每個階段獨立成自己的表單，所以所有驗證都不再是局部驗證，而是對表單的所有欄位（例如階段一表單的所有欄位）來做驗證：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://inorisense.casa/_astro/chart-02.toOf8nWX_1ywupL.webp&quot; alt=&quot;chart 02&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2402&quot; height=&quot;636&quot;&gt;&lt;/p&gt;
&lt;p&gt;由於拆分成多個表單，簡化了表單的驗證邏輯，不用再做多餘的判斷（局部欄位驗證）了。&lt;strong&gt;每個&lt;/strong&gt;表單元件的職責，如下：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;欄位狀態管理&lt;/li&gt;
&lt;li&gt;所有欄位驗證&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;將上述任務委派給表單元件處理之後，剩下待處理的邏輯是：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;顯示現在是何種階段表單元件的&lt;strong&gt;狀態&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;送出表單資料，執行非同步請求&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;由於「第一階段」的下一步只能到「第二階段」，而不行到「第三階段」或是「確認階段（Step Confirm）」，因此我想到利用有限狀態機來解決上述的兩個問題，而嘗試使用以實作有限狀態機很有名的 &lt;a href=&quot;https://xstate.js.org&quot;&gt;XState&lt;/a&gt; 套件來處理這些任務。&lt;/p&gt;
&lt;p&gt;因此，職責分配圖如下：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://inorisense.casa/_astro/chart-03.9MaRbg-3_Z2a96lR.webp&quot; alt=&quot;chart 03&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2498&quot; height=&quot;1688&quot;&gt;&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;職責分配&quot;&gt;職責分配&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;前一段大致描述了初步的想法，這裡整理一下職責分配的規劃，分為兩個部分：&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;狀態機&quot;&gt;狀態機&lt;/h3&gt;&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;階段流程控制（要顯示哪一階段的表單）&lt;/li&gt;
&lt;li&gt;儲存所有表單資料&lt;/li&gt;
&lt;li&gt;送出資料，執行非同步請求&lt;/li&gt;
&lt;li&gt;非同步請求的等待狀態（loading）、錯誤狀態&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;以上皆由 &lt;a href=&quot;https://xstate.js.org&quot;&gt;XState&lt;/a&gt; 實作&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;各階段表單元件&quot;&gt;各階段表單元件&lt;/h3&gt;&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;表單欄位狀態管理（由 &lt;a href=&quot;https://vee-validate.logaretm.com/v4/&quot;&gt;vee-validate&lt;/a&gt; 實作）&lt;/li&gt;
&lt;li&gt;表單欄位驗證（由 &lt;a href=&quot;https://zod.dev&quot;&gt;zod&lt;/a&gt; 實作）&lt;/li&gt;
&lt;li&gt;表單送出（submit）事件與表單資料（由 &lt;a href=&quot;https://vee-validate.logaretm.com/v4/&quot;&gt;vee-validate&lt;/a&gt; 實作）&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;&lt;h2 id=&quot;實作簡述&quot;&gt;實作簡述&lt;/h2&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;表單元件&quot;&gt;表單元件&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;表單元件，以第一階段 &lt;code dir=&quot;auto&quot;&gt;Form1.vue&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;#x3C;template&gt;&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;#x3C;div&gt;&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;#x3C;h2&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;formTitle&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt;Choose channels you like&lt;/span&gt;&lt;span&gt;&amp;#x3C;/h2&gt;&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;#x3C;form&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;form&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;@submit&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;onSubmit&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&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;#x3C;div&gt;&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;#x3C;input&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;checkbox&lt;/span&gt;&lt;span&gt;&quot;&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;discovery&lt;/span&gt;&lt;span&gt;&quot;&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;&quot;&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;v-model&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;channels&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/&gt;&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;#x3C;label&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;label&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;for&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;discovery&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt;Discovery&lt;/span&gt;&lt;span&gt;&amp;#x3C;/label&gt;&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;#x3C;/div&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;{/* other inputs... */}&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;#x3C;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;v-if&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;errors.channels&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;error&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt;{{ errors.channels }}&lt;/span&gt;&lt;span&gt;&amp;#x3C;/div&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;#x3C;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;buttonGroup&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&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;#x3C;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;button&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;submit&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt;next step&lt;/span&gt;&lt;span&gt;&amp;#x3C;/button&gt;&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;#x3C;/div&gt;&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;#x3C;/form&gt;&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;#x3C;/div&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;#x3C;/template&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;#x3C;script&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;setup&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;ts&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;import&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; &lt;/span&gt;&lt;span&gt;Form1Model&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;}&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;&quot;&lt;/span&gt;&lt;span&gt;@/types&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;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;toTypedSchema&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;}&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;&quot;&lt;/span&gt;&lt;span&gt;@vee-validate/zod&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;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;useForm&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;useField&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;}&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;&quot;&lt;/span&gt;&lt;span&gt;vee-validate&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;import&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;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;zod&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;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;interface&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;initialValues&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; Form1Model&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/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;interface&lt;/span&gt;&lt;span&gt; Emits &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;event&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;next&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; values&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; Form1Model&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&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;/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; props &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;withDefaults&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;defineProps&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;Props&lt;/span&gt;&lt;span&gt;&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;class&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&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;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; emits &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;defineEmits&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;Emits&lt;/span&gt;&lt;span&gt;&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;const&lt;/span&gt;&lt;span&gt; validationSchema &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;toTypedSchema&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&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;channels&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;array&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;nonempty&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Please choose at least one channel.&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;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;{&lt;/span&gt;&lt;span&gt; handleSubmit&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; errors&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; values &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;useForm&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;Form1Model&lt;/span&gt;&lt;span&gt;&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;initialValues&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;initialValues&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;validationSchema&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&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;/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; value&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; channels &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;useField&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;number&lt;/span&gt;&lt;span&gt;[]&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;channels&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;/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; onSubmit &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;handleSubmit&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;values&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;emits&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;next&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; values))&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;#x3C;/script&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;表單的欄位初始值（initial values）由狀態機提供，經由 &lt;code dir=&quot;auto&quot;&gt;props&lt;/code&gt; 傳進來；然後表單在送出的時候，再發事件出去讓狀態機處理，&lt;strong&gt;一個人只負責一件事&lt;/strong&gt;，可謂「單一職責原則」的精神。&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;外層元件&quot;&gt;外層元件&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;各階段的表單的顯示控制，在外層 &lt;code dir=&quot;auto&quot;&gt;MultiStepForm.vue&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;#x3C;template&gt;&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;#x3C;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;`h-full w-full ${props.class}`&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&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;#x3C;h1&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;title&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt;Multi Step Form Example&lt;/span&gt;&lt;span&gt;&amp;#x3C;/h1&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;#x3C;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 grid-cols-2 gap-x-6&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&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;#x3C;div&gt;&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;#x3C;h2&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;subTitle&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt;Form Component&lt;/span&gt;&lt;span&gt;&amp;#x3C;/h2&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;#x3C;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;p-4 border border-slate-700 rounded-lg&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&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;#x3C;&lt;/span&gt;&lt;span&gt;Form1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;v-if&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;state.matches(&apos;step1&apos;)&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;@next&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;send(&apos;NEXT_TO_STEP_2&apos;, { formValues: $event })&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;@prev&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;send(&apos;PREV&apos;)&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;:initial-values&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;state.context.form1Values&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;/&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;#x3C;&lt;/span&gt;&lt;span&gt;Form2&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;v-if&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;state.matches(&apos;step2&apos;)&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;@next&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;send(&apos;NEXT_TO_STEP_3&apos;, { formValues: $event })&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;@prev&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;send(&apos;PREV&apos;)&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;:initial-values&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;state.context.form2Values&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;/&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;#x3C;&lt;/span&gt;&lt;span&gt;Form3&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;v-if&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;state.matches(&apos;step3&apos;)&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;@next&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;send(&apos;NEXT_TO_STEP_CONFIRM&apos;, { formValues: $event })&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;@prev&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;send(&apos;PREV&apos;)&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;:initial-values&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;state.context.form3Values&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;/&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;#x3C;&lt;/span&gt;&lt;span&gt;FormConfirm&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;v-if&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;state.matches(&apos;stepConfirm&apos;)&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;@prev&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;send(&apos;PREV&apos;)&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;@submit&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;send(&apos;SUBMIT&apos;)&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;:is-submitting&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;state.matches(&apos;stepConfirm.submitting&apos;)&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;:error&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;state.context.error&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;:machine-context&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;state.context&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;:payload&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;state.context.payload&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;/&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;#x3C;&lt;/span&gt;&lt;span&gt;FormComplete&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;v-if&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;state.matches(&apos;complete&apos;)&lt;/span&gt;&lt;span&gt;&quot;&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;&quot;&lt;/span&gt;&lt;span&gt;send(&apos;RESTART&apos;)&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; /&gt;&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;#x3C;/div&gt;&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;#x3C;/div&gt;&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;#x3C;div&gt;&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;#x3C;p&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;subTitle&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt;Current Machine Context&lt;/span&gt;&lt;span&gt;&amp;#x3C;/p&gt;&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;#x3C;pre&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;preBlock&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt;{{ state.context }}&lt;/span&gt;&lt;span&gt;&amp;#x3C;/pre&gt;&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;#x3C;/div&gt;&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;#x3C;/div&gt;&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;#x3C;/div&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;#x3C;/template&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;#x3C;script&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;setup&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;ts&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&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;useMachine&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;}&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;&quot;&lt;/span&gt;&lt;span&gt;@xstate/vue&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;import&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Form1&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;&quot;&lt;/span&gt;&lt;span&gt;@/components/Form1.vue&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;import&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Form2&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;&quot;&lt;/span&gt;&lt;span&gt;@/components/Form2.vue&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;import&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Form3&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;&quot;&lt;/span&gt;&lt;span&gt;@/components/Form3.vue&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;import&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;FormConfirm&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;&quot;&lt;/span&gt;&lt;span&gt;@/components/FormConfirm.vue&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;import&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;FormComplete&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;&quot;&lt;/span&gt;&lt;span&gt;@/components/FormComplete.vue&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;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;multiStepFormMachine&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;}&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;&quot;&lt;/span&gt;&lt;span&gt;@/multiStepFormMachine&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;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;interface&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;class&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;/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;interface&lt;/span&gt;&lt;span&gt; Emits &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;event&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;click&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&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;/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; props &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;withDefaults&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;defineProps&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;Props&lt;/span&gt;&lt;span&gt;&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;class&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&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;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; emits &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;defineEmits&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;Emits&lt;/span&gt;&lt;span&gt;&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;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt; state&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; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;useMachine&lt;/span&gt;&lt;span&gt;(multiStepFormMachine)&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;#x3C;/script&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;表單狀態機&quot;&gt;表單狀態機&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;接著是狀態機，我將狀態機獨立成一個檔案 &lt;code dir=&quot;auto&quot;&gt;multiStepFormMachine.ts&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;{&lt;/span&gt;&lt;span&gt; assign&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; createMachine &lt;/span&gt;&lt;span&gt;}&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;&quot;&lt;/span&gt;&lt;span&gt;xstate&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;import&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; Form1Model&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; Form2Model&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; Form3Model&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; SubmitData &lt;/span&gt;&lt;span&gt;}&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;&quot;&lt;/span&gt;&lt;span&gt;./types&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;import&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt; FORM_1_INITIAL_VALUES&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; FORM_2_INITIAL_VALUES&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; FORM_3_INITIAL_VALUES &lt;/span&gt;&lt;span&gt;}&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;&quot;&lt;/span&gt;&lt;span&gt;./default&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;import&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt; sendFormData &lt;/span&gt;&lt;span&gt;}&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;&quot;&lt;/span&gt;&lt;span&gt;./utils&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;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;MachineEvent&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&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; type&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;NEXT_TO_STEP_2&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span&gt; formValues&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Form1Model&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&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; type&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;NEXT_TO_STEP_3&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span&gt; formValues&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Form2Model&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&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; type&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;NEXT_TO_STEP_CONFIRM&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span&gt; formValues&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Form3Model&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&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; type&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;PREV&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;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;&quot;&lt;/span&gt;&lt;span&gt;SUBMIT&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;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;&quot;&lt;/span&gt;&lt;span&gt;RESTART&lt;/span&gt;&lt;span&gt;&quot;&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;/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; &lt;/span&gt;&lt;span&gt;MachineContext&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;form1Values&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Form1Model&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;form2Values&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Form2Model&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;form3Values&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Form3Model&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;payload&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;SubmitData&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;error&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;/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; INITIAL_MACHINE_CONTEXT&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;MachineContext&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;form1Values&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; FORM_1_INITIAL_VALUES&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;form2Values&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; FORM_2_INITIAL_VALUES&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;form3Values&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; FORM_3_INITIAL_VALUES&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;payload&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;error&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;/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;type&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;MachineState&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&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; context&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;MachineContext&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;&quot;&lt;/span&gt;&lt;span&gt;step1&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;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;MachineContext&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;&quot;&lt;/span&gt;&lt;span&gt;step2&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;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;MachineContext&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;&quot;&lt;/span&gt;&lt;span&gt;step3&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;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;MachineContext&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;&quot;&lt;/span&gt;&lt;span&gt;stepConfirm&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;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;MachineContext&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;&quot;&lt;/span&gt;&lt;span&gt;stepConfirm.submitting&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;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;MachineContext&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;&quot;&lt;/span&gt;&lt;span&gt;complete&lt;/span&gt;&lt;span&gt;&quot;&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;/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;const&lt;/span&gt;&lt;span&gt; multiStepFormMachine &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;createMachine&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;MachineContext&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;MachineEvent&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;MachineState&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/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;id&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;multiStepForm&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;initial&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;step1&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;context&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; INITIAL_MACHINE_CONTEXT&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;states&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;step1&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;on&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;NEXT_TO_STEP_2&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;target&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;step2&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;actions&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;assign&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;              &lt;/span&gt;&lt;span&gt;form1Values&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;span&gt;,&lt;/span&gt;&lt;span&gt; event&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; event&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;formValues&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&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;/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;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;step2&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;on&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;NEXT_TO_STEP_3&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;target&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;step3&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;actions&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;assign&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;              &lt;/span&gt;&lt;span&gt;form2Values&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;span&gt;,&lt;/span&gt;&lt;span&gt; event&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; event&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;formValues&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&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;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&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; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&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;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;step1&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;/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;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;step3&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;on&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;NEXT_TO_STEP_CONFIRM&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;target&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;stepConfirm&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;actions&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;assign&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;              &lt;/span&gt;&lt;span&gt;form3Values&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;span&gt;,&lt;/span&gt;&lt;span&gt; event&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; event&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;formValues&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&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;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&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; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&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;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;step2&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;/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;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;stepConfirm&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;initial&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;preSubmit&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;states&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;preSubmit&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;entry&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;assign&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;              &lt;/span&gt;&lt;span&gt;payload&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;span&gt;,&lt;/span&gt;&lt;span&gt; event&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&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;context&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;form1Values&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&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;form2Values&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&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;form3Values&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&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;on&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;SUBMIT&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;target&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;submitting&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;/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;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;submitting&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;invoke&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;src&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;formSubmit&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;onDone&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;target&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;#multiStepForm.complete&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;actions&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;resetContext&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;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;onError&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;target&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;errored&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;actions&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;assign&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                  &lt;/span&gt;&lt;span&gt;error&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;span&gt;,&lt;/span&gt;&lt;span&gt; event&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; event&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;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;}&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/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;span&gt;          &lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;errored&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;on&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;SUBMIT&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;target&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;submitting&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;/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;span&gt;        &lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;on&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;PREV&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;target&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;step3&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;/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;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;complete&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;entry&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;resetContext&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;on&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;RESTART&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;target&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;step1&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;/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;span&gt;    &lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/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;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;actions&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;resetContext&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;assign&lt;/span&gt;&lt;span&gt;(INITIAL_MACHINE_CONTEXT)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/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;services&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;formSubmit&lt;/span&gt;&lt;span&gt;:&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;(&lt;/span&gt;&lt;span&gt;context&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; event&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&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; (context&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;payload) &lt;/span&gt;&lt;span&gt;{&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;await&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sendFormData&lt;/span&gt;&lt;span&gt;(context&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;payload)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&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;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;await&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;Promise&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;resolve&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; reject&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;reject&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Context cannot be null.&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;/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;span&gt;  &lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/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;/figure&gt;&lt;/div&gt;
&lt;p&gt;狀態機的流程操作，可以參考這個可視化頁面：&lt;a href=&quot;https://stately.ai/registry/editor/3f09f529-4eb3-493d-bdef-12dfb26db0e0?machineId=8f84d60f-3636-4da1-bb8e-74ae016f7c19&amp;#x26;mode=Design&quot;&gt;multi-step-form | Stately&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;以上是狀態機的程式碼，有點冗長還請見諒 🙏&lt;/p&gt;
&lt;p&gt;主要職責是：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code dir=&quot;auto&quot;&gt;Form1&lt;/code&gt; 會發出 &lt;code dir=&quot;auto&quot;&gt;NEXT_TO_STEP_2&lt;/code&gt; 事件進到 &lt;code dir=&quot;auto&quot;&gt;Form2&lt;/code&gt;，或 &lt;code dir=&quot;auto&quot;&gt;Form2&lt;/code&gt; 發出 &lt;code dir=&quot;auto&quot;&gt;PREV&lt;/code&gt; 事件回到 &lt;code dir=&quot;auto&quot;&gt;Form1&lt;/code&gt;，以此類推。&lt;/li&gt;
&lt;li&gt;&lt;code dir=&quot;auto&quot;&gt;FormConfirm&lt;/code&gt; 會發 &lt;code dir=&quot;auto&quot;&gt;SUBMIT&lt;/code&gt; 事件告訴狀態機要執行非同步請求，將表單的資料送出。&lt;/li&gt;
&lt;li&gt;狀態機的 &lt;code dir=&quot;auto&quot;&gt;context&lt;/code&gt; 中，存放 &lt;code dir=&quot;auto&quot;&gt;Form1&lt;/code&gt; 及其他表單元件的欄位資料，以及最後要送出請求的酬載（payload），此外還有送出非同步請求的狀態（loading、error）&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;&lt;h2 id=&quot;最後結果&quot;&gt;最後結果&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;以下是最後實作出來的結果，左邊是表單元件，右邊則顯示目前狀態機 &lt;code dir=&quot;auto&quot;&gt;context&lt;/code&gt; 的狀態，能清楚知道何時會更新 &lt;code dir=&quot;auto&quot;&gt;context&lt;/code&gt; 的資料&lt;/p&gt;
&lt;iframe src=&quot;http://kazettique.github.io/multi-step-form/&quot;&gt;&lt;/iframe&gt;
&lt;p&gt;&lt;a href=&quot;http://kazettique.github.io/multi-step-form/&quot;&gt;畫面&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;所有程式碼請參考&lt;a href=&quot;https://github.com/kazettique/multi-step-form&quot;&gt;這裡&lt;/a&gt;&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;小記&quot;&gt;小記&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;以上是多階段表單的一些構想，利用狀態機將表單的職責單一化，也清楚將邏輯切割並分派給各部分處理。&lt;/p&gt;
&lt;p&gt;這是第一次真正自己&lt;del&gt;認真&lt;/del&gt;寫狀態機，也還在摸索學習的階段，若有任何問題歡迎留言告訴我 😎&lt;/p&gt;
&lt;p&gt;感謝收看 🙏&lt;/p&gt;</content:encoded><category>JavaScript</category><category>TypeScript</category><category>XState</category><category>Vue</category></item><item><title>漢語拼音輸入法介紹與實用技巧</title><link>https://inorisense.casa/zh-tw/blog/introduction-of-pinyin-ime/</link><guid isPermaLink="true">https://inorisense.casa/zh-tw/blog/introduction-of-pinyin-ime/</guid><pubDate>Thu, 03 Aug 2023 00:00:00 GMT</pubDate><content:encoded>&lt;div&gt;&lt;h2 id=&quot;前言&quot;&gt;前言&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;對於使用繁體中文的人來說（尤其是在台灣），漢語拼音或許是一個熟悉又陌生的存在。&lt;/p&gt;
&lt;p&gt;我跟大部分的台灣人一樣，從小學的是注音，電腦打字自然也是用注音。直到 2014 那一年（&lt;del&gt;可能吃錯藥，或是頭撞到&lt;/del&gt;），開始了學習拼音的道路。然後就一路使用到現在。正好公司月會要介紹這個主題，所以正好順便記錄一下自己的使用體驗及心路歷程。&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;輸入法的市占率&quot;&gt;輸入法的市占率&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;我想要查詢一下近年台灣各輸入法使用的比例，卻沒什麼相關的統計數據，只有找到 2011 年的一篇統計&lt;a href=&quot;https://www.pollster.com.tw/Aboutlook/lookview_item.aspx?ms_sn=1476#:~:text=%E5%88%B0%E5%BA%95%E6%B0%91%E7%9C%BE%E6%9C%80%E5%B8%B8%E4%BD%BF%E7%94%A8,%E3%80%8C%E5%A4%A7%E6%98%93%E3%80%8D%E7%AD%89%E7%AD%89%E3%80%82&quot;&gt;報告&lt;/a&gt;：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.pollster.com.tw/uploadmessage/2414-1.bmp&quot; alt=&quot;usage percentage&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.pollster.com.tw/Aboutlook/lookview_item.aspx?ms_sn=1476#:~:text=%E5%88%B0%E5%BA%95%E6%B0%91%E7%9C%BE%E6%9C%80%E5%B8%B8%E4%BD%BF%E7%94%A8,%E3%80%8C%E5%A4%A7%E6%98%93%E3%80%8D%E7%AD%89%E7%AD%89%E3%80%82&quot;&gt;來源：Pollster 波仕特線上市調網&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;我想各輸入法在市場上使用率的消長，也不影響注音輸入法的地位，市佔率大幅領先其他各家。無論是老牌的倉頡、或曾經紅極一時的嘸蝦米輸入法，大概也無法追趕上注音的優勢地位。&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;中文輸入法的種類&quot;&gt;中文輸入法的種類&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;中文輸入法大致分為兩類：以字音為基礎的輸入法，稱為「拼音輸入法」；以字形為基礎的則稱為「字形輸入法」。&lt;/p&gt;
&lt;p&gt;以大家比較熟悉的注音與倉頡來比較，其實可以很明確地分別兩者的差異。注音使用注音符號來拼寫文字（對，聽起來是廢話），也就是以中文字的發音來組合文字；倉頡利用各種字根去拼寫文字，以字形來組合文字。&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;字形輸入法&quot;&gt;字形輸入法&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;字形輸入法，是依據字形拼寫的輸入法，簡單來說，&lt;strong&gt;會寫&lt;/strong&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;/ul&gt;
&lt;div&gt;&lt;h3 id=&quot;拼音輸入法&quot;&gt;拼音輸入法&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;拼音輸入法，則以字音拼寫的輸入，&lt;strong&gt;會唸&lt;/strong&gt;就拼得出來。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;（漢語）拼音輸入法&lt;/li&gt;
&lt;li&gt;注音輸入法&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;其實常見的注音輸入法也是拼音輸入法的其中一種，而在中國、新加坡等地最普及的「漢語拼音輸入法」，就是平常所稱呼的「拼音輸入法」。兩者的差異很明顯，一個是用注音符號來拼音，另一個則是用羅馬拼音（英文字）來拼音。&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;優點與缺點&quot;&gt;優點與缺點&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;以下整理了依漢語拼音輸入法的使用體驗，整理出的優缺點：&lt;/p&gt;













































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;優點&lt;/th&gt;&lt;th&gt;缺點&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;詞彙首碼略拼&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;/tr&gt;&lt;tr&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;/tr&gt;&lt;tr&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;/tr&gt;&lt;tr&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;/tr&gt;&lt;tr&gt;&lt;td&gt;九宮格模式（行動裝置）&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;div&gt;&lt;h3 id=&quot;優點&quot;&gt;優點&lt;/h3&gt;&lt;/div&gt;
&lt;div&gt;&lt;h4 id=&quot;詞彙首碼略拼&quot;&gt;詞彙首碼略拼&lt;/h4&gt;&lt;/div&gt;
&lt;p&gt;這是一個拼音輸入法很普遍的功能。「首碼略拼」是我自己創造的名詞。也&lt;a href=&quot;http://benyi.logdown.com/posts/2015/08/18/typing-on-the-iphone-why-would-i-use-pinyin&quot;&gt;有人&lt;/a&gt;稱為「字根首碼拼音」。&lt;/p&gt;
&lt;p&gt;直接看範例會比較好理解：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://inorisense.casa/_astro/assemble-mode-1.BfvT7tLm_7JOBM.webp&quot; alt=&quot;assemble-mode&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;618&quot; height=&quot;224&quot;&gt;&lt;/p&gt;
&lt;p&gt;上面這個詞彙，完整的羅馬拼音會是：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;xin shi ji fu yin zhan shi&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;然而，我們有更快的輸入方式，也就是&lt;strong&gt;只取&lt;/strong&gt;每個字拼音開頭的&lt;strong&gt;第一個&lt;/strong&gt;英文字，變成：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;x s j f y z s&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;以 macOS 拼音為例，以下是輸出結果：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://inorisense.casa/_astro/assemble-mode-3.C8tKZFXj_2frGPa.webp&quot; alt=&quot;assemble-mode&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1198&quot; height=&quot;272&quot;&gt;&lt;/p&gt;
&lt;p&gt;只要是一個常見的詞彙，都很適合用這種首碼略拼的方式輸入，藉此加速打字速度：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://inorisense.casa/_astro/assemble-mode-2.CbHP8y37_Z1dEHLu.webp&quot; alt=&quot;assemble-mode&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;661&quot; height=&quot;201&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://inorisense.casa/_astro/assemble-mode-4.D9nGFtyG_15bGAt.webp&quot; alt=&quot;assemble-mode&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1174&quot; height=&quot;254&quot;&gt;&lt;/p&gt;
&lt;p&gt;或許上面的範例詞彙實用性不高（應該也沒有人要手打整首長恨歌吧 😅），然而一些常見人名、地名、常見名詞等，也適用此方式，例如：馬英九（m y j）、蔡英文（c y w）、民進黨（m j d）、拉斯維加斯（l s w j s）⋯⋯&lt;/p&gt;
&lt;p&gt;以 macOS 的拼音來說，它具有學習模式，也就是會記憶平常輸入的使用者自訂詞彙，學起來之後，也同樣能夠用首碼略拼的方式打出來。&lt;/p&gt;
&lt;div&gt;&lt;h4 id=&quot;拆字模式&quot;&gt;拆字模式&lt;/h4&gt;&lt;/div&gt;
&lt;p&gt;下圖是關於生難字的一則新聞報導&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn2.ettoday.net/images/1529/1529856.jpg&quot; alt=&quot;hard-character&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.ettoday.net/news/20160106/625716.htm&quot;&gt;來源：這些生難字你都會嗎？　網友慚愧：除了吃飯什麼也不會 | ETtoday 生活新聞 | ETtoday 新聞雲 (2016)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;這些字到底該&lt;a href=&quot;https://pttpedia.fandom.com/zh/wiki/%E6%88%91%E9%83%BD%E5%94%B8ikea%E3%80%81%E6%88%91%E9%83%BD%E5%94%B8costco&quot;&gt;怎麼唸&lt;/a&gt;呢？&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://inorisense.casa/_astro/meme-i-say-ikea.NQdZtumA_Z2uFmYS.webp&quot; alt=&quot;i-say-ikea&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;500&quot; height=&quot;371&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.dcard.tw/f/meme/p/233071916&quot;&gt;來源：#轉 話說我都唸 IKEA - 梗圖板 | Dcard&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;我們常說「有邊讀邊、沒邊讀中間」。但太複雜太難的生難字大概也很難猜出發音。&lt;/p&gt;
&lt;p&gt;打出不會唸的生難字，則是字形輸入法的強項，因為不需要知道發音，只需要知道字形結構。同時，拼音輸入法則無用武之地，而這也是漢語拼音輸入法厲害的地方。因為它的「拆字」模式，擁有&lt;strong&gt;在拼音輸入架構下，同時又保有拼字輸入特性&lt;/strong&gt;的一種模式，文字表達有點難理解，就讓我們直接來一探究竟吧！&lt;/p&gt;
&lt;p&gt;我們拿上面報導的其中兩個詞來做範例。拆字模式顧名思義，就是將一個字拆成很多區塊來看，&lt;strong&gt;拆字方式沒有固定&lt;/strong&gt;（非倉頡輸入法那種固定拆解方式）。（有錯誤請不吝指教）&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://inorisense.casa/_astro/hard-words-1.Cye2suwN_1HNeBW.webp&quot; alt=&quot;hard words&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;140&quot; height=&quot;81&quot;&gt;&lt;/p&gt;
&lt;p&gt;以「覿氅」為例，「覿」字可拆成左右兩個區塊：「賣」與「見」；「氅」字可以拆成上下，「敞」與「毛」。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://inorisense.casa/_astro/hard-words-1.1.CTV5HqP4_Z23f8Iy.webp&quot; alt=&quot;hard words&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;512&quot; height=&quot;243&quot;&gt;&lt;/p&gt;
&lt;p&gt;因此我們輸入「chang」（敞）與「mao」（毛）之後，按下進入拆字模式的快捷鍵：&lt;code dir=&quot;auto&quot;&gt;shift&lt;/code&gt; + &lt;code dir=&quot;auto&quot;&gt;空白鍵&lt;/code&gt;（以 macOS 為例），會看到選單裡有我們要難字，而且還很貼心附上發音，「氅」字音同「廠」。&lt;/p&gt;
&lt;p&gt;我們再來練習這個詞，「曩磲」：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://inorisense.casa/_astro/hard-words-2.ChvLtyKB_1wuN3p.webp&quot; alt=&quot;hard words&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;140&quot; height=&quot;81&quot;&gt;&lt;/p&gt;
&lt;p&gt;「曩」字分成「日」、「襄」兩部份；「磲」分成「石」、「渠」兩部份，結果如下：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://inorisense.casa/_astro/hard-words-2.1.DooxgDjG_1zobEJ.webp&quot; alt=&quot;hard words&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;512&quot; height=&quot;154&quot;&gt;&lt;/p&gt;
&lt;p&gt;除了中文生難字外，某些日文漢字也可以打出來，有些日文漢字源自中文古字，所以古代就存在了，只是現在中文語圈沒在使用。&lt;/p&gt;
&lt;p&gt;例如下面這個日文漢字：「雫」。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://inorisense.casa/_astro/hard-words-3.BIjObE9F_1ah5z2.webp&quot; alt=&quot;hard words&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;82&quot; height=&quot;81&quot;&gt;&lt;/p&gt;
&lt;p&gt;會日文的同學，可以馬上切換日文輸入法打出來。「雫」字在日文唸作「しずく」（shi zu ku），意思是&lt;strong&gt;眼淚&lt;/strong&gt;。經過上面兩個範例，相信各位同學應該知道要怎麼拆解了，也就是拆解成「雨」與「下」：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://inorisense.casa/_astro/hard-words-3.1.CQAGrTxc_26iWQq.webp&quot; alt=&quot;hard words&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;512&quot; height=&quot;299&quot;&gt;&lt;/p&gt;
&lt;p&gt;原來這個字中文發音同「哪」。&lt;/p&gt;
&lt;p&gt;我們再來挑戰另一個很常見的日文漢字吧！&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://inorisense.casa/_astro/hard-words-4.CI8mBlZP_1F1KE3.webp&quot; alt=&quot;hard words&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;82&quot; height=&quot;81&quot;&gt;&lt;/p&gt;
&lt;p&gt;日文漢字：「峠」，日文唸作「とうげ」（to u ge），是棧道的意思。以這個字的結構來說，左邊是「山」，至於右邊 ⋯⋯ 看起來像是「卡」，但卻不是，於是再將右半邊拆成上下兩部份（也就是「上」、「下」二字），所以拆解成「山」、「上」、「下」，來看看結果：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://inorisense.casa/_astro/hard-words-4.2.bEPH473c_Z2ssnCI.webp&quot; alt=&quot;hard words&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;512&quot; height=&quot;210&quot;&gt;&lt;/p&gt;
&lt;p&gt;原來這個字的中文發音同「股」啊 🤔&lt;/p&gt;
&lt;p&gt;我認為拆字模式是拼音輸入架構下的一個很不錯備案，彌補了拼音拼寫時，生難字打不出來的缺點，若是注音只能舉雙手投降了&lt;/p&gt;
&lt;p&gt;另外值得注意的是，拆字模式只適合輸入單一漢字，而無法連續輸入很多漢字（畢竟不會一整句話都是生難字吧）&lt;/p&gt;
&lt;div&gt;&lt;h4 id=&quot;筆畫輸入模式&quot;&gt;筆畫輸入模式&lt;/h4&gt;&lt;/div&gt;
&lt;p&gt;「拆字模式」通常可以涵蓋大部分生難字的情境，但難免還是會有拆解過後的，部分字仍不會唸的情況。此時「筆畫輸入法模式」就派上用場了。&lt;/p&gt;
&lt;p&gt;漢語拼音輸入法除了「拆字模式」之外，還整合了手機上會搭載的「筆畫輸入法」，可以透過切換「筆畫輸入模式」開啟使用「筆畫輸入法」，並進行輸入。&lt;/p&gt;
&lt;p&gt;以 macOS 為例，輸入 &lt;code dir=&quot;auto&quot;&gt;u&lt;/code&gt; 則進入「筆畫輸入法模式」：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://inorisense.casa/_astro/stroke-mode-1.CQRVJWVr_ZDeNW4.webp&quot; alt=&quot;stroke-mode&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;512&quot; height=&quot;275&quot;&gt;&lt;/p&gt;
&lt;p&gt;進入模式之後，就可以開始利用 &lt;code dir=&quot;auto&quot;&gt;h&lt;/code&gt;、&lt;code dir=&quot;auto&quot;&gt;s&lt;/code&gt;、&lt;code dir=&quot;auto&quot;&gt;p&lt;/code&gt;、&lt;code dir=&quot;auto&quot;&gt;n&lt;/code&gt;、&lt;code dir=&quot;auto&quot;&gt;z&lt;/code&gt;、&lt;code dir=&quot;auto&quot;&gt;x&lt;/code&gt; 開始拼寫文字。下面是簡單的範例：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://inorisense.casa/_astro/stroke-mode-2.t6Ueo27F_1DkIIK.webp&quot; alt=&quot;stroke-mode&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;512&quot; height=&quot;227&quot;&gt;&lt;/p&gt;
&lt;p&gt;在我的認知裡，遇到生難字時，「拆字模式」是拼音的備案，那麼「筆畫輸入模式」就是&lt;strong&gt;備案中的備案&lt;/strong&gt;了 💪&lt;/p&gt;
&lt;img src=&quot;https://inorisense.casa/_astro/mind-blown-shocked.BVCToKct_Z20WoPV.webp&quot; alt=&quot;mind-explosion&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;220&quot; height=&quot;146&quot;&gt;
&lt;p&gt;或許有同學會問，這幾個符號是什麼意思？我之後打算寫一篇關於「筆畫輸入法」的文章，就先讓我賣個關子吧 😉&lt;/p&gt;
&lt;p&gt;這裡只要知道拼音輸入法，同時自帶筆畫輸入法模式就行了&lt;/p&gt;
&lt;div&gt;&lt;h4 id=&quot;轉換成本低&quot;&gt;轉換成本低&lt;/h4&gt;&lt;/div&gt;
&lt;p&gt;這裡指的轉換成本低，是針對注音輸入法的使用者，畢竟注音輸入法的使用者佔了大多數，如果要跳槽拼音輸入法，轉換成本（學習成本）就是一個很重要的考量因素。（像是我曾經要學習倉頡最後卻放棄了，因為學習門檻太高 😅）&lt;/p&gt;
&lt;p&gt;注音轉換漢語拼音，或是漢語拼音轉換注音，其實只要記憶一張對照表就行了，下面兩張是來自網路的對照表：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://inorisense.casa/_astro/pinyin-zhuyin-1.58YD8plr_ZReav0.webp&quot; alt=&quot;pinyin-zhuyin-1&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;577&quot; height=&quot;517&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://hiraku.dev/2011/10/2784/&quot;&gt;來源：漢語拼音輸入法：學習篇 - Hiraku Dev&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://inorisense.casa/_astro/pinyin-zhuyin-2.Cx5YBlh0_1qUoqt.webp&quot; alt=&quot;pinyin-zhuyin-2&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;600&quot; height=&quot;776&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://macon-chou.blogspot.com/2015/09/tbc.html&quot;&gt;來源：Just Old, So Record.: 注音-漢語拼音 對照表&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;我在學習拼音輸入法的時候，就是以這個對照表為起點的，簡單的背起來之後，就是大量的實戰練習，不懂的或忘記的，再回去對照表查詢就行了。&lt;/p&gt;
&lt;p&gt;對照表中有一些特別標註顏色的拼音，那是身為學習注音的用戶比較不直觀的拼音方式，需要多留意與背誦。&lt;/p&gt;
&lt;p&gt;例如：「女」的注音是「ㄋㄩ ˇ」，拼音是「nv」；「呂」的注音是「ㄌㄩ ˇ」，拼音是「lv」。「熊」的注音是「ㄒㄩㄥ ˊ」，拼音是「xiong」。&lt;/p&gt;
&lt;p&gt;此外，如果本身注音就不太好，「ㄣ」、「ㄥ」分不清楚，「應該」寫成「因該」，那我只能說 ⋯⋯ 學拼音一樣會寫錯字，因為同為拼音輸入法，會唸錯音，打出來的字就還是錯的，請務必補救一下小學國文：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://media.taaze.tw/showThumbnail.html?sc=11100257399&amp;#x26;height=400&amp;#x26;width=310&quot; alt=&quot;help-chinese&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.taaze.tw/usedList.html?oid=11100257399&quot;&gt;來源：升大學搶救國文大作戰 （新書、二手書、電子書） - 讀冊生活&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;好啦，其實這本是高中國文，我只是想借用一下書名而已。&lt;/p&gt;
&lt;div&gt;&lt;h4 id=&quot;有聲調拼音&quot;&gt;有聲調拼音&lt;/h4&gt;&lt;/div&gt;
&lt;p&gt;其實漢語拼音輸入法也有跟注音輸入法一樣的聲調系統，例如以下的字，在輸入完 &lt;code dir=&quot;auto&quot;&gt;mi&lt;/code&gt; 之後，按下 &lt;code dir=&quot;auto&quot;&gt;tab&lt;/code&gt; 標上聲調「一聲」，讓待選字更少更精準。按第二次 &lt;code dir=&quot;auto&quot;&gt;tab&lt;/code&gt; 則切換成「二聲」，以此類推。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://inorisense.casa/_astro/tone.D5-SaTP__1gAyg5.webp&quot; alt=&quot;tone&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;240&quot; height=&quot;264&quot;&gt;&lt;/p&gt;
&lt;p&gt;同拼字模式，聲調切換只限定輸入一個字的時候，不適用連續輸入，所以我個人平常比較少使用到。&lt;/p&gt;
&lt;div&gt;&lt;h4 id=&quot;通用鍵位與英文拼字檢查&quot;&gt;通用鍵位與英文拼字檢查&lt;/h4&gt;&lt;/div&gt;
&lt;p&gt;由於漢語拼音輸入法是使用英文 26 個字母來拼寫中文字，所以只使用了英文字母的鍵位，不需要像注音輸入法佔用到第二列（數字那一列）的鍵盤，因此鍵位就跟英打是共用的，同樣地，日文的羅馬拼音輸入法也是共用這 26 個英文鍵位。&lt;/p&gt;
&lt;p&gt;因此，只要學習&lt;strong&gt;一種&lt;/strong&gt;鍵位（也就是 QWERTY 美式鍵盤排列，如下圖），不需要額外記憶第二種鍵位（例如注音或倉頡鍵位），這對盲打的練習是事半功倍的。試想一下，無論你現在是在打英文、中文或是日文，都是在加強熟悉這個美式鍵盤排列的鍵位，可說是一箭三鵰啊！&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://inorisense.casa/_astro/us-keyboard-layout.DvTnSM2X_1hhS4d.webp&quot; alt=&quot;us-keyboard-layout&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1024&quot; height=&quot;422&quot;&gt;&lt;/p&gt;
&lt;p&gt;此外，由於沒有佔用到第二列，也讓拼音輸入法可以不用像注音輸入法，需要先&lt;strong&gt;切換英數模式&lt;/strong&gt;才能輸入數字。除了可以直接輸入數字之外，在輸入中英文夾雜的字句時，也是非常地好用，而且還具備英文單字拼音檢查的功能！&lt;/p&gt;
&lt;p&gt;例如我常忘記「維護」的英文，到底是 maintenance 還是 maintanance，這時候拼音輸入法就會告訴我答案：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://inorisense.casa/_astro/en-spell-check.Ce8Otr35_2g5jSX.webp&quot; alt=&quot;en-spell-check&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;512&quot; height=&quot;229&quot;&gt;&lt;/p&gt;
&lt;div&gt;&lt;h4 id=&quot;九宮格模式行動裝置&quot;&gt;九宮格模式（行動裝置）&lt;/h4&gt;&lt;/div&gt;
&lt;p&gt;手機上通常也會有標準 qwerty 全尺寸鍵盤排列的拼音輸入法，以 iOS 為例：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://inorisense.casa/_astro/pinyin-qwerty.DdqWtPtK_2ssLLn.webp&quot; alt=&quot;pinyin-qwerty&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1125&quot; height=&quot;646&quot;&gt;&lt;/p&gt;
&lt;p&gt;同時也可以設定成手機上經典九宮格的模式：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://inorisense.casa/_astro/t9-pinyin.DsF2ecGK_Z1jIkef.webp&quot; alt=&quot;t9-pinyin&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1125&quot; height=&quot;651&quot;&gt;&lt;/p&gt;
&lt;p&gt;九宮格輸入法的好處有：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;適合小螢幕的行動裝置&lt;/li&gt;
&lt;li&gt;適合單手操作&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果有胖手指困擾的同學們，九宮格式的輸入法可說是一大福音啊！&lt;/p&gt;
&lt;p&gt;九宮格式的輸入法還包括中文筆畫輸入法（T9 筆畫輸入）、日文假名輸入法、韓文輸入法：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://inorisense.casa/_astro/t9-stroke.IhIGKPX9_1ui9wE.webp&quot; alt=&quot;t9-stroke&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1125&quot; height=&quot;665&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://inorisense.casa/_astro/t9-kana.Axb2veLC_1sOq7R.webp&quot; alt=&quot;t9-kana&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1125&quot; height=&quot;661&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://inorisense.casa/_astro/t9-korean.lmzr7EGU_GU3VY.webp&quot; alt=&quot;t9-korean&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1125&quot; height=&quot;659&quot;&gt;&lt;/p&gt;
&lt;p&gt;相信常看日劇、韓劇的同學們，應該對這些鍵盤並不陌生才是 😎&lt;/p&gt;
&lt;div&gt;&lt;h4 id=&quot;與學中文的外國人溝通&quot;&gt;與學中文的外國人溝通&lt;/h4&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;但以我的經驗，我有一位來台北的師大華語中心學習&lt;strong&gt;繁體中文&lt;/strong&gt;的日本朋友，他學的也是拼音，而不是注音。&lt;/p&gt;
&lt;p&gt;其實想想也是很合理，學中文字前，要先學一套標音符號系統，成本是很高的，但英文字母大家都認得，所以學習成本相對低很多。&lt;/p&gt;
&lt;p&gt;在我的認知裡，「注音」與「日文五十音」的地位並不一樣，注音不會出現在正式書信文章裡（注音文不算正式書信，兒童書籍算是特定讀者取向），但五十音會，日文可以沒有漢字，但不能沒有五十音。因此學日文五十音是必要，然而注音則不是。&lt;/p&gt;
&lt;p&gt;若有很多世界各地學習中文的外國朋友，學習世界通用的漢語拼音可說是必須的，不然當外國朋友問：「這個字怎麼唸？」的時候，端出注音他們也看不懂啊 ⋯⋯ 這時候拿出漢語拼音，讓身旁的台灣人投以驚訝的眼光，&lt;del&gt;享受當下的優越感&lt;/del&gt;&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;缺點&quot;&gt;缺點&lt;/h2&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;使用者不普及&quot;&gt;使用者不普及&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;使用族群少的話，就很難推廣出去，然後也沒有機會擠進作業系統&lt;strong&gt;預設&lt;/strong&gt;輸入法的成員裡，像是曾經紅極一時的嘸蝦米輸入法就是如此，因為不是系統內建、屬於第三方輸入法之外，光是需要付費這點，就讓它不會出現在公用電腦（例如圖書館的電腦）上了。&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;軟體優化不足&quot;&gt;軟體優化不足&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;其實跟前一點的不普及因素也有關係，若沒有大量的使用者，開發者就不會去做很多的優化（雖然身為市占率最高的注音輸入法，在微軟上的選字智慧實在是 ⋯⋯ 嗯你懂的）&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;在地化不足&quot;&gt;在地化不足&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;問問各位在座的各位（沒學過拼音也沒關係），「垃圾」兩字你們會怎麼拼？&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;le se&lt;/li&gt;
&lt;li&gt;la ji&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;基本上以台灣用語而言，不用說一定是第一個。但以 &lt;a href=&quot;https://zh.wikipedia.org/zh-tw/Google%E6%8B%BC%E9%9F%B3%E8%BE%93%E5%85%A5%E6%B3%95&quot;&gt;Google 拼音輸入法&lt;/a&gt;為例，必須輸入「la ji」才能打出「垃圾」兩字。而 macOS 內建的繁體拼音輸入法則是輸入正確的「le se」。這就是所謂&lt;strong&gt;有無在地化&lt;/strong&gt;的差異。因為以往的 Google 拼音輸入法，是「繁體模式」，所以整個系統大部分只是簡體字翻譯成繁體字而已，用語是否有針對台灣用語做調整，就是另一回事了。&lt;/p&gt;
&lt;p&gt;而這方面，蘋果（包含 iOS 與 macOS）的在地化就做得十分好，這也包含前面所提&lt;a href=&quot;#%E8%A9%9E%E5%BD%99%E9%A6%96%E7%A2%BC%E7%95%A5%E6%8B%BC&quot;&gt;首碼略拼&lt;/a&gt;的例子（台灣政治人物、政黨名字略拼）。&lt;/p&gt;
&lt;p&gt;結論是：我都唸垃圾，而不是唸垃圾。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://inorisense.casa/_astro/meme-garbage.C5pfcPeB_ZPGWiW.webp&quot; alt=&quot;meme-garbage&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;500&quot; height=&quot;500&quot;&gt;&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;拼音輸入法列表&quot;&gt;拼音輸入法列表&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;目前來說，品質最好的漢語繁體拼音輸入法就是蘋果家了（也是我正在使用的），第三方的話，應該就屬 RIME 中州韻輸入法了，畢竟它同時可以安裝在 macOS 與 Windows 上，其他的只是列出來而已，不太推薦使用。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;macOS 繁體拼音輸入法&lt;/li&gt;
&lt;li&gt;iOS 繁體拼音輸入法&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.google.com/url?q=https://rime.im&amp;#x26;sa=D&amp;#x26;source=editors&amp;#x26;ust=1691109866774910&amp;#x26;usg=AOvVaw0IwGEdS3jEl4ZtJDUQpIe5&quot;&gt;RIME | 中州韻輸入法引擎&lt;/a&gt;（Windows, Mac）&lt;/li&gt;
&lt;li&gt;微軟拼音輸入法（需添加簡體中文語言，並設定繁體模式，在地化不足）&lt;/li&gt;
&lt;li&gt;微軟注音輸入法：拼音模式（不好用，不推薦）&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://zh.wikipedia.org/zh-tw/Google%E6%8B%BC%E9%9F%B3%E8%BE%93%E5%85%A5%E6%B3%95&quot;&gt;Google 拼音輸入法&lt;/a&gt;（以停止更新，不建議使用）&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h2 id=&quot;參考&quot;&gt;參考&lt;/h2&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://buzzorange.com/citiorange/2015/12/10/about-words/&quot;&gt;在政治操作下迷失的羅馬拼音 ── 為何明明是台灣地名，卻要用「中國漢語拼音」？ | CitiOrange 公民報橘&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://buzzorange.com/citiorange/2015/09/07/justfont/&quot;&gt;台灣已經快要在漢字圈銷聲匿跡，因為我們沒有屬於自己的「字型」 | CitiOrange 公民報橘&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://zh.wikipedia.org/zh-tw/%E5%8F%B0%E7%81%A3%E7%9A%84%E4%B8%AD%E6%96%87%E7%BE%85%E9%A6%AC%E6%8B%BC%E9%9F%B3&quot;&gt;台灣的中文羅馬拼音 - 維基百科，自由的百科全書&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://benyi.logdown.com/posts/2015/08/18/typing-on-the-iphone-why-would-i-use-pinyin&quot;&gt;在 iPhone 上打字，為什麼我會用拼音輸入法 « Benyi Hsia&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://hiraku.dev/2011/10/2784/&quot;&gt;漢語拼音輸入法：學習篇 - Hiraku Dev&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>中文輸入法</category><category>拼音</category><category>繁體中文</category><category>MacOS</category><category>iOS</category><category>Windows</category></item><item><title>[開箱] 無印良品隨身風扇</title><link>https://inorisense.casa/zh-tw/blog/muji-portable-fan-review/</link><guid isPermaLink="true">https://inorisense.casa/zh-tw/blog/muji-portable-fan-review/</guid><pubDate>Wed, 22 Mar 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;2023 年春季，無印良品出了一款&lt;a href=&quot;https://www.muji.com/tw/zh_tw/shop/120711/articles/muji-staff/11646&quot;&gt;隨身風扇&lt;/a&gt;，準備好迎接酷暑的到來。&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;muji-風扇大戶&quot;&gt;MUJI 風扇大戶&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;台灣的一年四季，大致可分為：夏、夏、夏、冬。嗯，你沒有眼花，確實就是&lt;strong&gt;三個&lt;/strong&gt;夏天。&lt;/p&gt;
&lt;p&gt;對於怕熱、易流汗的人來說（像是肥宅如我），風扇可是生活不可或缺的必要裝備。&lt;/p&gt;
&lt;p&gt;身為無印良品的愛用者，幾乎所有無印出的風扇都有了，也就是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.muji.com/tw/zh_tw/store/cmdty/detail/4549738309840&quot;&gt;空氣循環扇（大）&lt;/a&gt;，稱為「大白」&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.muji.com/tw/zh_tw/store/cmdty/detail/4550002435769&quot;&gt;空氣循環扇（小）&lt;/a&gt;，稱為「小白」&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.gq.com.tw/gadget/content-36163&quot;&gt;桌上型 USB 風扇&lt;/a&gt;，稱為「小小白」&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;就只差高貴的&lt;a href=&quot;https://www.muji.com/tw/zh_tw/store/cmdty/detail/4550182122763&quot;&gt;DC 馬達風扇&lt;/a&gt;沒有入手而已，稱為&lt;strong&gt;風扇大戶&lt;/strong&gt;也不為過（喂&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;手邊的隨身風扇&quot;&gt;手邊的隨身風扇&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;在入手這款之前，手邊擁有一支尺寸很迷你的素樂隨身風扇（右邊那支）：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;fan-comparison&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;4032&quot; height=&quot;3024&quot; src=&quot;https://inorisense.casa/_astro/muji-portable-fan-1.Bp8QGCj6_Z1P9CqQ.webp&quot;&gt;&lt;/p&gt;
&lt;p&gt;尺寸比無印的稍小，三段的風量控制，非常輕巧好攜帶&lt;/p&gt;
&lt;p&gt;使用起來十分愉快，直到發現了 MUJI 的隨身風扇 ⋯⋯&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;muji-隨身風扇&quot;&gt;MUJI 隨身風扇&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;其實我也考慮了一陣子之後才入手的（畢竟手邊也有相似的產品啊）&lt;/p&gt;
&lt;p&gt;既然入手了，那它就叫做 ⋯⋯ 「迷你白」吧！&lt;del&gt;（取名好辛苦）&lt;/del&gt;&lt;/p&gt;
&lt;p&gt;MUJI 隨身風扇最大的魅力，在於這個&lt;strong&gt;可折疊式擺放&lt;/strong&gt;的機構：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;foldable structure&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;3017&quot; height=&quot;2268&quot; src=&quot;https://inorisense.casa/_astro/muji-portable-fan-2.BF9hrCm-_Z1alKRe.webp&quot;&gt;&lt;/p&gt;
&lt;p&gt;平常坐捷運或是走路逛街，用手拿著倒是沒什麼問題，然而在吃飯的時候，雙手可是挺忙碌的，若能擺在桌上吹的話，使用體驗也會大大提升，所以最後還是敗下去了 😎&lt;/p&gt;
&lt;p&gt;這個隨身風扇有四段的風量調整，第一段風速很特別，是&lt;strong&gt;自然風&lt;/strong&gt;，也就是會轉一下又停下來的那種間歇模式，後面依序是小、中、大三種風量，調節風量的按鈕在後方：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;back&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;4032&quot; height=&quot;3024&quot; src=&quot;https://inorisense.casa/_astro/muji-portable-fan-4.DQsliJjs_Z1GuRWX.webp&quot;&gt;&lt;/p&gt;
&lt;p&gt;開機狀態長按按鈕大約 2 秒，就可以關機&lt;/p&gt;
&lt;p&gt;實際在吃東西的時候使用，涼風吹在臉上，&lt;del&gt;那種尊榮感真的不可言喻&lt;/del&gt;&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;功能比較&quot;&gt;功能比較&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;MUJI 隨身風扇使用起來的確不錯，但也不是沒有缺點，這裡來整理一下我自己的看法，做簡單的比較：&lt;/p&gt;








































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;&lt;/th&gt;&lt;th&gt;MUJI&lt;/th&gt;&lt;th&gt;素樂&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;體積&lt;/td&gt;&lt;td&gt;14.8(L) x 6.8(W) x 3.3(H)&lt;/td&gt;&lt;td&gt;13.5(L) x 6(W) x 3.5(H)cm&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;重量&lt;/td&gt;&lt;td&gt;93g&lt;/td&gt;&lt;td&gt;87g&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;tr&gt;&lt;td&gt;充電介面&lt;/td&gt;&lt;td&gt;Micro USB&lt;/td&gt;&lt;td&gt;USB-C&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;放置桌面&lt;/td&gt;&lt;td&gt;O&lt;/td&gt;&lt;td&gt;X&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;其實兩個都是不錯的產品。&lt;/p&gt;
&lt;p&gt;MUJI 風扇缺點在於，折疊的機構部分有點擔心會容易損壞，另外充電介面是使用有點過時的 Micro USB。&lt;/p&gt;
&lt;p&gt;素樂的風扇雖然只能手持，然而充電孔則是使用目前最通用的 Type-C 款式，算是不錯的優點。&lt;/p&gt;
&lt;p&gt;兩者體積很相近，重量都在 100g 內，都是很輕巧好攜帶的尺寸；兩者的三段風速應該都很夠用，自然風實不實用，就因人而異了。&lt;/p&gt;
&lt;p&gt;續航力的部分，我就懶得實測了 🙃&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;後記&quot;&gt;後記&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;MUJI 這次出品的這款隨身風扇，算是一款實用、價格親民的消暑道具，而且還有保固一年。&lt;/p&gt;
&lt;p&gt;在結帳的當下，&lt;del&gt;可愛的&lt;/del&gt; MUJI 店員很貼心地提醒，不要邊充電邊使用，還有第一段是間歇自然風，不要以為是壞掉了 😆&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;em&gt;reference link&lt;/em&gt;/}&lt;/p&gt;</content:encoded><category>無印良品</category><category>開箱</category></item><item><title>[Hugo] 用 GitHub Actions 將 Hugo 網站部署至 Vercel</title><link>https://inorisense.casa/zh-tw/blog/deploy-hugo-to-vercel-with-github-actions/</link><guid isPermaLink="true">https://inorisense.casa/zh-tw/blog/deploy-hugo-to-vercel-with-github-actions/</guid><pubDate>Sun, 01 Jan 2023 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;必備知識：&lt;code dir=&quot;auto&quot;&gt;Hugo&lt;/code&gt;、&lt;code dir=&quot;auto&quot;&gt;shell&lt;/code&gt;、&lt;code dir=&quot;auto&quot;&gt;npm&lt;/code&gt;、&lt;code dir=&quot;auto&quot;&gt;git&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div&gt;&lt;h2 id=&quot;前言&quot;&gt;前言&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;原本部落格是部署在 GitHub 自家提供的靜態網頁 GitHub Page 上，但因為 GitHub 對於免費帳號的限制（&lt;del&gt;給你免費用還嫌東嫌西&lt;/del&gt;），若要使用 GitHub Page 部署網頁，程式庫（repository）就要維持開源（public）的狀態，但是我希望將自己部落格的程式庫設定為私人的（private），畢竟尚未完成的草稿也在上面，並不想給別人看到。（&lt;del&gt;是說這麼冷清的部落格，沒人會沒事去翻你的原始碼&lt;/del&gt;）於是，就來研究有哪些可以部署靜態網頁的平台，其中兩個是我比較感興趣的，分別是：&lt;a href=&quot;https://deno.com/deploy&quot;&gt;Deno&lt;/a&gt; 與 &lt;a href=&quot;https://vercel.com/dashboard&quot;&gt;Vercel&lt;/a&gt;。想記錄一下設定部署的過程及遇到的問題。&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;部署-vercel&quot;&gt;部署 Vercel&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Vercel 是一個很有名的前端部署平台，也是 &lt;a href=&quot;https://nextjs.org&quot;&gt;Next.js&lt;/a&gt; 官方推薦的部署平台。它提供了「一鍵部署」的懶人功能，但由於我想用 GitHub Actions 自行寫腳本執行部署的動作，所以就不用一鍵部署了。&lt;/p&gt;
&lt;p&gt;我參考網路上一位來自烏克蘭的工程師 Oleh Andrushko 的文章，其實他的描述已經很詳細清楚了，但我又撞到了一些奇怪的問題，因此卡了好一段時間。&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;前置作業&quot;&gt;前置作業&lt;/h2&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;在本機安裝 Vercel CLI&lt;/li&gt;
&lt;li&gt;以 GitHub 帳號登入 Vercel CLI&lt;/li&gt;
&lt;li&gt;準備部署 Vercel 所需的三把鑰匙（Vercel 帳號憑證、Vercel 組織 Id、Vercel 專案 Id）&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h3 id=&quot;安裝-vercel-cli-與登入-vercel-帳號&quot;&gt;安裝 Vercel CLI 與登入 Vercel 帳號&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;首先，我們必須在本機以 npm 全域安裝 Vercel CLI：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&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;i&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;vercel&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;安裝完成後，開啟終端機（MacOS 的 terminal 或是 Windows 的 cmd），輸入 &lt;code dir=&quot;auto&quot;&gt;vercel&lt;/code&gt;，第一次安裝完通常會出現要求登入的訊息：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;vercel&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Vercel&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;CLI&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;28.10.1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; No existing credentials found. Please log in:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; Log in to Vercel (&lt;/span&gt;&lt;span&gt;Use&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;arrow&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;keys&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;❯&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Continue&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;with&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;GitHub&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;Continue&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;with&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;GitLab&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;Continue&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;with&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Bitbucket&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;Continue&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;with&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Email&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;Continue&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;with&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;SAML&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Single&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Sign-On&lt;/span&gt;&lt;/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;Cancel&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;這裡選擇以 GitHub 登入 Vercel：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; Log in to Vercel github&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; Please visit the following URL in your web browser:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; Success&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;GitHub&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;authentication&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;complete&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;for&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;#x3C;你 GitHub 登入所使用的 email&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;span&gt; You are deploying your home directory. Do you want to continue&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;y/N&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; n&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;最後會問你是否要部署 home 目錄，我們沒有要部署 home 目錄，所以選擇 &lt;code dir=&quot;auto&quot;&gt;n&lt;/code&gt;。&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;取得所需的密鑰&quot;&gt;取得所需的密鑰&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;進入要部署的專案資料匣位置，再下一次 &lt;code dir=&quot;auto&quot;&gt;vercel&lt;/code&gt; 指令：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;vercel&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Vercel&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;CLI&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;28.10.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; Set up and deploy &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;#x3C;你的 repo 的本地位置&gt;&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Y/n&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; y&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; Which scope &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;you&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;want&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;deploy&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;&quot;&lt;/span&gt;&lt;span&gt;&amp;#x3C;你的 GitHub 名字&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;span&gt; Link to existing project&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;y/N&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; n&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; What’s your project’s name&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;&amp;#x3C;你的 repo 名稱&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;span&gt; In which directory is your code located&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; ./&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Auto-detected&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Project&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Settings&lt;/span&gt;&lt;span&gt; (Hugo):&lt;/span&gt;&lt;/div&gt;&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;span&gt; &lt;/span&gt;&lt;span&gt;Command:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;hugo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-D&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--gc&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Development&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Command:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;hugo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;server&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-D&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-w&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-p&lt;/span&gt;&lt;span&gt; $PORT&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;-&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;Command:&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;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Output&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Directory:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;public&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;or&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;publishDir&lt;/span&gt;&lt;span&gt;`&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;the&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;`&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;file&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; Want to modify these settings&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;y/N&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt; n&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;⠸&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Deploying&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;#x3C;你的 Vercel 帳號名稱&gt;/&amp;#x3C;部署專案名稱&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;span&gt;  &lt;/span&gt;&lt;span&gt;Linked&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;&quot;&lt;/span&gt;&lt;span&gt;&amp;#x3C;你的 Vercel 帳號名稱&gt;/&amp;#x3C;部署專案名稱&gt;&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; (created &lt;/span&gt;&lt;span&gt;.vercel&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;added&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;to&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;.gitignore&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;🔍&lt;/span&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;Inspect:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;#x3C;Vercel 部署專案的頁面網址&gt;&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; [2s]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;✅&lt;/span&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;Preview:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;#x3C;Vercel 部署後預覽的網址&gt;&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; [10s]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;📝&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;deploy&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;production&lt;/span&gt;&lt;span&gt; (hugo-test-navy.vercel.app), run &lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;vercel&lt;/span&gt;&lt;span&gt; --prod&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;執行 &lt;code dir=&quot;auto&quot;&gt;vercel&lt;/code&gt; 指令後，會在根目錄自動產生 &lt;code dir=&quot;auto&quot;&gt;.vercel&lt;/code&gt; 資料匣，裡面會有兩支檔案，分別是 &lt;code dir=&quot;auto&quot;&gt;project.json&lt;/code&gt; 與 &lt;code dir=&quot;auto&quot;&gt;README.txt&lt;/code&gt;，而我們要關注的是 &lt;code dir=&quot;auto&quot;&gt;project.json&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;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;projectId&lt;/span&gt;&lt;span&gt;&quot;&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;&amp;#x3C;你的 Vercel 專案 Id&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;&lt;/span&gt;&lt;span&gt;orgId&lt;/span&gt;&lt;span&gt;&quot;&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;&amp;#x3C;你的 Vercel 組織 Id&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;/figure&gt;&lt;/div&gt;
&lt;p&gt;在此我們取得了「Vercel 組織 Id」、「Vercel 專案 Id」兩把鑰匙。&lt;/p&gt;
&lt;p&gt;切記這兩組 Id 是必須保密的，所以方才跑 &lt;code dir=&quot;auto&quot;&gt;vercel&lt;/code&gt; 指令的時候，有加入 &lt;code dir=&quot;auto&quot;&gt;.gitignore&lt;/code&gt;，脫離版本控制。詳細描述在 &lt;code dir=&quot;auto&quot;&gt;README.txt&lt;/code&gt; 裡面。&lt;/p&gt;
&lt;p&gt;第三把鑰匙「Vercel 帳號憑證」則要前往 &lt;a href=&quot;https://vercel.com/account/tokens&quot;&gt;Tokens – Account – Dashboard – Vercel&lt;/a&gt;，手動建立一個 GitHub 與 Vercel 建立連線的憑證。&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;設定-github-actions&quot;&gt;設定 GitHub Actions&lt;/h2&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;設定密鑰&quot;&gt;設定密鑰&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;前往 GitHub 的 repo，進入 Actions secrets 的設定頁面：&lt;code dir=&quot;auto&quot;&gt;repo &gt; Settings &gt; Secrets &gt; Actions&lt;/code&gt;（如果找不到，網址會是：&lt;code dir=&quot;auto&quot;&gt;https://github.com/&amp;#x3C;你的 GitHub 帳號&gt;/&amp;#x3C;repo 名稱&gt;/settings/secrets/actions&lt;/code&gt;），然後按 New repository secret 將這三個密鑰加進去。&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;建立-github-actions-腳本&quot;&gt;建立 GitHub Actions 腳本&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;在本機的 repo 裡建立 GitHub Actions 的部署腳本：&lt;code dir=&quot;auto&quot;&gt;/.github/workflows/vercel-prod.yaml&lt;/code&gt;（註：檔案名稱可以任意取）&lt;/p&gt;
&lt;p&gt;內容則參考 Oleh 的範例去修改，內容請參考以下連結：&lt;a href=&quot;https://gist.github.com/kazettique/cf132982caab036ea83cbbcbdad7848d/&quot;&gt;GitHub Actions for Vercel Deployment&lt;/a&gt;&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;複寫-vercel-自動建置build設定&quot;&gt;複寫 Vercel 自動建置（build）設定&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;因為我們要使用自己的 GitHub Actions 腳本進行建置的動作，所以要複寫掉 Vercel 的自動建置。&lt;/p&gt;
&lt;p&gt;參考 &lt;a href=&quot;https://github.com/amondnet/vercel-action#skip-vercels-build-step&quot;&gt;vercel-action 部署工具的文件&lt;/a&gt;，前往 Vercel 專案的設定頁面 &lt;code dir=&quot;auto&quot;&gt;repo &gt; Settings &gt; General&lt;/code&gt;，在 Build &amp;#x26; Development Settings 區塊中，Framework Preset 會自動偵測，自動帶入「Hugo」，在此選取「Other」，然後將「BUILD COMMAND」右方的 Override 按鈕開啟，複寫的值為「&lt;strong&gt;空&lt;/strong&gt;」，若 Vercel 阻擋空值儲存，則加一個「&lt;strong&gt;空白&lt;/strong&gt;」可以解決問題，設定結果請參考下圖：&lt;/p&gt;
&lt;figure data-align=&quot;start&quot;&gt; &lt;img src=&quot;https://inorisense.casa/_astro/vercel-build-override.oj9GFTCW_SoUN4.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2134&quot; height=&quot;1258&quot;&gt;  &lt;/figure&gt;
&lt;div&gt;&lt;h2 id=&quot;障礙排除&quot;&gt;障礙排除&lt;/h2&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;部署的路徑錯誤&quot;&gt;部署的路徑錯誤&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;GitHub Actions 的錯誤訊息：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Error:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;No&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Output&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Directory&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;named&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;found&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;after&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;the&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Build&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;completed.&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;You&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;can&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;configure&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;the&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Output&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Directory&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;your&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Project&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Settings.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;依據 Oleh 的部署腳本，它的部署路徑是 &lt;code dir=&quot;auto&quot;&gt;public&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;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Deploy to Vercel&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; &lt;/span&gt;&lt;span&gt;amondnet/vercel-action@v20&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&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;span&gt;vercel-action&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;vercel-token&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;${{ secrets.VERCEL_TOKEN }}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;vercel-org-id&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;${{ secrets.VERCEL_ORG_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;vercel-project-id&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;${{ secrets.VERCEL_PROJECT_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;github-comment&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;span&gt;vercel-args&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--prod&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;working-directory&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;public&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;/figure&gt;&lt;/div&gt;
&lt;p&gt;我將 &lt;code dir=&quot;auto&quot;&gt;working-directory: public&lt;/code&gt; 這行移除，就能正常部署了&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;env-環境變數錯誤&quot;&gt;env 環境變數錯誤&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;GitHub Actions 的錯誤訊息：&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Error:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Input&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;required&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;not&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;supplied:&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;  &lt;/span&gt;&lt;span&gt;at&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;getInput&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;加入 env 參數：&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;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Update Deployment Status&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; &lt;/span&gt;&lt;span&gt;bobheadxi/deployments@v1&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;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;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;step&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;finish&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;token&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&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;${{ job.status }}&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;span&gt; &lt;/span&gt;&lt;span&gt;${{ steps.deployment.outputs.env }}&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;    &lt;/span&gt;&lt;span&gt;deployment_id&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;${{ steps.deployment.outputs.deployment_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;env_url&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;${{ steps.vercel-action.outputs.preview-url }}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;node-舊版本的提示&quot;&gt;Node 舊版本的提示&lt;/h3&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;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;vercel-production&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Node.js&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;actions&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;are&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;deprecated.&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;For&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;more&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;information&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;see:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;https://github.blog/changelog/2022-09-22-github-actions-all-actions-will-begin-running-on-node16-instead-of-node12/.&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Please&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;update&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;the&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;following&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;actions&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;use&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Node.js&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;16:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;amondnet/vercel-action@v20&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Node 12 版本即將不支援這類提示性訊息，並不會造成部署的錯誤，只是在提醒而已。根據訊息，將使用的 GitHub Actions 工具升級到最新版（前往該工具的開源專案，就可以得知最新的版號），就可以消除提示訊息了。&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;後記&quot;&gt;後記&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;以上是紀錄我使用 GitHub Actions 腳本自動部署到 Vercel 的過程、設定以及碰到的一些小問題。&lt;/p&gt;
&lt;p&gt;一開始也提到，在經過了一番的調查之後，部署平台的口袋名單有 Vercel 與 Deno。最後採用 Vercel 的原因是，它提供了兩個部署環境：Preview 與 Production，於是我們可以寫兩份 GitHub Actions 腳本，分別部署 &lt;code dir=&quot;auto&quot;&gt;develop&lt;/code&gt; 與 &lt;code dir=&quot;auto&quot;&gt;main&lt;/code&gt; 分支，雖然最後我並沒有使用到 Vercel 預覽環境。然而因為我對於部落格的 Git flow 流程 &lt;sup&gt;&lt;a href=&quot;#user-content-fn-0&quot; id=&quot;user-content-fnref-0&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; 是直接在 &lt;code dir=&quot;auto&quot;&gt;main&lt;/code&gt; 分支寫稿上並推上遠端（反正在 markdown 裡的 front matter 可以設定為草稿，在建置的時候就會略過尚未完成的文章），此外設定除了分支名稱、一些設定不太一樣之外，都大同小異，有如此 Git flow 需求的同學，可以參考烏克蘭老兄的&lt;a href=&quot;https://olich.me/post/building-a-personal-blog-with-hugo-and-vercel/&quot;&gt;文章&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;此外，估計也會記錄一下部署 Deno 的流程，形式上也會跟本篇差不多。&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;參考資料&quot;&gt;參考資料&lt;/h2&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://olich.me/post/building-a-personal-blog-with-hugo-and-vercel/&quot;&gt;Building a personal blog with Hugo and Vercel&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/amondnet/vercel-action/&quot;&gt;amondnet/vercel-action: This action make a deployment with github actions instead of Vercel builder.&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;section data-footnotes=&quot;&quot;&gt;
&lt;ol&gt;
&lt;li id=&quot;user-content-fn-0&quot;&gt;
&lt;p&gt;關於部落格的 Git flow 流程，我有寫一篇短文描述這件事：&lt;a href=&quot;https://inorisense.casa/en/blog/gitflow-and-blog-version-control/&quot;&gt;GitFlow &amp;#x26; Blog Version Control&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-0&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 1&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</content:encoded><category>Hugo</category><category>Vercel</category><category>GitHub Actions</category><category>CI/CD</category></item><item><title>[開箱] Boox Poke 4 Lite 電子閱讀器：開放與封閉的抉擇</title><link>https://inorisense.casa/zh-tw/blog/boox-poke-4-lite-review/</link><guid isPermaLink="true">https://inorisense.casa/zh-tw/blog/boox-poke-4-lite-review/</guid><pubDate>Tue, 27 Dec 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;最近入手了 Boox 文石的電子閱讀器 Poke 4 Lite，今天就來聊一下關於電子閱讀器的各種事吧！😎&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;kindle初探電子書&quot;&gt;Kindle：初探電子書&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;說到電子書，亞馬遜可以說是很早就在&lt;strong&gt;佈局&lt;/strong&gt;電子書市場，深耕多年。&lt;/p&gt;
&lt;p&gt;在偶然的情況下，從友人那裡獲得了一台 Kindle Paperwhite 第二代，開啟了閱讀電子書的旅程。&lt;/p&gt;
&lt;p&gt;一開始使用 Kindle 的印象，沒有 LED 螢幕的背光、沒有惱人的 APP 訊息通知、因為很省電，只需要好幾周充一次電，各種會打擾分心的因素幾乎都摒除了，打造了可以靜心讀書的環境。&lt;/p&gt;
&lt;p&gt;使用 Kindle 閱讀器，除了在 Kindle 書城買書（我是使用日本亞馬遜），也會自己在網路上下載電子書來看。由於 Kindle 可接受的檔案格式是 MOBI、AWZ，較為通行的電子書格式 EPUB 卻無法相容，因此下載下來的電子書檔案都要經過轉檔。另外，若是下載了簡體書，也要轉換成繁體的版本，增加了不少麻煩，畢竟 Kindle 是封閉系統的閱讀器。但所幸轉檔、置入檔案的流程不是非常頻繁，書也不是很快就看完，所以也不算是太大的困擾。&lt;/p&gt;
&lt;p&gt;有好一陣子，當時（約莫是 2014 年）的日本亞馬遜書城裡沒有繁體中文書（大概有簡體書），而且不想換到中國亞馬遜看簡體書，也沒有很積極地要閱讀英、日文的書籍（想練習外語閱讀力，但是懶惰 🙈），反而都去圖書館借中文書來看，不用錢又有看不完的書，實在是很大的吸引力，因此 Kindle 閱讀器就晾在一旁，而且一放就是好幾年。2016 年，正是台灣電子書剛起步的時候，我也開始在用樂天 kobo 買電子書，通勤的時候，在手機（四吋的 iPhone SE）上閱讀，後來也在讀墨 Readmoo 上也買了一些電子書。總之，用手機看電子書的機會增加了許多，畢竟出門的時候一定會帶手機，然而閱讀器卻未必。「想要看卻發現沒帶身上，帶出門卻沒有用」的情況也不在少數（墨菲定律啊）。由於使用的頻率越來越少了，於是在某一次整理東西的時候，毅然決定將 Kindle 給脫手了。&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;圖書館的熱門書&quot;&gt;圖書館的熱門書&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;去圖書館借書，的確十分方便的資源，而且也會不斷有新書可以借閱。以台北市的圖書館為例，以往個人借閱證的借閱額度是 10 本，現在好像增加到 25 本的樣子，利用網頁或 APP 預約借書，可以在預約書都抵達的時候一次借回來，然後看完之後，再換其他本。比較熱門的書通常預約的人比較多，會等久一點，不過除非是閱讀迅速的人，不然應該不受影響，畢竟大部分的書是不需要排隊等待借書的。而且，圖書館就是滿滿的書山，根本看不完哪～&lt;/p&gt;
&lt;p&gt;我聽說有些人比較潔癖，因為嫌圖書館的書太髒，所以從不借圖書館的書。圖書館的書畢竟是很多人使用過的，確實不免會比較髒，我雖然沒有非常在意，但有時候也會遇到很不好的情況，例如：紙上有原子筆、鉛筆的畫記，黏著鼻屎、或將整頁插畫用美工刀割下來的情況，實在是挺困擾的。有免費的書讓很多人可以閱讀，卻做如此缺德的事，實在是不可取啊 😔&lt;/p&gt;
&lt;p&gt;此外，有些熱門書，就算要排隊預約，也要排幾個月，看到自己排一、兩千號，也是挺灰心的。像是前陣子很熱門的「被討厭的勇氣」、「情緒勒索」、「原子習慣」，都是大排長龍的熱門書。但想一想，一本書也沒多少錢，買了就可以馬上享受了，就算可以免費看，卻要等足足半年之久，感覺也不划算啊～而這也是我會直接買電子書的一大動機。&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;亞馬遜的繁體中文書&quot;&gt;亞馬遜的繁體中文書&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;近年，日本亞馬遜書城上開始有繁體中文書了。雖然書量跟台灣樂天 kobo 與讀墨 Readmoo 兩大巨頭比起來還差很遠就是了。然而日本亞馬遜開始上架繁體中文書這個趨勢，著實令人期待。因為以電子書的 APP 整體使用體驗而言，我認為 Kindle 還是最好的。&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;閱讀器&quot;&gt;閱讀器&lt;/h2&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;封閉系統的閱讀器&quot;&gt;封閉系統的閱讀器&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;首先，先來談一下書城的平台，也就是軟體的部分。&lt;/p&gt;
&lt;p&gt;電子書綁定書城的平台及其帳號，若我們在各家都買了書，就會形成很分散的情況：想像一下，家中必須放很多個書櫃，大本的書必須放大的書櫃、一般的書籍放在一般的書櫃、文庫本的書籍則放在小型的書櫃、而漫畫尺寸也放在漫畫尺寸的書櫃。&lt;/p&gt;
&lt;p&gt;若我們在各個平台都買了幾本書（通常都是因為想讀的書在 A 書城正好特價，或是只有 B 書城有買電子書的版權），就會發生這樣的情況，手機裡安裝了各家的 APP，我現在就是處於這樣的混亂狀態。若要集中管理，只能在固定在某一家買書了。電子書商城的經營模式畢竟不一樣，無法像紙本書一樣，想買皇冠出版的哈利波特時，誠品書店有賣、金石堂書店也有賣、在博客來網路書店也買得到。&lt;/p&gt;
&lt;p&gt;或許有人會說：「電子書的優點就是輕便啊，多裝一個 APP 在手機裡也不會變重，沒有這麼嚴重吧」&lt;/p&gt;
&lt;p&gt;我可以說，或許這是身為極簡主義者的潔癖情節 😝&lt;/p&gt;
&lt;p&gt;除了書籍綁定書城平台之外，再來談談硬體：閱讀器的部分。&lt;/p&gt;
&lt;p&gt;由於各家的閱讀器幾乎都是封閉式系統，意思就是：看書只能用自己家的書城。若我們買樂天 kobo 的閱讀器，就只能看 kobo 的書。當然自己放電子書檔案進去通常也是允許的，但畢竟那不是花錢買授權書的流程，而不在討論的範圍內。所以，若有使用好幾個書城，且都要使用閱讀器的話，豈不是要買好幾個閱讀器嗎？！這實在與電子書環保的理念背道而馳，節省了紙張的消費，反而買了更多電子產品，我想大部分人也不會這麼做。因此，開放系統的閱讀器正可以解決這個問題。&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;開放系統的閱讀器&quot;&gt;開放系統的閱讀器&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;Boox 文石的閱讀器在代理進台灣之後，我一直有在關注。他們主打的一大賣點就是：Android 系統的閱讀器，所以可在 Play 商店下載任何 APP，就跟使用智慧型手機一樣。開放系統的優點，解決了封閉系統閱讀器的最大痛點，也就是&lt;strong&gt;單裝置多平台&lt;/strong&gt;的終極目標。然而，文石的閱讀器也不是沒有缺點。&lt;/p&gt;
&lt;p&gt;電子墨水螢幕跟手機螢幕的差別在於，基於目前（2022 年）電子墨水的技術，它的刷新速度沒有 LED（或 OLED）螢幕這麼快速，因此一般的手機 APP 也不會特別為了電子墨水螢幕這個小眾硬體去做軟體上的優化或客製化的設定。而這些正是封閉系統閱讀器的優點所在。封閉系統的閱讀器針對電子墨水螢幕的特性去做軟體上的調校，所以在使用體驗會比手機 APP 直接在電子墨水螢幕上好很多。例如 APP 通常會加上翻頁的特效，因為這樣的特效在 LED 上很有翻實體書的感覺，LED 螢幕上看起來演示得很好，但在刷新速度較低的電子墨水螢幕上卻慘不忍睹。有些 APP 可以在設定裡面，將一些漸變動畫給關閉，再翻頁切換的體驗就會近似於封閉系統的閱讀器，但有些則不行，例如說亞馬遜的 Kindle APP ⋯⋯&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;閱讀器的抉擇&quot;&gt;閱讀器的抉擇&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;於是，最近因為有開始閱讀電子書，手機閱讀縱然方便，但因為工作時都是使用電腦，所以希望下班後可以減少看 LED 螢幕的時間，於是又興起了買電子閱讀器的念頭。&lt;/p&gt;
&lt;p&gt;雖然看到亞馬遜可以買繁體中文書有點心動，很想購入最新的 Kindle 閱讀器，但礙於手邊還有其他平台的中文書籍要閱讀，因而頓時陷入了兩難。最後的抉擇是買了文石 Boox Poke 4 Lite，在上面閱讀 Kindle、kobo、Readmoo 的電子書。&lt;/p&gt;
&lt;p&gt;標題雖然稱之為開箱文，但沒有常見開箱文該有的樣子，請見諒 😅&lt;/p&gt;
&lt;p&gt;這裡就簡單列一下 Boox Poke 4 Lite 的規格，並與低、中階最新的 Kindle、還有我的手機做比較：&lt;/p&gt;








































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;&lt;/th&gt;&lt;th&gt;Poke 4 Lite&lt;/th&gt;&lt;th&gt;Kindle 11&lt;/th&gt;&lt;th&gt;Kindle Paperwhite 5&lt;/th&gt;&lt;th&gt;iPhone 12 mini&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;尺寸&lt;/td&gt;&lt;td&gt;153 x 107 x 7.1 mm&lt;/td&gt;&lt;td&gt;158 x 109 x 8.0 mm&lt;/td&gt;&lt;td&gt;180 x 120 x 8.1 mm&lt;/td&gt;&lt;td&gt;131.5 x 64.2 x 7.4 mm&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;重量&lt;/td&gt;&lt;td&gt;150g&lt;/td&gt;&lt;td&gt;158g&lt;/td&gt;&lt;td&gt;205g&lt;/td&gt;&lt;td&gt;135g&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;顯示器&lt;/td&gt;&lt;td&gt;6 吋&lt;/td&gt;&lt;td&gt;6 吋&lt;/td&gt;&lt;td&gt;6.8 吋&lt;/td&gt;&lt;td&gt;5.4 吋&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;解析度&lt;/td&gt;&lt;td&gt;758 x 1024 &lt;br&gt;(212 ppi)&lt;/td&gt;&lt;td&gt;1072 × 1448&lt;br&gt; (300ppi)&lt;/td&gt;&lt;td&gt;1236 x 1648 &lt;br&gt;(300ppi)&lt;/td&gt;&lt;td&gt;1080 x 2340 &lt;br&gt;(476ppi)&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;一開始對 Poke 212ppi 的解析度有點擔心，但使用幾天下來，發現也看不出什麼顆粒感（&lt;del&gt;說到底沒有寫輪眼&lt;/del&gt;），150 克的重量拿起來真的沒什麼負擔。若說要以集中使用亞馬遜買電子書為目標來說（念念不忘 Kindle 😝），Boox 算是一個不錯的過渡期選擇，當然或許就此習慣開放系統的閱讀器而回不去了也說不定。總之，比起用手機閱讀，電子墨水的螢幕總是舒服許多，重點是毫無分心、毫無干擾。&lt;/p&gt;
&lt;p&gt;或許有人會問：「既然是開放式系統，會不會因此把手機常用的 APP 都安裝，然後就跟手機一樣充滿著推播訊息？」&lt;/p&gt;
&lt;p&gt;我有試著裝過 Spotify，操作體驗真的不好。前面有提過，電子墨水目前的更新速度並不快速，就算裝了社群類、通訊類 APP，我們也不會想要在上面使用，因為體驗實在是太糟了 😆&lt;/p&gt;
&lt;p&gt;既然說到這裡，來分享一下我目前有裝的 APP 吧：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Kindle&lt;/li&gt;
&lt;li&gt;Notion&lt;/li&gt;
&lt;li&gt;Readmoo 看書&lt;/li&gt;
&lt;li&gt;Wikipedia&lt;/li&gt;
&lt;li&gt;樂天 Kobo&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h2 id=&quot;紙本書-vs-電子書&quot;&gt;紙本書 vs. 電子書&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;雖然本篇主要是想聊聊電子閱讀器，畢竟電子閱讀器的出現，跟電子書密切相關。若讀者是多年的蘋果用戶使用者（無論是 iPhone 或 iPad），應該都知道一個叫做 iBooks（2018 年改名為 Apple Books）的 APP，我查了一下維基百科，發現它在 2010 年 iOS4，也就是 iPhone 4 的年代就已經存在了。而亞馬遜的 Kindle 則在更早的 2007 年發表了第一代的 Kindle 電子閱讀器。然而蘋果公司則從未聽到要研發電子閱讀的任何消息 ⋯⋯&lt;/p&gt;
&lt;p&gt;紙本書及電子書的優缺點，我想網路上很多人都曾討論過這個議題，我自己的感想也是大同小異，如果不介意的話就繼續讀下去吧 😆&lt;/p&gt;
&lt;p&gt;就先從優點開始講吧&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;紙本書的優點&quot;&gt;紙本書的優點&lt;/h3&gt;&lt;/div&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;del&gt;看實體書好有氣質（虛榮感氾濫 ）&lt;/del&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;
&lt;li&gt;可以二手脫售&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h3 id=&quot;電子書的優點&quot;&gt;電子書的優點&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;再來說說電子書的優點&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;輕薄輕量，一間圖書館隨手帶著走（古人無法想像飛機在天上飛的概念）&lt;/li&gt;
&lt;li&gt;通常價格比實體書便宜，而且購買方便（就跟蒐集 Steam 遊戲一樣簡單。什麼？我剛說蒐集嗎？）&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;/ul&gt;
&lt;div&gt;&lt;h3 id=&quot;紙本書的缺點&quot;&gt;紙本書的缺點&lt;/h3&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;重量很重，也佔空間（搬過家的人應該都知道書有多重）&lt;/li&gt;
&lt;li&gt;紙張需要保養及維護（溫度與濕度的控制很重要，還要防蟲害）&lt;/li&gt;
&lt;li&gt;地震的時候很可怕（聽說日本 311 大地震時，很多人被倒下的書櫃壓到）&lt;/li&gt;
&lt;li&gt;二手書不一定好賣&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h3 id=&quot;電子書的缺點&quot;&gt;電子書的缺點&lt;/h3&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;藍光傷眼的問題（若使用手機或平板等裝置）&lt;/li&gt;
&lt;li&gt;推播訊息，其他的 APP 會使人容易分心（若使用手機或平板等裝置）&lt;/li&gt;
&lt;li&gt;來回查閱比較困難，而且不直覺&lt;/li&gt;
&lt;li&gt;少了翻書的感覺、書本的味道&lt;/li&gt;
&lt;li&gt;&lt;del&gt;人家不知道你/妳是在滑手機，還是在看文學巨著（又是莫名的虛榮心）&lt;/del&gt;&lt;/li&gt;
&lt;li&gt;不能墊泡麵（誤）&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h2 id=&quot;總結&quot;&gt;總結&lt;/h2&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;只能說，兩者的優點在於使用情境的不同，才能夠發揮所長。工具書為了查找方便，所以使用紙本書；娛樂性質的小說，買電子書存放在手機或閱讀器裡，省去看完很難處理的困擾；收藏一整套火影忍者的漫畫，光是擺在架上就賞心悅目 ⋯⋯。當然，如果收藏一整套電子版的火影忍者，隨時隨地都可以翻閱享受，那又是另一種收藏的浪漫。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;有時候，書是工具、是資訊的載體；但在某些情況下，書本身更是擁有者的收藏。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;於是我可以這樣子歸納：&lt;/p&gt;
&lt;p&gt;若一本書記載的資訊遠大於書本身，就買電子書。&lt;/p&gt;
&lt;p&gt;若一本書的收藏意義重大（內容固然也很棒），就買紙本書。&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;參考資料&quot;&gt;參考資料&lt;/h2&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Amazon_Kindle&quot;&gt;Amazon Kindle - Wikipedia&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Apple_Books&quot;&gt;Apple Books - Wikipedia&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>開箱</category><category>文石</category><category>閱讀器</category><category>電子墨水</category><category>電子紙</category><category>電子書</category></item><item><title>牙醫與臭豆腐</title><link>https://inorisense.casa/zh-tw/blog/dentist-and-stinky-tofu/</link><guid isPermaLink="true">https://inorisense.casa/zh-tw/blog/dentist-and-stinky-tofu/</guid><pubDate>Fri, 11 Nov 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;晚餐後有預約看牙醫。&lt;/p&gt;
&lt;p&gt;雖然是預約看牙醫，但實際上牙齒並沒有什麼不適，只是做定期的洗牙維護罷了。在吃晚餐之前，突然想到要看牙醫這件事。不是我忘記今天要看牙醫，而是突然想到：「看牙醫之前，吃東西也沒有禁忌這件事？」不過聽到禁忌不要誤會了，不是指迷信那回事。我在思考的問題是，打個比方好了：如果看牙醫之前去&lt;strong&gt;吃臭豆腐&lt;/strong&gt;的話，牙醫會不會在你嘴巴張開的那一刻，皺起眉頭呢？&lt;/p&gt;
&lt;p&gt;我以前也想過這些事，各科的醫生有各種辛苦。皮膚科醫生可能每天看著滿臉痘子的病人、臭腳伸得高高有長雞眼的病人。外科醫生時常看到病人身體裡的各種內臟、還有滿滿的紅色。牙醫每天要聞著從病人的口中飄出來的各種味道，一邊辛苦地工作。每次這樣想像著，都會不禁感嘆：「啊，這種工作我還真做不來，跟生命有關，尤其跟人命有關的工作，我真的沒辦法。」倒不是因為看到血會昏倒這麼簡單的理由。&lt;/p&gt;
&lt;p&gt;回到臭豆腐的話題，如果換成吃麻辣鍋，不知道牙醫師會是什麼心情？如果工作忙到餓著肚子還沒吃飯，卻又從病人口中聞到各種美食的味道，不是很令人沮喪嗎？不過畢竟飯還是要吃的，最後我吃了清淡的飯菜、配上了味噌湯，或許會有味噌湯的味道。不過不要吃臭豆腐的話，應該在可接受的範圍吧？我暗自認為牙醫師會這麼想。各位去看牙醫之前，會特別做什麼事嗎？例如：吃個涼糖、刷個牙、用漱口水漱個口，還是不會特別做什麼？&lt;/p&gt;</content:encoded></item><item><title>[CSS] Tailwind 主題色的切換</title><link>https://inorisense.casa/zh-tw/blog/tailwind-theme-switcher-poc/</link><guid isPermaLink="true">https://inorisense.casa/zh-tw/blog/tailwind-theme-switcher-poc/</guid><pubDate>Fri, 14 Oct 2022 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;必備知識：&lt;code dir=&quot;auto&quot;&gt;CSS&lt;/code&gt;、&lt;code dir=&quot;auto&quot;&gt;HTML&lt;/code&gt;、&lt;code dir=&quot;auto&quot;&gt;JS&lt;/code&gt;、&lt;code dir=&quot;auto&quot;&gt;React&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div&gt;&lt;h2 id=&quot;前言&quot;&gt;前言&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Tailwind 提供了&lt;a href=&quot;https://Tailwindcss.com/docs/dark-mode&quot;&gt;深色模式（Dark Mode）&lt;/a&gt;，可以自行設定深色模式時的樣式，只要在 CSS class 前面加上 &lt;code dir=&quot;auto&quot;&gt;dark:&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;#x3C;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;text-black dark:text-white&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&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;#x3C;/div&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;假如我們要做「主題色切換」的功能，&lt;code dir=&quot;auto&quot;&gt;dark:&lt;/code&gt; 這個用法也許可行，但也只&lt;strong&gt;限於兩個&lt;/strong&gt;主題色的切換，另一個缺點就是&lt;strong&gt;到處都要&lt;/strong&gt;加上 &lt;code dir=&quot;auto&quot;&gt;dark:&lt;/code&gt; 的切換樣式，這聽起來不是一個好的做法。&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;tldr&quot;&gt;TL;DR&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;想跳過本文&lt;del&gt;廢話&lt;/del&gt;、直接看最終實作結果的同學，請走&lt;a href=&quot;#%E5%8E%9F%E5%A7%8B%E7%A2%BC&quot;&gt;傳送門&lt;/a&gt;。&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;在-tailwind-config-的自定義主題色盤&quot;&gt;在 Tailwind config 的自定義主題色盤&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;在 Tailwind 的 config 當中，其中一個客制化的選項，就是可以自定義顔色的 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;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;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&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;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;colors&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;primary&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;#0d6efd&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;secondary&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;#6c757d&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;danger&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;#dc3545&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;warning&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;#ffc107&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;/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;/figure&gt;&lt;/div&gt;
&lt;p&gt;PS. 以上的名稱及色碼取自 &lt;a href=&quot;https://getbootstrap.com/docs/5.2/customize/color/&quot;&gt;Bootstrap 的主題色盤&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;若我們有兩個主題色，config 就必須寫成這樣：&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;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&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;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;colors&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;&quot;&lt;/span&gt;&lt;span&gt;theme-1-primary&lt;/span&gt;&lt;span&gt;&quot;&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;#0d6efd&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;theme-1-secondary&lt;/span&gt;&lt;span&gt;&quot;&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;#6c757d&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;theme-1-danger&lt;/span&gt;&lt;span&gt;&quot;&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;#dc3545&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;theme-1-warning&lt;/span&gt;&lt;span&gt;&quot;&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;#ffc107&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;theme-2-primary&lt;/span&gt;&lt;span&gt;&quot;&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;#0d6efd&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;theme-2-secondary&lt;/span&gt;&lt;span&gt;&quot;&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;#6c757d&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;theme-2-danger&lt;/span&gt;&lt;span&gt;&quot;&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;#dc3545&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;theme-2-warning&lt;/span&gt;&lt;span&gt;&quot;&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;#ffc107&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;/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;/figure&gt;&lt;/div&gt;
&lt;p&gt;如此一來，就要在 HTML 上去抽換不同主題的 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;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Button&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; buttonTheme &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; isTheme1 &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;bg-theme-1-primary&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;bg-theme-2-primary&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;&amp;#x3C;button&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;className&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;buttonTheme&lt;/span&gt;&lt;span&gt;}&gt;&lt;/span&gt;&lt;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;/figure&gt;&lt;/div&gt;
&lt;p&gt;到處都要有這樣的判斷，看來也不是明智的做法。此外，若我們有四、五個主題要切換，就不是三元運算可以簡單解決的事。&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;css-的全域變數&quot;&gt;CSS 的全域變數&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;CSS 提供了變數的功能，可分爲區域變數與全域變數。&lt;/p&gt;
&lt;p&gt;定義 CSS 區域變數，下面這個 &lt;code dir=&quot;auto&quot;&gt;--main-bg-color&lt;/code&gt; 只能在這個 scope（&lt;code dir=&quot;auto&quot;&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;element&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--main-bg-color&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;brown;&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;/figure&gt;&lt;/div&gt;
&lt;p&gt;比較常見使用 CSS 變數的方式，是使用 CSS 全域變數，建立一個稱作 &lt;code dir=&quot;auto&quot;&gt;:root&lt;/code&gt; 的 pseudo-class，裡面定義的變數在整個 HTML document 底下都可以使用（其實就是全域變數了）：&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;root&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--main-bg-color&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;brown;&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;/figure&gt;&lt;/div&gt;
&lt;p&gt;使用 CSS 變數時，用 &lt;code dir=&quot;auto&quot;&gt;var()&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;element&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;background-color&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;--main-bg-color&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;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;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h2 id=&quot;tailwind-與-css-變數&quot;&gt;Tailwind 與 CSS 變數&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;我們結合 Tailwind 及 CSS 變數的特性，就可以達到主題色盤抽換的目的，我們將方才 config 中寫死的色碼，用 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;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;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&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;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;colors&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;color-one&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;var(--color-one)&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;color-two&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;var(--color-two)&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;color-three&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;var(--color-three)&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;color-four&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;var(--color-four)&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;color-five&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;var(--color-five)&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;}&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;/figure&gt;&lt;/div&gt;
&lt;p&gt;設定好 Tailwind config 之後，接著就是定義各主題色盤變數的時候了&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;const&lt;/span&gt;&lt;span&gt; theme_ayanami &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;: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;--color-one: #1d446c;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--color-two: #f1f1f1;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--color-three: #571a1a;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--color-four: #000000;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--color-five: #525252;&lt;/span&gt;&lt;/div&gt;&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;const&lt;/span&gt;&lt;span&gt; theme_ikari &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;: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;--color-one: #3f6d4e;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--color-two: #8bd450;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--color-three: #1d1a2f;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--color-four: #965fd4;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--color-five: #734f9a;&lt;/span&gt;&lt;/div&gt;&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;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;然後將變數包在 &lt;code dir=&quot;auto&quot;&gt;&amp;#x3C;style&gt;&lt;/code&gt; 後，安插在 &lt;code dir=&quot;auto&quot;&gt;&amp;#x3C;head&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;function&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; &lt;/span&gt;&lt;span&gt;{&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;currentTheme&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; setCurrentTheme&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;useState&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;ayanami&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;/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;getThemeVariables&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;_theme&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&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;switch&lt;/span&gt;&lt;span&gt; (_theme) &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;ayanami&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;return&lt;/span&gt;&lt;span&gt; theme_ayanami&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;break;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;case&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;ikari&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;return&lt;/span&gt;&lt;span&gt; theme_ikari&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;break;&lt;/span&gt;&lt;/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;useEffect&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&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;!&lt;/span&gt;&lt;span&gt;document&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;getElementById&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;customThemeId&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;const&lt;/span&gt;&lt;span&gt; head &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;head&lt;/span&gt;&lt;span&gt;;&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; newStyleElement &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;createElement&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;style&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;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;head&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;appendChild&lt;/span&gt;&lt;span&gt;(newStyleElement)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;newStyleElement&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;span&gt;&quot;&lt;/span&gt;&lt;span&gt;customThemeId&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;newStyleElement&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;getThemeVariables&lt;/span&gt;&lt;span&gt;(currentTheme)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&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;const&lt;/span&gt;&lt;span&gt; styleElement &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;getElementById&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;customThemeId&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;/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; (styleElement) &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&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;styleElement&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;getThemeVariables&lt;/span&gt;&lt;span&gt;(currentTheme)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/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;span&gt;  &lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;span&gt; [currentTheme])&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;&amp;#x3C;div&gt;&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;#x3C;button&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;onClick&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;setCurrentTheme&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;ayanami&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;}&gt;&lt;/span&gt;&lt;span&gt;凌波零&lt;/span&gt;&lt;span&gt;&amp;#x3C;/button&gt;&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;#x3C;button&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;onClick&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;setCurrentTheme&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;ikari&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;}&gt;&lt;/span&gt;&lt;span&gt;碇真嗣&lt;/span&gt;&lt;span&gt;&amp;#x3C;/button&gt;&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;#x3C;/div&gt;&lt;/span&gt;&lt;/div&gt;&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;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&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;#x3C;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-color-one&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&amp;#x3C;/div&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;藉由抽換 &lt;code dir=&quot;auto&quot;&gt;&amp;#x3C;style&gt;&lt;/code&gt; 的内容，將新切換的主題變數複寫進去，便可以主題切換了！&lt;/p&gt;

&lt;figure data-align=&quot;start&quot;&gt; &lt;img src=&quot;https://inorisense.casa/_astro/demo-01.D2hF17rc_Z1EwLdG.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;960&quot; height=&quot;461&quot;&gt;  &lt;/figure&gt;
&lt;p&gt;從瀏覽器的 Dev Tools 中，可以看到我們只抽換了 CSS 的全域變數，元件的部分都沒有異動&lt;/p&gt;
&lt;p&gt;同場加映！「初音未來」跟「涼宮春日」的主題色！😘&lt;/p&gt;

&lt;figure data-align=&quot;start&quot;&gt; &lt;img src=&quot;https://inorisense.casa/_astro/demo-02.CWxOUW51_XwFLJ.webp&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;960&quot; height=&quot;461&quot;&gt;  &lt;/figure&gt;
&lt;div&gt;&lt;h2 id=&quot;原始碼&quot;&gt;原始碼&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;若想看整個範例的結果，請前往&lt;a href=&quot;https://kazettique.github.io/tw-theme-switcher-poc/&quot;&gt;這裡&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;範例原始碼，請參考&lt;a href=&quot;https://github.com/kazettique/tw-theme-switcher-poc&quot;&gt;這裡&lt;/a&gt;&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;後記&quot;&gt;後記&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;我們利用 CSS 全域變數的特性，並搭配 Tailwind 的客制化顔色設定，實現了主題色切換的目的。優點非常顯著，我們在切換主題的時候，不用動用任何一行原本的 HTML、不用改動任何的 CSS class，也不用在元件裡面寫切換主題的邏輯，就可以快速達成主題切換的效果。&lt;/p&gt;
&lt;p&gt;再來是 CSS 全域變數的部分，各主題的變數其實就是一個字串，上面的範例直接放在 JS 裡面，因此若遇到要&lt;strong&gt;更新主題顔色&lt;/strong&gt;、或&lt;strong&gt;新增新主題色&lt;/strong&gt;時，仍必須&lt;strong&gt;重新打包&lt;/strong&gt;（build）一次前端的專案。若要避免重新打包，就要看 CI/CD 的架構策略，將這些字串移到別的地方，然而這不在我目前擅長的領域，也非本次討論的重點，就交由各位同學去發揮吧！&lt;/p&gt;
&lt;p&gt;以上，Tailwind 主題切換分享到這裡，祝各位開發愉快 😎&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;參考資料&quot;&gt;參考資料&lt;/h2&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://tailwindcss.com&quot;&gt;Tailwind CSS - Rapidly build modern websites without ever leaving your HTML.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties&quot;&gt;Using CSS custom properties (variables) - CSS: Cascading Style Sheets | MDN&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/524696/how-to-create-a-style-tag-with-javascript&quot;&gt;html - How to create a &amp;#x3C;style&gt; tag with Javascript? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/574944/how-to-load-up-css-files-using-javascript&quot;&gt;html - How to load up CSS files using Javascript? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>tailwindcss</category><category>css</category><category>react</category></item><item><title>初探多執行緒實作模式</title><link>https://inorisense.casa/zh-tw/blog/multithreaded-patterns-in-brief/</link><guid isPermaLink="true">https://inorisense.casa/zh-tw/blog/multithreaded-patterns-in-brief/</guid><pubDate>Sun, 25 Sep 2022 00:00:00 GMT</pubDate><content:encoded>&lt;div&gt;&lt;h2 id=&quot;前言&quot;&gt;前言&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;本篇是略讀「&lt;a href=&quot;https://learning.oreilly.com/library/view/multithreaded-javascript/9781098104429/&quot;&gt;Multithreaded JavaScript&lt;/a&gt;」這本書第六章：「多執行緒實作模式」（Multithreaded Patterns），整理的一些筆記&lt;/p&gt;
&lt;p&gt;本章節介紹了一些多執行緒常見的實作模式，有以下：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;執行緒池（Thread Pool）&lt;/li&gt;
&lt;li&gt;互斥鎖（Mutex）&lt;/li&gt;
&lt;li&gt;環形緩衝（Ring Buffers）&lt;/li&gt;
&lt;li&gt;演員模型（Actor Model）&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;&lt;h2 id=&quot;執行緒池thread-pool&quot;&gt;執行緒池（Thread Pool）&lt;/h2&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;多執行緒應用程式很常使用的一個實作方式&lt;/li&gt;
&lt;li&gt;執行緒池是一個集合，裡面含有同質性（homogeneous）的 worker 執行緒，每個 worker 都可用來處理重負載、複雜運算的工作&lt;/li&gt;
&lt;li&gt;Node.js 的 &lt;code dir=&quot;auto&quot;&gt;libuv&lt;/code&gt; 函式庫提供了執行緒池的功能，預設為四個執行緒，可以處理底層 I/O 的一些操作&lt;/li&gt;
&lt;li&gt;概念很類似分散式系統&lt;/li&gt;
&lt;li&gt;分為兩部分討論：執行緒池的大小（Pool Size）與委派策略（Dispatch Strategies）&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h3 id=&quot;執行緒池的大小pool-size&quot;&gt;執行緒池的大小（Pool Size）&lt;/h3&gt;&lt;/div&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;以 Node.js 的 &lt;code dir=&quot;auto&quot;&gt;libuv&lt;/code&gt; 函式庫為例，裡面包含三個執行緒，分別是：主執行緒、worker 執行緒、垃圾回收執行緒（Garbage Collection Thread）&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h3 id=&quot;查看核心數量&quot;&gt;查看核心數量&lt;/h3&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;span data-icon=&quot;i-material-icon-theme:javascript&quot;&gt;&lt;/span&gt;Node.js&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// browser&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;cores &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; navigator&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;hardwareConcurrency&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;cores &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;&quot;&lt;/span&gt;&lt;span&gt;os&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;cpus&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;length&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;決定執行緒池的大小&quot;&gt;決定執行緒池的大小&lt;/h3&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;別忘了還有主執行緒，所以是 n + 1&lt;/li&gt;
&lt;li&gt;依據不同的目的來決定執行緒的數量
&lt;ul&gt;
&lt;li&gt;對於挖礦而言，99.9% 的工作在於 worker 上所執行繁重的運算，幾乎沒有 I/O，主執行緒也沒有特別的事情，因此可以開與核心數量相同的 worker 執行緒&lt;/li&gt;
&lt;li&gt;對串流影音或轉檔而言，則有大量的 CPU 運算及 I/O 操作，因此必須預留兩個核心給這兩個程序，剩下再分配給 worker 執行緒&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;如果不確定要如何分配，那麼留一個核心給主執行緒是一個安全的作法&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h2 id=&quot;委派策略dispatch-strategies&quot;&gt;委派策略（Dispatch Strategies）&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;我們將高成本、繁重的運算收集整理起來，然後分配個 worker 執行緒去執行這些任務，這裡介紹三個常見的委派策略：&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;輪詢調度round-robin&quot;&gt;輪詢調度（Round Robin）&lt;/h3&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;按照順序指派工作，指派到最後一位，下一次就回到第一位 worker 身上&lt;/li&gt;
&lt;li&gt;可以確保每位 worker 都有事做&lt;/li&gt;
&lt;li&gt;可能造成每位 worker 的負擔不均&lt;/li&gt;
&lt;li&gt;HAProxy 稱此委派策略為 roundrobin&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h3 id=&quot;隨機調度random&quot;&gt;隨機調度（Random）&lt;/h3&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;就如字面的意思，隨機選取一個 worker 來處理工作&lt;/li&gt;
&lt;li&gt;可能造成每位 worker 的負擔不均&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h3 id=&quot;閒置優先調度least-busy&quot;&gt;閒置優先調度（Least Busy）&lt;/h3&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;將新的任務指派給負載最低的 worker&lt;/li&gt;
&lt;li&gt;當有最低負載的 worker 有兩個的時候，&lt;strong&gt;隨機&lt;/strong&gt;選取一個指派&lt;/li&gt;
&lt;li&gt;HAProxy 稱此委派策略為 leastconn&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h2 id=&quot;互斥鎖mutex&quot;&gt;互斥鎖（Mutex）&lt;/h2&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Mutex 全名為 &lt;strong&gt;mutually exclusive lock&lt;/strong&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;在上鎖及釋放鎖定之間，稱之為「臨界區段」（Critical Section）&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h2 id=&quot;串流資料與環形緩衝ring-buffers&quot;&gt;串流資料與環形緩衝（Ring Buffers）&lt;/h2&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;環形緩衝是「先進先出」（first-in-first-out, aka FIFO）佇列的典型實作，利用一組「配對指標」指向當下的記憶體位址&lt;/li&gt;
&lt;li&gt;當佇列的指標在陣列末位，下一步會移動到陣列首位，形成環形結構的概念&lt;/li&gt;
&lt;li&gt;在北美餐廳中常被使用的點餐圓盤（order wheel）則是類比世界中，同樣的概念實踐&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h3 id=&quot;構成要素&quot;&gt;構成要素&lt;/h3&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;head 指標：指向下一個&lt;strong&gt;寫入&lt;/strong&gt;佇列的位置&lt;/li&gt;
&lt;li&gt;tail 指標：指向下一個&lt;strong&gt;讀取&lt;/strong&gt;佇列的位置&lt;/li&gt;
&lt;li&gt;佇列長度：我們想要建立多大的佇列，一個陣列長度，head 指標、tail 指標會在上面移動&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h3 id=&quot;運作規則&quot;&gt;運作規則&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;借用一下書中的示意圖：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://learning.oreilly.com/api/v2/epubs/urn%3Aorm%3Abook%3A9781098104429/files/assets/mtjs_0601.png&quot; alt=&quot;ring buffer&quot;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;當寫入佇列時，head 指標移動的下一個位置&lt;/li&gt;
&lt;li&gt;當讀取佇列時，tail 指標移動的下一個位置&lt;/li&gt;
&lt;li&gt;當指標位在佇列末位，下一次就會移到首位&lt;/li&gt;
&lt;li&gt;因為是&lt;strong&gt;環形&lt;/strong&gt;，所以沒有頭尾之分，因此指標在哪個位置並不重要&lt;/li&gt;
&lt;li&gt;tail 指標最多只會跟 head 指標在同一個位置，不能超過 head 指標&lt;/li&gt;
&lt;li&gt;緩衝區&lt;strong&gt;滿載&lt;/strong&gt;的時候，若要再寫入佇列，則有兩種策略：
&lt;ul&gt;
&lt;li&gt;覆蓋最舊的佇列：相較於未處理的舊資料，新資料比較重要&lt;/li&gt;
&lt;li&gt;拋出異常，且不寫入佇列：資料順序性很重要的時候&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;必須&lt;strong&gt;正確讀出&lt;/strong&gt;最舊佇列&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h3 id=&quot;fifolifo動態佇列&quot;&gt;FIFO、LIFO、動態佇列&lt;/h3&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;儲存在環形緩衝裡的每個元素不會做移動，只有加入與移除，適合實作「先進先出」（first-in-first-out, aka FIFO）佇列&lt;/li&gt;
&lt;li&gt;「非環形緩衝」若要實作 FIFO，則必須在處理完一個任務之後，移動佇列上所有的元素&lt;/li&gt;
&lt;li&gt;「非環形緩衝」較適合實作「後進先出」（last-in-first-out, aka LIFO）佇列&lt;/li&gt;
&lt;li&gt;JavaScript 的堆疊（stack）就是 LIFO 的實作&lt;/li&gt;
&lt;li&gt;動態改變佇列的大小，意味著必須重新賦予記憶體，會影響效能。若要實作動態佇列，鍊表（linked list）則較為適合&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h2 id=&quot;演員模型actor-model&quot;&gt;演員模型（Actor Model）&lt;/h2&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;演員模型是實踐同步運算的一種程式設計模式&lt;/li&gt;
&lt;li&gt;一個 actor 代表一個執行程式碼的容器&lt;/li&gt;
&lt;li&gt;在 Erlang 程式語言中，actor 是一級公民，但在 JavaScript 中也可以模擬其實作&lt;/li&gt;
&lt;li&gt;每個 actor 皆具有三種功能：執行運算、建立新的 actor、actor 之間的相互傳遞訊息&lt;/li&gt;
&lt;li&gt;每個 actor 擁有自己的訊息佇列（message queue），以 FIFO 佇列順序處理每個任務&lt;/li&gt;
&lt;li&gt;因為 actor 都無法操作共享記憶體（shared memory），因此避免了多執行緒容易發生的情況：race condition 及 deadlock&lt;/li&gt;
&lt;li&gt;actor 是單執行緒，一次執行一件事&lt;/li&gt;
&lt;li&gt;使用 actor 的系統必須能夠接受延遲或順序不一致的現象&lt;/li&gt;
&lt;li&gt;每個 actor 可以擁有一個位址，例如：&lt;code dir=&quot;auto&quot;&gt;tcp://127.0.0.1:1234/3&lt;/code&gt; 代表著在 1234 port 中，第三個 actor&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;借用一下書中的示意圖：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://learning.oreilly.com/api/v2/epubs/urn%3Aorm%3Abook%3A9781098104429/files/assets/mtjs_0602.png&quot; alt=&quot;actor model&quot;&gt;&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;參考資料&quot;&gt;參考資料&lt;/h2&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/HAProxy&quot;&gt;HAProxy - Wikipedia&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://zh.wikipedia.org/zh-tw/%E4%BA%92%E6%96%A5%E9%94%81&quot;&gt;互斥鎖 - 維基百科，自由的百科全書&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://zh.wikipedia.org/zh-tw/%E8%87%A8%E7%95%8C%E5%8D%80%E6%AE%B5&quot;&gt;臨界區段 - 維基百科，自由的百科全書&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://juejin.cn/post/7113550346835722276#heading-7&quot;&gt;ring buffer，一篇文章講透它？ - 掘金&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/FIFO_(computing_and_electronics)&quot;&gt;FIFO (computing and electronics) - Wikipedia&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Circular_buffer&quot;&gt;Circular buffer - Wikipedia&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.techbridge.cc/2019/06/21/actor-model-in-web/&quot;&gt;46 年老技術與 Web 的新火花 - Actor Model in Web&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ouch1978.github.io/blog/2022/09/21/common-locks-in-programming-world&quot;&gt;一張圖看懂寫程式時常見的幾種鎖 | OUCH1978@GITHUB&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>JavaScript</category><category>筆記</category></item><item><title>GitFlow &amp; Blog Version Control</title><link>https://inorisense.casa/zh-tw/blog/gitflow-and-blog-version-control/</link><guid isPermaLink="true">https://inorisense.casa/zh-tw/blog/gitflow-and-blog-version-control/</guid><pubDate>Mon, 05 Sep 2022 00:00:00 GMT</pubDate><content:encoded>&lt;aside&gt; &lt;p&gt;This article has not been translated into English yet.&lt;/p&gt; &lt;a href=&quot;https://inorisense.casa/en/blog/gitflow-and-blog-version-control/&quot;&gt;Read the original article&lt;/a&gt; &lt;/aside&gt;</content:encoded></item><item><title>鴨川的各種風貌</title><link>https://inorisense.casa/zh-tw/blog/kamogawa_scenes/</link><guid isPermaLink="true">https://inorisense.casa/zh-tw/blog/kamogawa_scenes/</guid><pubDate>Wed, 31 Aug 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;曾經一段時間住在京都車站的南側，大概是十條車站那一帶。&lt;/p&gt;
&lt;p&gt;因為鄰近鴨川，所以很常去那裡散步或跑步。説到鴨川這條河流，想必去過京都旅遊的人都會有印象。鴨川由南邊一路向北，貫穿京都市的各個著名的觀光景點。京都市内東西向的幾條大路，我想很多人並不陌生。從北邊的「二條」一直到靠近京都車站南邊的「十條」。如果對路名不太有概念也沒什麽關係，因爲我也不是非常地熟門熟路。我自創一個口訣來瞭解全貌：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;古城二條、祇園四條、清水五條、前站七條、後站九條&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;二條最有名的景點大概就是二條城；四條則是祇園、八坂神社、鴨川納涼床，還有商店街、咖啡店、旅館與酒吧；五條非清水寺、產寧坂莫屬；七條則是京都車站的前站，有京都電視塔與 Yodobashi Camera；京都後站大概就是東寺、東福寺、Aeon Mall&lt;/p&gt;
&lt;p&gt;或許你好奇問：「那十條有什麼？」&lt;/p&gt;
&lt;p&gt;十條是個很平凡的地方，硬要說有什麼地標的話，&lt;strong&gt;任天堂&lt;/strong&gt;總部在這裡，像是一塊龐大的白色石碑佇立在那裡，直到晚上九點多仍是燈火通明。&lt;/p&gt;
&lt;p&gt;起初，鴨川給我的印象是一個充滿濃濃京都風、河邊充滿著閒散旅客的地方，或是夏天的「鴨川納涼床」（雖然我從來沒進去坐過）。但實際上那卻只是其中的一小部分而已。或者該說，大部分的景象其實沒有那麼地唯美。&lt;/p&gt;
&lt;p&gt;沿著主要幹道往東邊走，就會遇到鴨川。因為過橋之後就要上交流道，路上的車快速疾駛，空氣中充斥著引擎聲。橋的前方有一整區的集合住宅大樓，一整排站開來望著鴨川，我從那個社區旁一個狹窄走道溜進去，迎面而來就是鼎鼎大名的鴨川了。陽光佈滿這個河床，更顯得綠意盎然，河邊的步道緊鄰著水域，也沒什麼欄杆之類的人造物，感覺接近了大自然一點。步道上幾乎沒有人，散步兼散心，想說就這樣一路往北走，能走多遠就走多遠。&lt;/p&gt;
&lt;p&gt;潺潺流水聲、映入眼簾的各種鳥類、植物、河床上滿地的礫石，說真的真是另一種享受，像是「我正存在著」的那種感覺。有時會看到零星幾個孩子在河裡戲水、或是大叔們坐在那靜靜地釣魚。平凡無奇的景色，也會來一點不一樣的驚喜。偶爾在河床上看到一堆堆的石碓，各種不同的型態，依然故我地佇立在那裡。起初以為是因爲&lt;strong&gt;超自然力量&lt;/strong&gt;或&lt;strong&gt;都市傳説&lt;/strong&gt;之類的緣故造成的（做什麼白日夢），後來查了一下，是稱之為「賽之河原」的傳說故事。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://kotobank.jp/word/%E8%B3%BD%E3%81%AE%E6%B2%B3%E5%8E%9F-508276&quot;&gt;賽之河原&lt;/a&gt; &lt;sup&gt;&lt;a href=&quot;#user-content-fn-0&quot; id=&quot;user-content-fnref-0&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; 的傳説是出自於日本的中世紀時代，但也有種説法是出自於佛教的「法華經」。人們相信比父母親早逝的孩子們，會在陰間 &lt;sup&gt;&lt;a href=&quot;#user-content-fn-1&quot; id=&quot;user-content-fnref-1&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; 的河床 &lt;sup&gt;&lt;a href=&quot;#user-content-fn-2&quot; id=&quot;user-content-fnref-2&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;3&lt;/a&gt;&lt;/sup&gt; ——也就是西院（齋院）的河床上遭遇苦難，他們會在此堆起石頭，然而惡鬼會出現，撞倒那些石碓，並責罵那些孩子們，此時地藏菩薩就會出現拯救那些孩子。「賽」一字，一說是出自於京都的佐比川（「佐比」音同「賽」）或是奈良的狹井川（「狹井」音同「賽」）。&lt;/p&gt;
&lt;p&gt;原來是一個帶點悲傷的故事，而不是什麼都市傳說。我不知道做那些石碓的人們是不是都發生這樣的事。每次經過時，雖然沒有認真數過，總感覺有增加新的石碓，卻從來沒有看過正在堆疊石頭的人影。我不禁思索，他們是在何時來這裏堆石頭呢？又是帶著如何的心情？這個故事帶給我一種悲傷但沉靜的感覺，卻隱隱有股强大的意志力。&lt;/p&gt;
&lt;p&gt;每從一座橋下穿越，就代表著又往前了一點。有些橋是鐵路專用，有些則是一般道路，給行人、汽車使用。尤其是站在鐵路專用橋的下方，距離大概只有兩公尺左右，往上可以看到陽光從鐵軌之間灑下，與其説是木漏日&lt;sup&gt;&lt;a href=&quot;#user-content-fn-3&quot; id=&quot;user-content-fnref-3&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;，應該稱作是&lt;strong&gt;鐵&lt;/strong&gt;漏日才是。不過原本柔幻似水的詞，用在此確實少了那種意境，反帶了點陽剛的氣息。當電車經過的時候，壓過鐵軌發出的聲響可真是震撼，心想著自己正站在沉重巨大疾駛電車的正下方，那種感覺真是難以言喻。&lt;/p&gt;
&lt;p&gt;越往北邊，河邊的步道逐漸變得寬敞，也越來越多人影。有像是音樂系的學生（我猜的），面著河吹著小號或是薩克斯風，我不太懂音樂，但伴隨著鴨川的流水聲與充滿綠意的河床，確實是視覺與聽覺上的享受。還有人帶了小型烤肉架，圍坐著吃肉、喝啤酒。以往我都是用橋來辨認位置的（例如：「啊，三條大橋到了」）但其實從人們在河邊休閒活動，就可以大略知道目前正位於鴨川的哪一段。繼續往北走，就會看到有名的「鴨川納涼床」，上面塞滿了旅客，納涼床附近一帶的河床非常寬廣，但是同樣也擠滿了人，而且一眼望去，不只日本人，外國人的面孔也不在少數。你可以想像是在大安森林公園的草地上，擠滿了人圍坐成一個個圈子那種光景。離開河床，走上去看看旁邊的街道。四條烏丸附近交通特別地繁忙，烏黑得閃閃發亮的計程車到處都是，還有滿滿的人。這裡大概是最熱鬧的地方吧，有各種餐廳、商店街、咖啡店、酒吧，還有百貨公司及旅館，外國人密集度最高的地方，總像是走在大阪梅田車站附近的感覺。&lt;/p&gt;
&lt;p&gt;其實從十條走到四條、三條這一帶，腳已經很酸了，雖然只要走回街道上搭個電車就可以輕鬆回家，但我實在不想這麼做。充滿吵雜與人群的商店街會讓我更身心更疲憊。於是我選擇沿著原路回去，我還想要多聽聽鴨川的聲音。天色漸暗，四條烏丸一帶的人卻仍然很多，京都的夜生活才剛開始呢，這裡大概是京都少有的夜生活吧。托觀光客的福，店家才願意營業到比較晚的時間。三條若再繼續走下去，直到出町柳站，那裡是鴨川與今出川的匯流處，因為是交匯處所以水域非常寬廣，有很多觀光客、學生們，還有有名的跳石。&lt;/p&gt;
&lt;p&gt;台灣有些地方的河濱公園規劃得很好，非常適合晚間散步或運動，所以我想說也可以在鴨川試試，沒想到出乎意料。吃完晚餐後，我走向通往交流道的橋邊，看到橋的對面，小鋼珠店閃亮的招牌，讓我聯想到美國拉斯維加斯的霓虹招牌，雖然我沒去過拉斯維加斯。一樣走過河邊社區旁狹窄的走道，卻馬上感到異樣。完全沒有路燈，白天走的時候完全沒有注意到，晚上卻異常得明顯，河邊一盞路燈都沒有，幾乎只有微弱的月光，聽起來很浪漫？一點也不。白天都沒什麼人了，更別提晚上，此時也就不逞強說自己不怕黑了，走路的時候總是瞻前顧後、提心吊膽，沒有人還好，看到疑似人影就嚇得半死。鴨川景色依舊，只是籠罩在黑夜裡，整個氛圍就不太一樣了。心中不斷拉扯：「再走一小段？還是掉頭直接回家？」最後在前方一個小出口，還有一盞路燈（當下心裡則稱之為&lt;strong&gt;明燈&lt;/strong&gt;）的位置離開了鴨川。&lt;/p&gt;
&lt;p&gt;那次真是有驚無險的經驗，雖然我什麼也沒看到、沒聽到、更沒遇到什麼事，但心裡總有種說不出的不安。當下還冒出「就算在這裡被殺也不會馬上被發現」的想法。才晚上八點多而已，河邊只剩一片漆黑 ⋯⋯ 我想納涼床那邊應該還是車水馬龍、燈火通明吧，彷彿完全不同的世界，若問哪一個比較接近日常的鴨川，我應該會選差點把我嚇死的那一種吧，但是僅限於白天就是了。只要走一次，就能享受鴨川的不同風情、不同的魅力。不過若邀我夜遊鴨川，恕我敬謝不敏。&lt;/p&gt;
&lt;p&gt;{/*
Ref:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://osaka.letsgojp.com/archives/72649/&quot;&gt;https://osaka.letsgojp.com/archives/72649/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.plurk.com/p/f5fh9i&quot;&gt;https://www.plurk.com/p/f5fh9i&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://matcha-jp.com/tw/3709&quot;&gt;https://matcha-jp.com/tw/3709&lt;/a&gt;
*/}&lt;/li&gt;
&lt;/ul&gt;
&lt;section data-footnotes=&quot;&quot;&gt;
&lt;ol&gt;
&lt;li id=&quot;user-content-fn-0&quot;&gt;
&lt;p&gt;賽の河原（さいのかわら） &lt;a href=&quot;#user-content-fnref-0&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 1&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-1&quot;&gt;
&lt;p&gt;冥土（めいど） &lt;a href=&quot;#user-content-fnref-1&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 2&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-2&quot;&gt;
&lt;p&gt;河原（かわら） &lt;a href=&quot;#user-content-fnref-2&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 3&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-3&quot;&gt;
&lt;p&gt;木漏れ日：日文中非常有詩意、卻難以翻譯的詞彙之一，字面意思是「從樹葉空隙灑進來的陽光」 &lt;a href=&quot;#user-content-fnref-3&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 4&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</content:encoded><category>京都</category><category>日本</category><category>日常</category><category>re:wind</category></item><item><title>E2E 測試導向的開發流程</title><link>https://inorisense.casa/zh-tw/blog/e2e-testing-oriented-developing-process/</link><guid isPermaLink="true">https://inorisense.casa/zh-tw/blog/e2e-testing-oriented-developing-process/</guid><pubDate>Sun, 28 Aug 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;某天，我們從設計師那裡拿到了設計圖&lt;/p&gt;
&lt;p&gt;於是我們開始切版，跟往常一樣，主要注重在畫面的樣式與互動上，此時並不會考慮到 E2E 測試&lt;/p&gt;
&lt;p&gt;若開發期間，很幸運地拿到了 QA 的測試案例（或任何寫測試案例的人），我們是否可以在切版的當下，也將未來寫測試案例的情況也加入考慮？&lt;/p&gt;
&lt;p&gt;此外，這裡提到的測試案例是指 E2E 測試（自動化測試），而非人工的整合測試&lt;/p&gt;
&lt;p&gt;為了讓寫測試的過程能更順利，依據測試案例上的各種 action，例如：點擊某個 button、在 input 輸入文字，或是從某個 div 元素上獲取文字。在這些計畫做事的 DOM 元件上安插屬性（attribute）作為 &lt;code dir=&quot;auto&quot;&gt;querySelector&lt;/code&gt; 的查詢指標&lt;/p&gt;
&lt;p&gt;若手上沒有測試案例又該如何呢？沒有測試案例，就無法準確知道哪些元素會被查詢，只能用推測的。此時腦中閃過一個想法：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;如果制定一些規則，然後按照這個規則去添加屬性，是否就可以維持一定的覆蓋率？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;因此我根據自己寫測試的經驗，嘗試制定了一些規則。我將會分成以下幾個部分說明：&lt;/p&gt;
&lt;ol&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;/ol&gt;
&lt;p&gt;以下所有程式，我會以 React 元件來解釋。實際上無論框架，最終都會回到 HTML 元素身上&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;元件取向&quot;&gt;元件取向&lt;/h2&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;頁面元件page-component&quot;&gt;頁面元件（Page Component）&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;插入 &lt;code dir=&quot;auto&quot;&gt;data-test-page&lt;/code&gt; 屬性於&lt;strong&gt;最外層&lt;/strong&gt;的 HTML 元素：&lt;code dir=&quot;auto&quot;&gt;data-test-page=&apos;&amp;#x3C;頁面元件名稱&gt;&apos;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;有了 &lt;code dir=&quot;auto&quot;&gt;data-test-page&lt;/code&gt; 屬性，我們可以很快地辨認出這個頁面元件的&lt;strong&gt;範圍&lt;/strong&gt;（我更喜歡稱之為「邊界」）&lt;/p&gt;
&lt;p&gt;註：屬性名稱全看個人喜好，這裡展示的是我自己覺得不錯的命名方式 🙃&lt;/p&gt;
&lt;p&gt;例如我們有一個 home 元件：&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;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;HomePage&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;&amp;#x3C;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;data-test-page&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;&gt;{&lt;/span&gt;&lt;span&gt;/* ... */&lt;/span&gt;&lt;span&gt;}&amp;#x3C;/div&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;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;基本元件basic-component&quot;&gt;基本元件（Basic Component）&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;插入 &lt;code dir=&quot;auto&quot;&gt;data-test-comp&lt;/code&gt; 屬性於最外層的 HTML 元素：&lt;code dir=&quot;auto&quot;&gt;data-test-comp=&apos;&amp;#x3C;元件名稱&gt;&apos;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;有了 &lt;code dir=&quot;auto&quot;&gt;data-test-comp&lt;/code&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;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Button&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;&amp;#x3C;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;data-test-comp&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;&gt;{&lt;/span&gt;&lt;span&gt;/* ... */&lt;/span&gt;&lt;span&gt;}&amp;#x3C;/div&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;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h2 id=&quot;位置取向&quot;&gt;位置取向&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;元件會在不同地方重複使用（這也是我們要抽成元件的初衷，DRY 原則），因此，&lt;code dir=&quot;auto&quot;&gt;data-test-comp&lt;/code&gt; 就會出現重複的情況，而且如果在相近的地方有多個重複的元件，就不太好快速辨識目標元件&lt;/p&gt;
&lt;p&gt;比如說我們在一個頁面上，用了 5 個 button 元件，而測試案例的動作是：「選取其中一個 button，然後點擊它。」此時，我們必須先找出所有 button 的元素，然後在裡面尋找我們要做點擊的那個 button（比對 button 的文字；或是直接指定&lt;strong&gt;第幾個&lt;/strong&gt; button 元素 ⋯⋯ 等）&lt;/p&gt;
&lt;p&gt;這個過程不困難，但我認為這個過程很煩，而且不斷重複在做這個查找的動作。於是我開始思考，是否有更精確、更快速的方式。如果有一個唯一值（或接近唯一的值，亦即很少重複），這個元素尋找的過程將會簡單許多&lt;/p&gt;
&lt;p&gt;因此，像是這樣的情境，我們需要另一個表示&lt;strong&gt;功能&lt;/strong&gt;的特徵&lt;sup&gt;&lt;a href=&quot;#user-content-fn-0&quot; id=&quot;user-content-fnref-0&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; 屬性（這個特徵屬性最好是唯一值）&lt;/p&gt;
&lt;p&gt;使用 &lt;code dir=&quot;auto&quot;&gt;data-test-feat&lt;/code&gt; 屬性，用來辨識這個元件&lt;strong&gt;在這裡&lt;/strong&gt;所提供的功能（或目的）&lt;/p&gt;
&lt;p&gt;我認為這個屬性比較偏向是非強制性的。倘若只有在元件上標示屬性，仍然可以拿到我們想要的元件。例如：在導覽列（navbar 元件）上的登入 button 及選單裡（menu 元件）的登入 button，分別包含在不同元件裡面。此外，過度使用可能會造成最後很多重複的屬性，唯一性的特徵消失了，&lt;strong&gt;精準打擊&lt;/strong&gt;（aka 準確查詢）的初衷也會隨之化為泡影。因此，要在這之間取得平衡&lt;/p&gt;
&lt;p&gt;所以，&lt;code dir=&quot;auto&quot;&gt;data-test-feat&lt;/code&gt; 屬性大概有兩種加入方式：&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;在容器元素container-element&quot;&gt;在容器元素（container element）&lt;/h3&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;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;HomePage&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;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;&gt;&lt;/span&gt;&lt;/div&gt;&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;#x3C;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;data-test-feat&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;confirmBtn&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&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;#x3C;&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/&gt;&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;#x3C;/div&gt;&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;#x3C;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;data-test-feat&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;cancelBtn&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&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;#x3C;&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/&gt;&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;#x3C;/div&gt;&lt;/span&gt;&lt;/div&gt;&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;#x3C;/&gt;&lt;/span&gt;&lt;/div&gt;&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;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;上面的範例，可以很清楚地知道，在 home 頁面裡，有兩個 button：確認 button 及取消 button&lt;/p&gt;
&lt;p&gt;如果在元件外面包一層容器元素不是你的菜，可以透過 &lt;code dir=&quot;auto&quot;&gt;props&lt;/code&gt; 的方式穿進去元件裡：&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;透過-props-安插在元件上&quot;&gt;透過 props 安插在元件上&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;若沒有外層的容器元素，透過 props 傳進去是另一個選擇：&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;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;HomePage&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;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;&gt;&lt;/span&gt;&lt;/div&gt;&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;#x3C;&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;dataTestFeat&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;confirmBtn&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/&gt;&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;#x3C;&lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;dataTestFeat&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;cancelBtn&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/&gt;&lt;/span&gt;&lt;/div&gt;&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;#x3C;/&gt;&lt;/span&gt;&lt;/div&gt;&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;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;在 button 元件裡：&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;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;span&gt; dataTestFeat&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; btnText&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; labelText &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;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;data-test-comp&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;&gt;&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;#x3C;label&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;labelText&lt;/span&gt;&lt;span&gt;}&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;#x3C;button&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;data-test-feat&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;dataTestFeat&lt;/span&gt;&lt;span&gt;}&gt;{&lt;/span&gt;&lt;span&gt;btnText&lt;/span&gt;&lt;span&gt;}&amp;#x3C;/button&gt;&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;#x3C;/label&gt;&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;#x3C;/div&gt;&lt;/span&gt;&lt;/div&gt;&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;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h2 id=&quot;狀態取向&quot;&gt;狀態取向&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;有時候我們想知道某個元件的狀態，假如我們有一個 toggle button 元件：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;toggle button&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;400&quot; height=&quot;299&quot; src=&quot;https://inorisense.casa/_astro/toggle-button.A9JvYDJ6_1UvT9D.webp&quot;&gt;&lt;/p&gt;
&lt;p&gt;測試案例中，我們想要在點擊 toggle button 之後，確認它會確實從 on 狀態轉變成 off 的狀態&lt;/p&gt;
&lt;p&gt;我們當然可以透過辨認 style 的變化得知它的狀態。如果是用 SASS 的話，或許可以很簡單地從 class 上辨認出（端看 class 怎麼設計了）。然而我最近比較常用的是 tailwindcss，此時就會變得不太直覺。以下是我的 toggle button 元件：&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;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ToggleButton&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;[&lt;/span&gt;&lt;span&gt;isOn&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; setIsOn&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;useState&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&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;handleChange&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;=&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;setIsOn&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; &lt;/span&gt;&lt;span&gt;=&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;/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;&amp;#x3C;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;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;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;className&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;rounded-full overflow-hidden relative w-16 h-7 shrink-0 text-white font-semibold text-sm uppercase ml-2.5 bg-neutral-500&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;onClick&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;handleChange&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;/* here is what the difference by state */&lt;/span&gt;&lt;span&gt;}&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;#x3C;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;className&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;absolute transition left-1.5 top-1/2 -translate-y-1/2 &lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;isOn&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;#x26;&amp;#x26;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;translate-x-9&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;}&gt;&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;#x3C;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;className&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;rounded-full bg-white w-4 h-4 relative&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&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;#x3C;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;className&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;absolute right-full top-1/2 -translate-y-1/2 px-2 whitespace-nowrap&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt;On&lt;/span&gt;&lt;span&gt;&amp;#x3C;/div&gt;&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;#x3C;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;className&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;absolute left-full top-1/2 -translate-y-1/2 px-2 whitespace-nowrap&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt;Off&lt;/span&gt;&lt;span&gt;&amp;#x3C;/div&gt;&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;#x3C;/div&gt;&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;#x3C;/div&gt;&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;#x3C;/button&gt;&lt;/span&gt;&lt;/div&gt;&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;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;這個元件裡，&lt;code dir=&quot;auto&quot;&gt;translate-x-9&lt;/code&gt; 是 on 與 off 狀態上差異的 class。我們仍然可以辨識，但會造成修改樣式時後容易抓不到元素的情況。Utility first 樣式庫的優點，在此時卻成了缺點&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;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Button&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;span&gt; btnText&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; labelText &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;[&lt;/span&gt;&lt;span&gt;isOn&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; setIsOn&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;useState&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&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;handleChange&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;=&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;setIsOn&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; &lt;/span&gt;&lt;span&gt;=&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;/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;&amp;#x3C;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;data-test-comp&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;&gt;&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;#x3C;label&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;labelText&lt;/span&gt;&lt;span&gt;}&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;#x3C;button&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;data-test-state&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;isOn&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;onClick&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;handleChange&lt;/span&gt;&lt;span&gt;}&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;btnText&lt;/span&gt;&lt;span&gt;}&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;#x3C;/button&gt;&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;#x3C;/label&gt;&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;#x3C;/div&gt;&lt;/span&gt;&lt;/div&gt;&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;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;另一個常見的情境是載入狀態（loading），假如有一個列表與搜尋列元件：&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;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;SearchPage&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;// ...some state here&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;&amp;#x3C;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;data-test-page&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;searchPage&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&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;#x3C;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;data-test-comp&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;searchBar&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&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;#x3C;input&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;searchValue&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;data-test-feat&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;searchInput&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/&gt;&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;#x3C;button&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;onClick&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;handleSearch&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;data-test-feat&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;searchBtn&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&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;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&amp;#x3C;/button&gt;&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;#x3C;/div&gt;&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;#x3C;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;data-test-comp&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;list&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;data-test-loading&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;isLoading&lt;/span&gt;&lt;span&gt;}&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;/* list data here */&lt;/span&gt;&lt;span&gt;}&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;#x3C;/div&gt;&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;#x3C;/div&gt;&lt;/span&gt;&lt;/div&gt;&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;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;註：基於可讀性，我直接用 HTML 表示&lt;/p&gt;
&lt;p&gt;點擊搜尋 button 之後，頁面就會開始 loading 打 API 拿資料，並在結束後，結束 loading 狀態，並更新資料至底下的列表&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;無形資料取向&quot;&gt;無形資料取向&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;假如有一個 card 元件要顯示商品資訊，如在元素上存在 product id 的話，就能快速地找到目標商品。然而商品資訊卡片通常不會顯示 product id，此時就必須將這個值加到屬性當中：&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;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ProductCard&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;span&gt; productId&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;span&gt;restProps &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;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;data-test-comp&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;productCard&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;data-test-product-id&lt;/span&gt;&lt;span&gt;={&lt;/span&gt;&lt;span&gt;productId&lt;/span&gt;&lt;span&gt;}&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;/* product card 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;&amp;#x3C;/div&gt;&lt;/span&gt;&lt;/div&gt;&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;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;至於命名原則，我認為只要夠&lt;strong&gt;語意化&lt;/strong&gt;就行了&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;結構取向&quot;&gt;結構取向&lt;/h2&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;模擬元素simulated-element&quot;&gt;模擬元素（Simulated Element）&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;在一些特殊情境下，我們會需要用 &lt;code dir=&quot;auto&quot;&gt;div&lt;/code&gt; 元素去&lt;strong&gt;模擬&lt;/strong&gt;其他元素&lt;/p&gt;
&lt;p&gt;這裡舉個例子，用 &lt;code dir=&quot;auto&quot;&gt;div&lt;/code&gt; 元素去模擬 &lt;code dir=&quot;auto&quot;&gt;table&lt;/code&gt; 元素的結構。加入 &lt;code dir=&quot;auto&quot;&gt;data-test-el&lt;/code&gt; 屬性：&lt;code dir=&quot;auto&quot;&gt;data-test-el=&apos;&amp;#x3C;模擬的元素名稱&gt;&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;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Table&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;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;data-test-el&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;table&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&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;#x3C;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;data-test-el&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;tbody&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;{&lt;/span&gt;&lt;span&gt;/* ... */&lt;/span&gt;&lt;span&gt;}&amp;#x3C;/div&gt;&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;#x3C;/div&gt;&lt;/span&gt;&lt;/div&gt;&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;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;或許會有人不太懂為何要這樣做，但我還是可以簡單說明一下&lt;/p&gt;
&lt;p&gt;在開發的時候，遇到某些樣式在與 table 有關的元素上（也就是&lt;code dir=&quot;auto&quot;&gt;&amp;#x3C;table&gt;&lt;/code&gt;、&lt;code dir=&quot;auto&quot;&gt;&amp;#x3C;thead&gt;&lt;/code&gt;、&lt;code dir=&quot;auto&quot;&gt;&amp;#x3C;tbody&gt;&lt;/code&gt;⋯⋯ 等），在 Safari 瀏覽器下的顯示會不如預期（Safari 的 bug ⋯⋯），因此必須用 &lt;code dir=&quot;auto&quot;&gt;div&lt;/code&gt; 元素&lt;strong&gt;重建&lt;/strong&gt;整個 table 的結構，因此在加入屬性之後，可以很快地了解整個 table 的結構，藉此增加可讀性&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;元件的結構語意化&quot;&gt;元件的結構語意化&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;誠如模擬元素，為了增加可讀性，我們也可以在其他地方加入屬性，可以更快地了解整個元件結構。例如一個 dialog 元件：&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;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Dialog&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;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;data-test-comp&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;dialog&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&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;#x3C;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;data-test-el&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;dialogHeader&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt;this is dialog header&lt;/span&gt;&lt;span&gt;&amp;#x3C;/div&gt;&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;#x3C;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;data-test-el&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;dialogBody&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt;this is dialog body&lt;/span&gt;&lt;span&gt;&amp;#x3C;/div&gt;&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;#x3C;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;data-test-el&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;dialogFooter&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt;this is dialog footer&lt;/span&gt;&lt;span&gt;&amp;#x3C;/div&gt;&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;#x3C;/div&gt;&lt;/span&gt;&lt;/div&gt;&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;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;加入 &lt;code dir=&quot;auto&quot;&gt;data-test-el&lt;/code&gt; 屬性之後，可以有效率地一眼看出整個元件的結構（在這裡，dialog 分為 dialog header、dialog body、dialog footer 三個區塊）&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;結語&quot;&gt;結語&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;開發時，加入屬性將會是一個漫長的過程。在開發的過程中，測試案例並非我們的優先考量。制定規則並遵守它，可以讓我們無腦地加入屬性，並將專注力放在實作樣式外觀、操作互動以及資料上。當開發結束後，撰寫測試案例將會更為順利，並感謝以前的自己&lt;/p&gt;
&lt;p&gt;最後，說到底這些規則畢竟只是自己的想法，並非什麼業界標準那麼偉大的東西 😎&lt;/p&gt;
&lt;p&gt;開發愉快&lt;/p&gt;
&lt;section data-footnotes=&quot;&quot;&gt;
&lt;ol&gt;
&lt;li id=&quot;user-content-fn-0&quot;&gt;
&lt;p&gt;Feature，我翻譯成「特徵」 &lt;a href=&quot;#user-content-fnref-0&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 1&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</content:encoded><category>E2E Testing</category><category>筆記</category><category>JavaScript</category><category>React</category><category>前端</category></item><item><title>機動性與獨立傢俱</title><link>https://inorisense.casa/zh-tw/blog/about-movable-furniture/</link><guid isPermaLink="true">https://inorisense.casa/zh-tw/blog/about-movable-furniture/</guid><pubDate>Sun, 21 Aug 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;我對擁有&lt;strong&gt;機動性&lt;/strong&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;雖說沒有要「先查看當日運勢，再依照幸運方位做擺設」如此般地狂熱&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;
&lt;p&gt;基本上就看房東的屋子是如何就是如何&lt;/p&gt;
&lt;p&gt;硬是要塞進大型傢俱，對居住的當事人而言，恐怕也是很困擾的&lt;/p&gt;
&lt;p&gt;所幸現在的租屋處，在搬進來前，是&lt;strong&gt;幾近空屋&lt;/strong&gt;的狀態&lt;/p&gt;
&lt;p&gt;說是&lt;strong&gt;得來不易&lt;/strong&gt;，我覺得一點也不誇張&lt;/p&gt;
&lt;p&gt;然而，我也沒有因此要大顯身手、打造成 Ikea 樣品屋的北歐風，或無印良品風&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;img src=&quot;https://excellentjourney.files.wordpress.com/2015/06/jobs-final.jpg&quot; alt=&quot;steve jobs in living room&quot;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;身為極簡主義者，適當的空白是很重要的&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;這句台詞頗適合，但他本人並沒有說過這種話就是了&lt;/p&gt;
&lt;p&gt;不過無需將這概念想得太過崇高&lt;/p&gt;
&lt;p&gt;其實概念很簡單的：&lt;/p&gt;
&lt;img src=&quot;https://inorisense.casa/_astro/im-minimalist-im-poor.zR0mFVU2_2aX2qm.webp&quot; alt=&quot;i&amp;#x27;m minimalist&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;500&quot; height=&quot;500&quot;&gt;
&lt;p&gt;或許，把身為凡人的我與老賈相比，本來就不是個明智的作為&lt;/p&gt;</content:encoded><category>生活</category><category>空想</category></item><item><title>學習摩斯電碼</title><link>https://inorisense.casa/zh-tw/blog/learning-morse-code/</link><guid isPermaLink="true">https://inorisense.casa/zh-tw/blog/learning-morse-code/</guid><pubDate>Sat, 23 Jul 2022 00:00:00 GMT</pubDate><content:encoded>&lt;div&gt;&lt;h2 id=&quot;前言&quot;&gt;前言&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;最近在重溫一款以前很喜歡的遊戲：絕地戰兵（&lt;a href=&quot;https://store.steampowered.com/agecheck/app/394510/&quot;&gt;HELLDIVERS&lt;/a&gt;）&lt;/p&gt;
&lt;p&gt;遊戲中有一個叫做「真實傳播器」（Truth Transmitter）的裝置，在任務完成後，它會發出摩斯電碼訊號，其傳輸的訊號是：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;··· ··- ·—· · ·-· -····- · ·- ·-· - ···· -··· · ·- -·-· --- -· ·- -·-· - ·· ···- · ·-·-·-&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;意思是：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;SUPER-EARTHBEACONACTIVE.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;↓ 真實傳播器 ↓
&lt;img src=&quot;https://static.wikia.nocookie.net/helldivers_gamepedia/images/4/49/Packed-transmitter.jpg&quot; alt=&quot;truth-transmitter&quot;&gt;&lt;/p&gt;
&lt;p&gt;這讓我對摩斯電碼產生興趣&lt;/p&gt;
&lt;p&gt;於是就開始在網路上尋找摩斯電碼的學習資源&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;開始學習&quot;&gt;開始學習&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Google 提供了一個線上的摩斯密碼學習工具，可以在手機或是電腦上開啟，可以立即開始學習，有興趣的同學可以點選連結前往：&lt;a href=&quot;https://morse.withgoogle.com/learn/&quot;&gt;Morse - Learn&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;這個學習工具利用圖像學習記憶法，去記憶每一個字幕以及對應的符號，感覺非常地有用&lt;/p&gt;
&lt;p&gt;雖然在學習的當中，感覺有點像是在學海軍常用的「&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E5%8C%97%E7%BA%A6%E9%9F%B3%E6%A0%87%E5%AD%97%E6%AF%8D&quot;&gt;北約音標字母&lt;/a&gt;（&lt;a href=&quot;https://en.wikipedia.org/wiki/NATO_phonetic_alphabet&quot;&gt;NATO phonetic alphabet&lt;/a&gt;）」（也就是所謂的 Alpha、Bravo、Charlie、……），
但那些北約音標字母對應的單詞，都是世界通用的，而非只是方便記憶&lt;/p&gt;
&lt;p&gt;事實上，我也有找到其他的記憶學習法&lt;/p&gt;
&lt;p&gt;學習的過程中，每個字母對應一個單字，然後利用這個單字將其意義圖像化之後，聯想對應的密碼組合，感覺滿有趣的&lt;/p&gt;
&lt;p&gt;Google 的這套學習方式，其字母對照表如下：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;hello morse&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1000&quot; height=&quot;1500&quot; src=&quot;https://inorisense.casa/_astro/Hello-Morse-Google.CSjXXq-N_1cOHbz.webp&quot;&gt;&lt;/p&gt;
&lt;p&gt;當然，除了英文字母外，還有數字的圖像記憶，這裡就不列出來了，待有興趣的同學自行探索&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;摩斯電碼的規則&quot;&gt;摩斯電碼的規則&lt;/h2&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;基本元素&quot;&gt;基本元素&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;從前面的對照表中，我們知道基本上是由短訊號、長訊號兩種元素組成&lt;/p&gt;
&lt;p&gt;但基本組成元素大致可分為分為五種：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;點（或稱「滴」）：短訊號，符號表示為 &lt;code dir=&quot;auto&quot;&gt;·&lt;/code&gt;，1 個單位時間（點決定基本單位時間）&lt;/li&gt;
&lt;li&gt;劃（或稱「答」）：長訊號，符號表示為 &lt;code dir=&quot;auto&quot;&gt;-&lt;/code&gt;，時間長度為 3個單位時間&lt;/li&gt;
&lt;li&gt;點劃間隔：在一個字母裡，&lt;code dir=&quot;auto&quot;&gt;·&lt;/code&gt; 與 &lt;code dir=&quot;auto&quot;&gt;·&lt;/code&gt;、&lt;code dir=&quot;auto&quot;&gt;·&lt;/code&gt; 與 &lt;code dir=&quot;auto&quot;&gt;-&lt;/code&gt;、&lt;code dir=&quot;auto&quot;&gt;-&lt;/code&gt; 與 &lt;code dir=&quot;auto&quot;&gt;-&lt;/code&gt; 之間的間隔，為 1 個單位時間&lt;/li&gt;
&lt;li&gt;字符間隔：3 個單位時間&lt;/li&gt;
&lt;li&gt;單詞間隔：7 個單位時間&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;借用一下維基百科的範例，「morse code」寫成摩斯電碼，如下：&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;M   O   R   S  E        C    O   D  E&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;timing-解釋&quot;&gt;Timing 解釋&lt;/h3&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;第三行是實際打訊號的動作，&lt;code dir=&quot;auto&quot;&gt;=&lt;/code&gt; 代表 signal on，&lt;code dir=&quot;auto&quot;&gt;.&lt;/code&gt; 代表 signal off&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;1         2         3         4         5         6         7         8&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789&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;M------   O----------   R------   S----   E       C----------   O----------   D------   E&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;^               ^    ^       ^             ^&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span&gt;|              dah  dit      |             |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;symbol space                letter space    word space&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;訊號傳輸媒介&quot;&gt;訊號傳輸媒介&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;電碼需要傳輸媒介，才能將要表達的訊號傳出去，接收者才能收到訊息並加以解讀，傳統的摩斯電碼是透過電纜傳輸電子訊號，此外還有其他的傳輸媒介：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;有線電路：傳統的摩斯電碼透過叫做「電鍵」（Telegraph Key）的裝置、經由有線電路傳輸電子訊號&lt;/li&gt;
&lt;li&gt;無線電波：無線電波透過無線電波傳送兩種類的訊號&lt;/li&gt;
&lt;li&gt;可見訊號 &lt;sup&gt;&lt;a href=&quot;#user-content-fn-1&quot; id=&quot;user-content-fnref-1&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; 媒介：利用&lt;a href=&quot;https://en.wikipedia.org/wiki/Signal_lamp&quot;&gt;阿爾迪斯燈（Aldis lamp）&lt;/a&gt;、&lt;a href=&quot;https://en.wikipedia.org/wiki/Heliograph&quot;&gt;日光儀（Heliograph）&lt;/a&gt;或是手電筒發送可見光訊號&lt;/li&gt;
&lt;li&gt;可聽訊號 &lt;sup&gt;&lt;a href=&quot;#user-content-fn-2&quot; id=&quot;user-content-fnref-2&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; 媒介：例如用車用喇叭發送聲波訊號&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h2 id=&quot;prosign&quot;&gt;Prosign&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Prosigns_for_Morse_code&quot;&gt;Prosign&lt;/a&gt; 是 Procedural Sign 的縮寫，是摩斯電碼為了簡化及標準化通訊方式的一種方式&lt;/p&gt;
&lt;p&gt;主要會是透過一些大家共識的縮寫來代表個意義，藉此增進通訊效率與準確性&lt;/p&gt;
&lt;p&gt;一個簡單的範例是：用 &lt;code dir=&quot;auto&quot;&gt;K&lt;/code&gt; 代表 “okay, hear you, continue” 的意思&lt;/p&gt;
&lt;p&gt;另一個常見的 prosign 的範例是廣為人知的 SOS，摩斯電碼寫成：&lt;code dir=&quot;auto&quot;&gt;···---···&lt;/code&gt;，因為 prosign 是一個組合代表特定意義（例如這裡是求救訊號），所以字母之間就不會用間隔（&lt;code dir=&quot;auto&quot;&gt;··· --- ···&lt;/code&gt;）&lt;/p&gt;
&lt;p&gt;不是所有的 prosign 都是通用的，不同的 prosign 由不同的機構定義&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;google-的摩斯電碼輸入法&quot;&gt;Google 的摩斯電碼輸入法&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Google 除了有一個學習網頁推廣摩斯電碼之外，還提供了一個摩斯電碼的輸入法供大家使用&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;gboard&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;407&quot; height=&quot;360&quot; src=&quot;https://inorisense.casa/_astro/gboard.4AXA9pVs_9pfW2.webp&quot;&gt;&lt;/p&gt;
&lt;p&gt;只要在手機上安裝 Gboard 輸入法（&lt;a href=&quot;https://apps.apple.com/us/app/gboard-the-google-keyboard/id1091700242&quot;&gt;App Store&lt;/a&gt;、&lt;a href=&quot;https://play.google.com/store/apps/details?id=com.google.android.inputmethod.latin&amp;#x26;hl=en&amp;#x26;gl=US&quot;&gt;Google Play&lt;/a&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;雖然一開始應該會奇慢無比吧🤣&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;後記&quot;&gt;後記&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;其實寫這篇文的當下，我還沒有把所有的摩斯電碼符號學完&lt;/p&gt;
&lt;p&gt;只完成了英文字母及 0～9 的數字，剩下的符號部分則還沒開始&lt;/p&gt;
&lt;p&gt;其實摩斯電碼不是只有英文，歐洲的一些語言也有各自的版本，像是希臘文、俄文&lt;/p&gt;
&lt;p&gt;亞洲的部分，像是阿拉伯文、日文、甚至中文也都有自己的一套摩斯電碼&lt;/p&gt;
&lt;p&gt;感覺滿有趣，之後想來研究一下日文的摩斯電碼&lt;/p&gt;
&lt;p&gt;PS. 據維基百科，日文摩斯電碼稱之為&lt;strong&gt;和文符號&lt;/strong&gt;（&lt;a href=&quot;https://en.wikipedia.org/wiki/Wabun_code#:~:text=Wabun%20code%20(%E5%92%8C%E6%96%87%E3%83%A2%E3%83%BC%E3%83%AB%E3%82%B9%E7%AC%A6%E5%8F%B7,symbol%20represents%20a%20Japanese%20kana.&quot;&gt;Wabun Code&lt;/a&gt;），
日文稱為「&lt;a href=&quot;https://ja.wikipedia.org/wiki/%E3%83%A2%E3%83%BC%E3%83%AB%E3%82%B9%E7%AC%A6%E5%8F%B7#%E5%92%8C%E6%96%87%E3%83%A2%E3%83%BC%E3%83%AB%E3%82%B9%E7%AC%A6%E5%8F%B7&quot;&gt;和文モールス符号&lt;/a&gt;」&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;參考資料&quot;&gt;參考資料&lt;/h2&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Telegraph_key&quot;&gt;Telegraph key - Wikipedia&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Morse_code&quot;&gt;Morse code - Wikipedia&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://morse.withgoogle.com/learn/&quot;&gt;Morse - Learn&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://experiments.withgoogle.com/collection/morse&quot;&gt;Hello Morse - Experiments with Google&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Heliograph&quot;&gt;Heliograph - Wikipedia&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Signal_lamp&quot;&gt;Signal lamp - Wikipedia&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Prosigns_for_Morse_code&quot;&gt;Prosigns for Morse code - Wikipedia&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/NATO_phonetic_alphabet&quot;&gt;NATO phonetic alphabet - Wikipedia&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://science.howstuffworks.com/innovation/inventions/morse-code.htm&quot;&gt;How Morse Code Works and Still Lives On in the Digital Age | HowStuffWorks&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;section data-footnotes=&quot;&quot;&gt;
&lt;ol&gt;
&lt;li id=&quot;user-content-fn-1&quot;&gt;
&lt;p&gt;Visual signal &lt;a href=&quot;#user-content-fnref-1&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 1&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-2&quot;&gt;
&lt;p&gt;Audible &lt;a href=&quot;#user-content-fnref-2&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 2&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</content:encoded><category>雜學</category><category>密碼</category><category>摩斯電碼</category></item><item><title>行動裝置上 100vh 的奇怪行爲</title><link>https://inorisense.casa/zh-tw/blog/strange-100vh-on-mobile-device/</link><guid isPermaLink="true">https://inorisense.casa/zh-tw/blog/strange-100vh-on-mobile-device/</guid><pubDate>Sun, 17 Jul 2022 00:00:00 GMT</pubDate><content:encoded>&lt;div&gt;&lt;h2 id=&quot;前言&quot;&gt;前言&lt;/h2&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;大致分為用 JS 及 CSS 去解決&lt;/p&gt;
&lt;p&gt;CSS 的解決方式比較簡單明瞭&lt;/p&gt;
&lt;p&gt;想要直接看結果，可以走傳送門：&lt;a href=&quot;#js-%E7%9A%84%E8%A7%A3%E6%B1%BA%E6%96%B9%E6%A1%88&quot;&gt;JS 解決方案&lt;/a&gt; 、&lt;a href=&quot;#css-%E7%9A%84%E8%A7%A3%E6%B1%BA%E6%96%B9%E6%A1%88&quot;&gt;CSS 解決方案&lt;/a&gt;&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;滿版的樣式&quot;&gt;滿版的樣式&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;這個頁面是滿版的，所以 container 的部分佔滿整個 &lt;code dir=&quot;auto&quot;&gt;document.body&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;container 裡面，由上而下包含三個區域：header、body、footer&lt;/p&gt;
&lt;p&gt;header 與 footer 分別固定在 container 上緣與下緣，body 撐滿剩下的空間，如果内容超過就會有捲軸滾動&lt;/p&gt;
&lt;p&gt;layout 的示意圖如下：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://inorisense.casa/_astro/layout.NjdIaRfr_ZSTjP6.webp&quot; alt=&quot;layout&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;312&quot; height=&quot;555&quot;&gt;&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;意料之外的-100vh&quot;&gt;意料之外的 100vh&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;外層的 container 寬高分別是 100vw、100vh，在電腦上看起來沒什麼問題，然而在手機上就不是那麼回事：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://inorisense.casa/_astro/100vh-issue-1.C1koGPpo_ZJwhWO.webp&quot; alt=&quot;100vh-issue-1&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;256&quot; height=&quot;555&quot;&gt;&lt;/p&gt;
&lt;p&gt;可以看到 container 高度是 100vh，卻仍然可以 scroll 的現象，照理來說應該要剛好&lt;strong&gt;撐滿&lt;/strong&gt;才是&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;頁面連結：&lt;a href=&quot;https://kazettique.github.io/mobile-test/100vh-issue-1&quot;&gt;100vh-issue-1-page&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;原始碼：&lt;a href=&quot;https://github.com/kazettique/mobile-test/blob/main/100vh-issue-1.html&quot;&gt;100vh-issue-1-code&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;備註：可以在手機上點開連結，看一下真實情況&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;因此，若將 container 裡面的東西加進來，就會造成內外都有 scroll 的情況：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://inorisense.casa/_astro/100vh-issue-2.BR41cBZ7_1JUIQ6.webp&quot; alt=&quot;100vh-issue-2&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;256&quot; height=&quot;555&quot;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;頁面連結：&lt;a href=&quot;https://kazettique.github.io/mobile-test/100vh-issue-2&quot;&gt;100vh-issue-2-page&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;原始碼：&lt;a href=&quot;https://github.com/kazettique/mobile-test/blob/main/100vh-issue-2.html&quot;&gt;100vh-issue-2-code&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;看來 CSS 所抓到的 100vh，跟實際上的 &lt;code dir=&quot;auto&quot;&gt;window&lt;/code&gt; 高度不一樣，&lt;code dir=&quot;auto&quot;&gt;window&lt;/code&gt; 比較小，所以造成了 scroll&lt;/p&gt;
&lt;p&gt;於是，試著把 container 元素的高度與 &lt;code dir=&quot;auto&quot;&gt;window&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;var&lt;/span&gt;&lt;span&gt; containerElement &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;getElementById&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;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; containerHeight &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; containerElement&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;clientHeight&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; heightElement &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;getElementById&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;container-height&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;heightElement&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;innerText &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;container clientHeight: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; containerHeight &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;px&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;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; windowHeightEl &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;getElementById&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;window-height&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;windowHeightEl&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;&quot;&lt;/span&gt;&lt;span&gt;window innerHeight: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; window&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;innerHeight &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;px&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;在電腦瀏覽器上，這兩個數字會是一樣的，在手機上卻不同：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://inorisense.casa/_astro/different-height.D5BKgoXB_16G9H4.webp&quot; alt=&quot;different-height&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1125&quot; height=&quot;454&quot;&gt;&lt;/p&gt;
&lt;p&gt;查了一下網路上的資料，大概有兩種方式可以解&lt;/p&gt;
&lt;p&gt;一個是用 JS，一個是用 CSS&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;js-的解決方案&quot;&gt;JS 的解決方案&lt;/h2&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;監聽-window-的-resize-事件&quot;&gt;監聽 window 的 resize 事件&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;於是我加上一個 &lt;code dir=&quot;auto&quot;&gt;resize&lt;/code&gt; 事件，監聽 &lt;code dir=&quot;auto&quot;&gt;window&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;var&lt;/span&gt;&lt;span&gt; containerElement &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;getElementById&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;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; containerHeight &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; containerElement&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;clientHeight&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; heightElement &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;getElementById&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;container-height&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;heightElement&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;innerText &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;container clientHeight: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; containerHeight &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;px&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;/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;updateWindowHeight&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;var&lt;/span&gt;&lt;span&gt; windowHeightEl &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;getElementById&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;window-height&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;windowHeightEl&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;&quot;&lt;/span&gt;&lt;span&gt;window innerHeight: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; window&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;innerHeight &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;px&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;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;updateWindowHeight&lt;/span&gt;&lt;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;window&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;&quot;&lt;/span&gt;&lt;span&gt;resize&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; updateWindowHeight)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;在 iOS Chrome 上，可以看出兩者的高度並不一致：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://inorisense.casa/_astro/100vh-issue-3-ios-chrome.8UU1IDoq_ZcdeUe.webp&quot; alt=&quot;100vh-issue-3-ios-chrome&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;256&quot; height=&quot;555&quot;&gt;&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;ios-safari-與-android-chrome-的彩蛋&quot;&gt;iOS Safari 與 Android Chrome 的彩蛋&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;iOS 的 Safari，除了 container 與 window 高度不同之外，還發現了另一個現象&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://inorisense.casa/_astro/100vh-issue-3.hlf53vcQ_Z1Heeys.webp&quot; alt=&quot;100vh-issue-3&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;256&quot; height=&quot;555&quot;&gt;&lt;/p&gt;
&lt;p&gt;在最頂的時候往下滑動，&lt;code dir=&quot;auto&quot;&gt;window&lt;/code&gt; 的高度是&lt;strong&gt;動態的&lt;/strong&gt;！&lt;/p&gt;
&lt;img src=&quot;https://inorisense.casa/_astro/mind-blown-shocked.BVCToKct_Z20WoPV.webp&quot; alt=&quot;mind-explosion&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;220&quot; height=&quot;146&quot;&gt;
&lt;p&gt;在 Android Chrome 上，也出現 &lt;code dir=&quot;auto&quot;&gt;window&lt;/code&gt; 變化的狀況，但還是有點差異&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://inorisense.casa/_astro/100vh-issue-3-android-chrome.WRoNIK_E_Z1f7hYt.webp&quot; alt=&quot;100vh-issue-3-android-chrome&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;319&quot; height=&quot;686&quot;&gt;&lt;/p&gt;
&lt;p&gt;原來是彩蛋啊！！🥚&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;頁面連結：&lt;a href=&quot;https://kazettique.github.io/mobile-test/100vh-issue-3&quot;&gt;100vh-issue-3-page&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;原始碼：&lt;a href=&quot;https://github.com/kazettique/mobile-test/blob/main/100vh-issue-3.html&quot;&gt;100vh-issue-3-code&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h3 id=&quot;複寫-container-的高度&quot;&gt;複寫 container 的高度&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;既然 100vh 與 &lt;code dir=&quot;auto&quot;&gt;window&lt;/code&gt; 的高度會不一致，而且在 iOS Safari 與 Android Chrome 上還會不斷改變，
那麼就在監聽 &lt;code dir=&quot;auto&quot;&gt;window&lt;/code&gt; 的 &lt;code dir=&quot;auto&quot;&gt;resize&lt;/code&gt; 事件時，
將 container 元素的高度複寫成當下的 &lt;code dir=&quot;auto&quot;&gt;window&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;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;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;updateWindowHeight&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;var&lt;/span&gt;&lt;span&gt; currentWindowHeight &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; window&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;innerHeight&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;var&lt;/span&gt;&lt;span&gt; windowHeightEl &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;getElementById&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;window-height&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;windowHeightEl&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;&quot;&lt;/span&gt;&lt;span&gt;window innerHeight: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; currentWindowHeight &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;px&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;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; containerElement &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;getElementById&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;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;containerElement&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;height &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; currentWindowHeight &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;px&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;var&lt;/span&gt;&lt;span&gt; containerHeight &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; containerElement&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;clientHeight&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;var&lt;/span&gt;&lt;span&gt; heightElement &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;getElementById&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;container-height&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;heightElement&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;innerText &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;container clientHeight: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; containerHeight &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;px&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;/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;updateWindowHeight&lt;/span&gt;&lt;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;window&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;&quot;&lt;/span&gt;&lt;span&gt;resize&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; updateWindowHeight)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&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;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src=&quot;https://inorisense.casa/_astro/100vh-issue-4.DMnsb8V__Z2oKNkl.webp&quot; alt=&quot;100vh-issue-4&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;256&quot; height=&quot;555&quot;&gt;&lt;/p&gt;
&lt;p&gt;我們可以看到，container 高度與 &lt;code dir=&quot;auto&quot;&gt;window&lt;/code&gt; 高度，始終保持同步，也就避免出現外層 scroll 的情況發生了&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://inorisense.casa/_astro/100vh-issue-rotate.qi_R7M5h_Z1eIQbu.webp&quot; alt=&quot;100vh-issue-rotate&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;256&quot; height=&quot;555&quot;&gt;&lt;/p&gt;
&lt;p&gt;旋轉螢幕也不是問題～&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;頁面連結：&lt;a href=&quot;https://kazettique.github.io/mobile-test/100vh-issue-4&quot;&gt;100vh-issue-4-page&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;原始碼：&lt;a href=&quot;https://github.com/kazettique/mobile-test/blob/main/100vh-issue-4.html&quot;&gt;100vh-issue-4-code&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h3 id=&quot;最終結果&quot;&gt;最終結果&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;既然 container 的高度解決了，再將 container 裡面的 header、body、footer 加進來：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://inorisense.casa/_astro/100vh-issue-final.BgzEDajq_Z2uxO8E.webp&quot; alt=&quot;100vh-issue-final&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;256&quot; height=&quot;555&quot;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;頁面連結：&lt;a href=&quot;https://kazettique.github.io/mobile-test/&quot;&gt;100vh-issue-final-page&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;原始碼：&lt;a href=&quot;https://github.com/kazettique/mobile-test/blob/main/index.html&quot;&gt;100vh-issue-final-code&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;一切看來都非常美好 😎&lt;/p&gt;
&lt;p&gt;至於 &lt;code dir=&quot;auto&quot;&gt;resize&lt;/code&gt; 事件是否要加入 &lt;code dir=&quot;auto&quot;&gt;debounce&lt;/code&gt; 增進效能，那就看個人選擇了&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;css-的解決方案&quot;&gt;CSS 的解決方案&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;CSS 的方法有分幾種：&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;max-height--webkit-fill-available&quot;&gt;max-height: -webkit-fill-available&lt;/h3&gt;&lt;/div&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;container&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;display&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;flex;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;flex-direction&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;column;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;width&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;100&lt;/span&gt;&lt;span&gt;vw;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;height&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;100&lt;/span&gt;&lt;span&gt;vh;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;max-height&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-webkit-fill-available;&lt;/span&gt;&lt;span&gt; // 加上 fill-available&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;background&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;lavender;&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;/figure&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;頁面連結：&lt;a href=&quot;https://kazettique.github.io/mobile-test/100vh-issue-css-fill-available&quot;&gt;100vh-issue-css-fill-available-page&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;原始碼：&lt;a href=&quot;https://github.com/kazettique/mobile-test/blob/main/100vh-issue-css-fill-available.html&quot;&gt;100vh-issue-css-fill-available-code&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;但如同前綴 &lt;code dir=&quot;auto&quot;&gt;-webkit-&lt;/code&gt; 的字面意義，在 Android Chrome 上仍然有問題&lt;/p&gt;
&lt;p&gt;但 Windows 及 macOS 上的 Chrome 卻沒事&lt;/p&gt;






























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;&lt;/th&gt;&lt;th&gt;Chrome&lt;/th&gt;&lt;th&gt;Safari&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;macOS&lt;/td&gt;&lt;td&gt;O&lt;/td&gt;&lt;td&gt;O&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Windows&lt;/td&gt;&lt;td&gt;O&lt;/td&gt;&lt;td&gt;NA&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;iOS&lt;/td&gt;&lt;td&gt;O&lt;/td&gt;&lt;td&gt;O&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Android&lt;/td&gt;&lt;td&gt;X&lt;/td&gt;&lt;td&gt;NA&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;div&gt;&lt;h3 id=&quot;html-body-height-100&quot;&gt;html, body height 100%&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;將 html 與 body 的高度設為 100%，底下的 container 就放心地用 100%&lt;/p&gt;
&lt;p&gt;完全將在 mobile 上有爭議的 vh 單位移除了，是一個簡潔的方式&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;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;body&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;margin&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;padding&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;height&lt;/span&gt;&lt;span&gt;:&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; // 用 100%&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;width&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;100&lt;/span&gt;&lt;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;span&gt;container&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;display&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;flex;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;flex-direction&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;column;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;width&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;100&lt;/span&gt;&lt;span&gt;%;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;height&lt;/span&gt;&lt;span&gt;:&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; // 這裡也用 100%&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;background&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;lavender;&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;/figure&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;頁面連結：&lt;a href=&quot;https://kazettique.github.io/mobile-test/100vh-issue-css-body-100&quot;&gt;100vh-issue-css-body-100-page&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;原始碼：&lt;a href=&quot;https://github.com/kazettique/mobile-test/blob/main/100vh-issue-css-body-100.html&quot;&gt;100vh-issue-css-body-100-code&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;所有平台及裝置的瀏覽器都通過了考驗 💯&lt;/p&gt;






























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;&lt;/th&gt;&lt;th&gt;Chrome&lt;/th&gt;&lt;th&gt;Safari&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;macOS&lt;/td&gt;&lt;td&gt;O&lt;/td&gt;&lt;td&gt;O&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Windows&lt;/td&gt;&lt;td&gt;O&lt;/td&gt;&lt;td&gt;NA&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;iOS&lt;/td&gt;&lt;td&gt;O&lt;/td&gt;&lt;td&gt;O&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Android&lt;/td&gt;&lt;td&gt;O&lt;/td&gt;&lt;td&gt;NA&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;div&gt;&lt;h3 id=&quot;新的-css-單位dvh&quot;&gt;新的 CSS 單位：dvh&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;有一個人分享了利用新 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;container&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;display&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;flex;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;flex-direction&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;column;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;width&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;100&lt;/span&gt;&lt;span&gt;%;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;height&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;100&lt;/span&gt;&lt;span&gt;dvh;&lt;/span&gt;&lt;span&gt; // 用單位 dvh&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;background&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;lavender;&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;/figure&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;頁面連結：&lt;a href=&quot;https://kazettique.github.io/mobile-test/100vh-issue-css-dvh&quot;&gt;100vh-issue-css-dvh-page&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;原始碼：&lt;a href=&quot;https://github.com/kazettique/mobile-test/blob/main/100vh-issue-css-dvh.html&quot;&gt;100vh-issue-css-dvh-code&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;關於 dvh 的示意圖如下：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://inorisense.casa/_astro/google-io-new-units.DrfPzKRx_2p7mM0.webp&quot; alt=&quot;new unit&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1330&quot; height=&quot;1104&quot;&gt;&lt;/p&gt;
&lt;p&gt;PS. 本圖來自 Google IO&lt;/p&gt;
&lt;p&gt;但這個似乎還很新，看來支援度沒有太好&lt;/p&gt;
&lt;p&gt;簡單測試了一下，在 Safari 可以正常運作，但是 Chrome 卻不認識這個單位&lt;/p&gt;






























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;&lt;/th&gt;&lt;th&gt;Chrome&lt;/th&gt;&lt;th&gt;Safari&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;macOS&lt;/td&gt;&lt;td&gt;X&lt;/td&gt;&lt;td&gt;O&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Windows&lt;/td&gt;&lt;td&gt;X&lt;/td&gt;&lt;td&gt;NA&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;iOS&lt;/td&gt;&lt;td&gt;O&lt;/td&gt;&lt;td&gt;O&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Android&lt;/td&gt;&lt;td&gt;X&lt;/td&gt;&lt;td&gt;NA&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;支援度可以參考：&lt;a href=&quot;https://caniuse.com/?search=dvh&quot;&gt;“dvh” | Can I use… Support tables for HTML5, CSS3, etc&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;之後想要來研究一下關於 dvh 這一類給 mobile 專用的單位&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;各平台裝置比較表整理&quot;&gt;各平台裝置比較表整理&lt;/h2&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;100vh-與-window-高度比較表&quot;&gt;100vh 與 window 高度比較表&lt;/h3&gt;&lt;/div&gt;






























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;&lt;/th&gt;&lt;th&gt;Chrome&lt;/th&gt;&lt;th&gt;Safari&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;MacOS&lt;/td&gt;&lt;td&gt;O&lt;/td&gt;&lt;td&gt;O&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Windows&lt;/td&gt;&lt;td&gt;O&lt;/td&gt;&lt;td&gt;NA&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;iOS&lt;/td&gt;&lt;td&gt;X&lt;/td&gt;&lt;td&gt;X&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Android&lt;/td&gt;&lt;td&gt;X&lt;/td&gt;&lt;td&gt;NA&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;ul&gt;
&lt;li&gt;O：代表 100vh 與 window 高度一致&lt;/li&gt;
&lt;li&gt;X：代表 100vh 與 window 高度不同&lt;/li&gt;
&lt;li&gt;NA：此平台無此瀏覽器&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h3 id=&quot;window-動態高度比較表&quot;&gt;window 動態高度比較表&lt;/h3&gt;&lt;/div&gt;




















&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;&lt;/th&gt;&lt;th&gt;Chrome&lt;/th&gt;&lt;th&gt;Safari&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;iOS&lt;/td&gt;&lt;td&gt;X&lt;/td&gt;&lt;td&gt;O&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Android&lt;/td&gt;&lt;td&gt;O&lt;/td&gt;&lt;td&gt;NA&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;ul&gt;
&lt;li&gt;O：代表 window 高度會隨 scroll 變化&lt;/li&gt;
&lt;li&gt;X：代表 window 高度固定&lt;/li&gt;
&lt;li&gt;NA：此平台無此瀏覽器&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h2 id=&quot;測試瀏覽器作業系統與版本&quot;&gt;測試瀏覽器、作業系統與版本&lt;/h2&gt;&lt;/div&gt;























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;&lt;/th&gt;&lt;th&gt;OS&lt;/th&gt;&lt;th&gt;Chrome&lt;/th&gt;&lt;th&gt;Safari&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;iOS&lt;/td&gt;&lt;td&gt;15.15&lt;/td&gt;&lt;td&gt;103.0.5060.63&lt;/td&gt;&lt;td&gt;15.15&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Android&lt;/td&gt;&lt;td&gt;11&lt;/td&gt;&lt;td&gt;103.0.5060.71&lt;/td&gt;&lt;td&gt;NA&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;div&gt;&lt;h2 id=&quot;後記&quot;&gt;後記&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;這次因為使用了滿版 layout 搭配 100vh，而發現了這個&lt;del&gt;水很深&lt;/del&gt;的問題&lt;/p&gt;
&lt;p&gt;iOS Safari 在往下滑動時，&lt;code dir=&quot;auto&quot;&gt;window&lt;/code&gt; 會不斷改變的現象，確實是大開眼界&lt;/p&gt;
&lt;p&gt;總結下來，JS 的解法需要去監聽 &lt;code dir=&quot;auto&quot;&gt;resize&lt;/code&gt; 事件，
此外大概不可避免要搭配 debounce，會需要寫很多的程式碼，
還需要在適當的時機取消訂閱 &lt;code dir=&quot;auto&quot;&gt;resize&lt;/code&gt; 事件，避免記憶體洩漏問題，
簡單來說為了效能及資源，都要額外再做其他事&lt;/p&gt;
&lt;p&gt;CSS 的三種解法都相對簡單，沒有太多的程式碼&lt;/p&gt;
&lt;p&gt;但唯有&lt;a href=&quot;#html-body-height-100&quot;&gt;第二個解法&lt;/a&gt;（html、body 設為 100%）通過了所有瀏覽器的考驗&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;#%E6%96%B0%E7%9A%84-css-%E5%96%AE%E4%BD%8Ddvh&quot;&gt;第三個解法&lt;/a&gt;（新單位 dvh）雖然現在有相容性問題，但未來的可用性令人期待&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;參考資料&quot;&gt;參考資料&lt;/h2&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/37112218/css3-100vh-not-constant-in-mobile-browser&quot;&gt;html - CSS3 100vh not constant in mobile browser - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://dev.to/nirazanbasnet/dont-use-100vh-for-mobile-responsive-3o97&quot;&gt;Don’t use 100vh for mobile responsive - DEV Community&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://css-tricks.com/css-fix-for-100vh-in-mobile-webkit/&quot;&gt;CSS fix for 100vh in mobile WebKit | CSS-Tricks - CSS-Tricks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://allthingssmitty.com/2020/05/11/css-fix-for-100vh-in-mobile-webkit/&quot;&gt;CSS fix for 100vh in mobile WebKit - Matt Smith&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://chanind.github.io/javascript/2019/09/28/avoid-100vh-on-mobile-web.html&quot;&gt;Avoid 100vh On Mobile Web | chanind.github.io&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>前端</category><category>JavaScript</category><category>React</category><category>mobile</category><category>手機</category><category>平板</category><category>Safari</category><category>Chrome</category><category>iOS</category><category>Android</category><category>CSS</category></item></channel></rss>