LESSON-25 · 零基礎學生教材
完成商品目錄、購物車、checkout 與聯絡表單的離線 mock
完成可離線操作的商品目錄、名稱/商品編號搜尋、購物車、checkout 與聯絡表單;所有需求欄位可輸入或顯示,重新整理後購物車仍存在,Network 沒有外部資料請求。
先理解:這堂課解決什麼問題?
品牌 shell 已完成;本堂只使用本地商品陣列、localStorage 與 mock 回應,完整走過選商品、購物車、填訂單與聯絡流程,嚴禁 fetch、GAS endpoint 或 Sheet 寫入。
本堂要完成:完成可離線操作的商品目錄、名稱/商品編號搜尋、購物車、checkout 與聯絡表單;所有需求欄位可輸入或顯示,重新整理後購物車仍存在,Network 沒有外部資料請求。
學習邊界:先完成「完成商品目錄、購物車、checkout 與聯絡表單的離線 mock」,不提前引入尚未教過的框架或建置工具。
先備知識與操作環境
延續上一章的鐵刻運動電商成果。本章只增加 完成商品目錄、購物車、checkout 與聯絡表單的離線 mock 的責任;先開啟 examples/minimal.html,再依 Starter → Checkpoint → Solution 留下檔案、操作、預期與實際證據。
- 檔案位置:先找到
project/e-commerce/products.html、cart.html、checkout.html、order-success.html、contact.html、js/api.js、js/store.js、js/products.js、js/cart.js、js/checkout.js、js/contact.js,再從本頁的最小實驗開始。 - 瀏覽器:開啟最小實驗與階段頁;每次修改後儲存並重新整理。
- DevTools:依本堂內容使用 Elements、Styles、Computed、Console 或 Network,記下第一個可觀察證據。
- 操作習慣:每次只修改一個檔案或一個責任,先預測、再操作、最後驗收。
先做最小實驗:先看見結果
這不是期末網站,而是一個可以直接看到本堂核心結果的最小實驗。先完成一次操作,再回到檔案和名詞卡對照。
- 入口:開啟
examples/minimal.html。 - 操作:開啟本章最小實驗,按下執行按鈕,再比對畫面、Elements、Console 與 390px;不得把 local mock 當成真實 Sheet 成功。
- 預期:只呈現 L25 在鐵刻運動連續專案新增的責任,且能指出上一章輸入與下一章交付。
- 重設:重新整理
examples/minimal.html;本章最小實驗不送正式資料、不部署。
學生練習流程
依序完成每一張卡;目前只聚焦一個步驟,先操作,再用可觀察證據確認結果。
-
1
最小實驗
先閱讀本頁的問題、檔案地圖、名詞卡與最小例子。 -
2
Step 2
自己回答互動問答 4 題,再展開解析。 -
3
最小實驗 2
開啟最小實驗,實際操作一次並記下證據。 -
4
Starter
把 Starter 複製到自己的工作資料夾,照 3 張微步驟卡逐一完成;每一步都要看到結果再繼續。 -
5
Checkpoint
前往 Checkpoint,只處理 1 個本堂核心缺口;先預測,再用指定證據驗收。 -
6
Solution
前往 Solution,先看逐檔差異與修改原因,再決定要不要把完整內容帶回自己的檔案。 -
7
變體驗收
完成情境變體與獨立挑戰,留下檔案位置、操作、結果與驗收證據,並閱讀下一堂銜接:GAS 契約:下一堂把已驗收的 mock 邊界換成商品 GET、訂單 POST 與聯絡 POST。
檔案地圖:每個位置負責什麼?
先不要急著背檔名;請把檔案位置和責任連起來。你在階段頁會看到同一份檔案的可執行版本。
index.html鐵刻運動電商頁面或發佈檔案。products.html鐵刻運動電商頁面或發佈檔案。cart.html鐵刻運動電商頁面或發佈檔案。checkout.html鐵刻運動電商頁面或發佈檔案。contact.html鐵刻運動電商頁面或發佈檔案。css/style.css黃色與鐵灰共用樣式:包含響應式版面、focus 與可讀狀態。js/api.js原生 JavaScript:負責商品、購物車、表單、local mock 或 GAS 測試契約。js/store.js原生 JavaScript:負責商品、購物車、表單、local mock 或 GAS 測試契約。
先學會這些詞
先從名詞、英文與白話意思開始;每次只展開一張名詞卡,再對照正式定義、範例與驗收證據。專業術語、語法、檔案路徑會使用不同樣式。第一次遇到術語時,先回到這裡,不要靠猜。
離線商品目錄
3 個詞Product mockProduct mock 白話意思:用來理解「Product mock」的實務概念。 展開查看正式定義、範例與驗收證據
- 正式定義
- 本章將 Product mock 定義為可在檔案、畫面或 DevTools 中驗證的技術責任。
- 什麼時候用
- 實作 Product mock 或排查相關問題時。
- 最小範例
- Product mock
- 可觀察證據
- 能在專章範例指出 Product mock 的操作結果。
- 容易混淆
- 只記名稱卻沒有實際操作或驗收證據。
名稱/圖片/售價/特價名稱/圖片/售價/特價 白話意思:用來理解「名稱/圖片/售價/特價」的實務概念。 展開查看正式定義、範例與驗收證據
- 正式定義
- 本章將 名稱/圖片/售價/特價 定義為可在檔案、畫面或 DevTools 中驗證的技術責任。
- 什麼時候用
- 實作 名稱/圖片/售價/特價 或排查相關問題時。
- 最小範例
- 名稱/圖片/售價/特價
- 可觀察證據
- 能在專章範例指出 名稱/圖片/售價/特價 的操作結果。
- 容易混淆
- 只記名稱卻沒有實際操作或驗收證據。
active 與有效售價active 與有效售價 白話意思:用來理解「active 與有效售價」的實務概念。 展開查看正式定義、範例與驗收證據
- 正式定義
- 本章將 active 與有效售價 定義為可在檔案、畫面或 DevTools 中驗證的技術責任。
- 什麼時候用
- 實作 active 與有效售價 或排查相關問題時。
- 最小範例
- active 與有效售價
- 可觀察證據
- 能在專章範例指出 active 與有效售價 的操作結果。
- 容易混淆
- 只記名稱卻沒有實際操作或驗收證據。
購物車與結帳
4 個詞localStorage cartlocalStorage cart 白話意思:用來理解「localStorage cart」的實務概念。 展開查看正式定義、範例與驗收證據
- 正式定義
- 本章將 localStorage cart 定義為可在檔案、畫面或 DevTools 中驗證的技術責任。
- 什麼時候用
- 實作 localStorage cart 或排查相關問題時。
- 最小範例
- localStorage cart
- 可觀察證據
- 能在專章範例指出 localStorage cart 的操作結果。
- 容易混淆
- 只記名稱卻沒有實際操作或驗收證據。
qtyqty 白話意思:用來理解「qty」的實務概念。 展開查看正式定義、範例與驗收證據
- 正式定義
- 本章將 qty 定義為可在檔案、畫面或 DevTools 中驗證的技術責任。
- 什麼時候用
- 實作 qty 或排查相關問題時。
- 最小範例
- qty
- 可觀察證據
- 能在專章範例指出 qty 的操作結果。
- 容易混淆
- 只記名稱卻沒有實際操作或驗收證據。
subtotal/shipping/totalsubtotal/shipping/total 白話意思:用來理解「subtotal/shipping/total」的實務概念。 展開查看正式定義、範例與驗收證據
- 正式定義
- 本章將 subtotal/shipping/total 定義為可在檔案、畫面或 DevTools 中驗證的技術責任。
- 什麼時候用
- 實作 subtotal/shipping/total 或排查相關問題時。
- 最小範例
- subtotal/shipping/total
- 可觀察證據
- 能在專章範例指出 subtotal/shipping/total 的操作結果。
- 容易混淆
- 只記名稱卻沒有實際操作或驗收證據。
buyer/gender/phone/address/emailbuyer/gender/phone/address/email 白話意思:用來理解「buyer/gender/phone/address/email」的實務概念。 展開查看正式定義、範例與驗收證據
- 正式定義
- 本章將 buyer/gender/phone/address/email 定義為可在檔案、畫面或 DevTools 中驗證的技術責任。
- 什麼時候用
- 實作 buyer/gender/phone/address/email 或排查相關問題時。
- 最小範例
- buyer/gender/phone/address/email
- 可觀察證據
- 能在專章範例指出 buyer/gender/phone/address/email 的操作結果。
- 容易混淆
- 只記名稱卻沒有實際操作或驗收證據。
離線聯絡表單
3 個詞姓名/性別/電話/信箱/內容姓名/性別/電話/信箱/內容 白話意思:用來理解「姓名/性別/電話/信箱/內容」的實務概念。 展開查看正式定義、範例與驗收證據
- 正式定義
- 本章將 姓名/性別/電話/信箱/內容 定義為可在檔案、畫面或 DevTools 中驗證的技術責任。
- 什麼時候用
- 實作 姓名/性別/電話/信箱/內容 或排查相關問題時。
- 最小範例
- 姓名/性別/電話/信箱/內容
- 可觀察證據
- 能在專章範例指出 姓名/性別/電話/信箱/內容 的操作結果。
- 容易混淆
- 只記名稱卻沒有實際操作或驗收證據。
FormDataFormData 白話意思:用來理解「FormData」的實務概念。 展開查看正式定義、範例與驗收證據
- 正式定義
- 本章將 FormData 定義為可在檔案、畫面或 DevTools 中驗證的技術責任。
- 什麼時候用
- 實作 FormData 或排查相關問題時。
- 最小範例
- FormData
- 可觀察證據
- 能在專章範例指出 FormData 的操作結果。
- 容易混淆
- 只記名稱卻沒有實際操作或驗收證據。
零 fetch/GAS/Sheet零 fetch/GAS/Sheet
白話意思:用來理解「零 fetch/GAS/Sheet」的實務概念。
展開查看正式定義、範例與驗收證據
- 正式定義
- 本章將 零
fetch/GAS/Sheet 定義為可在檔案、畫面或 DevTools 中驗證的技術責任。 - 什麼時候用
- 實作 零
fetch/GAS/Sheet 或排查相關問題時。 - 最小範例
- 零 fetch/GAS/Sheet
- 可觀察證據
- 能在專章範例指出 零
fetch/GAS/Sheet 的操作結果。 - 容易混淆
- 只記名稱卻沒有實際操作或驗收證據。
最小例子:先看懂,再複製
這段只保留本堂第一個微成果。先預測結果,再逐行讀;完整檔案請開啟下方最小實驗。
const matched = products.filter((product) => product.name.includes(keyword) || product.productId.includes(keyword));Live Lab:逐段讀碼與驗證
每一步只回答五件事:為什麼做、改哪裡、會看到什麼、用什麼證據確認、卡住先查什麼。
-
Step 1
以
api.js的私有六欄本地商品資料渲染 active 商品,並讓搜尋只比對name/productId- 目的
- 先用離線資料證明商品 UI、欄位與搜尋契約,不把網路問題混入本章。
- 修改位置
js/api.js、js/products.js、products.html
以api.js的私有六欄本地商品資料渲染 active 商品,並讓搜尋只比對name/productId- 預期結果
- 只渲染 active 商品;有特價時保留原價與特價語意;輸入名稱或商品編號可篩選,沒有其他搜尋 key。
- 驗收證據
- 商品卡 DOM、四個顯示欄位、名稱/編號搜尋結果、img
src/alt與 Network 零請求。 - 卡住先查
- 先搜尋
fetch/http 與額外搜尋 key,確認為零,再分別輸入名稱及商品編號。
-
Step 2
以
store.js保存購物車,並由cart.js/checkout.js完成增減、有效售價、運費與訂單欄位- 目的
- 先把選購與金額顯示做成可重現本地流程;L27 才由 GAS 權威重算。
- 修改位置
js/store.js、js/cart.js、js/checkout.js、cart.html、checkout.html
以store.js保存購物車,並由cart.js/checkout.js完成增減、有效售價、運費與訂單欄位- 預期結果
- 重新整理仍保留購物車;checkout 有購買人、性別、電話、地址、信箱;顯示商品總額、運費、訂單總額與 mock 狀態。
- 驗收證據
- localStorage 快照、購物車數量、三個金額、FormData key 與 order-success mock 畫面。
- 卡住先查
- 先用一件有特價商品 ×2 手算,再比對 store snapshot 與三個畫面金額。
-
Step 3
建立姓名、性別、電話、信箱、內容欄位與本地
success/errormock,並阻止任何fetch/GAS/Sheet- 目的
- 先驗證聯絡資料 contract 與狀態畫面,下一章才替換 transport。
- 修改位置
contact.html、js/contact.js
建立姓名、性別、電話、信箱、內容欄位與本地success/errormock,並阻止任何fetch/GAS/Sheet- 預期結果
- 五個聯絡欄位都有
label/id/name/required;送出不離頁,可重現idle/loading/success/error,Network 無資料 request。 - 驗收證據
- Elements、FormData 五個 key、
aria-live/data-state、Network 與全專案fetch搜尋。 - 卡住先查
- 先數 FormData key 是否正好涵蓋五個需求欄位,再確認 Network 沒有 request。
-
Step 4
將商品小計由 price 改為 salePrice ?? price
- 目的
- Checkpoint 只修正特價商品的單一計價缺口。
- 修改位置
js/api.js
將商品小計由 price 改為 salePrice ?? price- 預期結果
- FIT-001 ×2 使用 1380 而非 1680,小計與訂單總額同步下降。
- 驗收證據
- 手算結果、購物車列單價、subtotal 與 total。
- 卡住先查
- 固定使用同一筆 FIT-001,比對
api.js的有效售價與 summary 輸入。
-
Step 5
專章驗收:離線商品卡與搜尋契約
- 目的
- 將概念轉成可重現的實作證據。
- 修改位置
examples/minimal.html與對應 stage files
操作 離線商品卡與搜尋契約 並記錄結果。- 預期結果
- 能展示 離線商品卡與搜尋契約 的畫面、DOM、Console 或 Network 證據。
- 驗收證據
- 檔案位置、操作、預期、實際與重新驗證。
- 卡住先查
- 只重做一個最小操作並查看第一個錯誤。
-
Step 6
專章驗收:購物車與有效售價
- 目的
- 將概念轉成可重現的實作證據。
- 修改位置
examples/minimal.html與對應 stage files
操作 購物車與有效售價 並記錄結果。- 預期結果
- 能展示 購物車與有效售價 的畫面、DOM、Console 或 Network 證據。
- 驗收證據
- 檔案位置、操作、預期、實際與重新驗證。
- 卡住先查
- 只重做一個最小操作並查看第一個錯誤。
讀碼順序:先找入口,再找被引用的檔案,最後用畫面、DOM、Console、Network 或資料結果驗證。
互動問答
先回答目前這一題,再展開解析;完成後按下一題,讓每一次回答都回到檔案或瀏覽器完成驗證。
Q1概念理解 「Product mock」在本堂要解決什麼問題? 本題線索:Product mock;回到名詞卡與 能在專章範例指出 Product mock 的操作結果。 展開解析收合解析
白話解析:
用來理解「Product mock」的實務概念。 正式來說,本章將 Product mock 定義為可在檔案、畫面或 DevTools 中驗證的技術責任。
對照位置:
回到名詞卡「Product mock」,再看 能在專章範例指出 Product mock 的操作結果。
預期觀察:
你應該能用自己的話說出:實作 Product mock 或排查相關問題時。
常見錯誤:
不要只回答「它是Product mock」;那是重複名詞,不是說明責任。
立即驗證:
開啟 examples/minimal.html,依序做「開啟本章最小實驗,按下執行按鈕,再比對畫面、Elements、Console 與 390px;不得把 local mock 當成真實 Sheet 成功。」並記下畫面或工具證據。
Q2程式碼閱讀
修改指定規則後,你預測畫面會看到什麼?
本題線索:js/api.js、js/products.js、products.html;要修改:以 api.js 的私有六欄本地商品資料渲染 active 商品,並讓搜尋只比對 name/productId;觀察:商品卡 DOM、四個顯示欄位、名稱/編號搜尋結果、img src/alt 與 Network 零請求。
展開解析收合解析
白話解析:
先看檔案責任,再預測結果。這一步的目的:先用離線資料證明商品 UI、欄位與搜尋契約,不把網路問題混入本章。
對照位置:
對照 Live Lab Step 1,位置是 js/api.js、js/products.js、products.html;範例內容:const matched = products.filter((product) => product.name.includes(keyword) || product.productId.includes(keyword));
預期觀察:
只渲染 active 商品;有特價時保留原價與特價語意;輸入名稱或商品編號可篩選,沒有其他搜尋 key。
常見錯誤:
如果只改了檔案但沒有結果,先不要重寫全部;先搜尋 fetch/http 與額外搜尋 key,確認為零,再分別輸入名稱及商品編號。
立即驗證:
實際操作後檢查 商品卡 DOM、四個顯示欄位、名稱/編號搜尋結果、img src/alt 與 Network 零請求。。
Q3概念比較
「Product mock」和「名稱/圖片/售價/特價」在本堂的責任有什麼不同?
本題線索:比較 Product mock 與 名稱/圖片/售價/特價;操作位置:js/store.js、js/cart.js、js/checkout.js、cart.html、checkout.html
展開解析收合解析
白話解析:
Product mock:用來理解「Product mock」的實務概念。;名稱/圖片/售價/特價:用來理解「名稱/圖片/售價/特價」的實務概念。
對照位置:
對照兩張名詞卡的正式定義:本章將 Product mock 定義為可在檔案、畫面或 DevTools 中驗證的技術責任。/本章將 名稱/圖片/售價/特價 定義為可在檔案、畫面或 DevTools 中驗證的技術責任。
預期觀察:
你應該能指出兩者分別出現在哪個檔案或工具,以及哪一個結果會改變。
常見錯誤:
不要把「改變畫面」當成所有技術的責任;先說清楚誰負責結構、呈現、行為或驗收。
立即驗證:
在 js/store.js、js/cart.js、js/checkout.js、cart.html、checkout.html 做「以 store.js 保存購物車,並由 cart.js/checkout.js 完成增減、有效售價、運費與訂單欄位」,再用 localStorage 快照、購物車數量、三個金額、FormData key 與 order-success mock 畫面。 比較前後差異。
Q4除錯驗收
如果完成操作後結果不對,你會先從哪一個證據開始查?
本題線索:examples/minimal.html 與對應 stage files;第一個檢查位置:檔案位置、操作、預期、實際與重新驗證。
展開解析收合解析
白話解析:
先描述症狀,再提出一個最小假設。本堂常見錯誤是:只口頭說明完成。
對照位置:
對照最後一段 Live Lab:examples/minimal.html 與對應 stage files;檢查方式:只重做一個最小操作並查看第一個錯誤。
預期觀察:
你要能指出一個具體位置,而不是一次修改很多檔案。
常見錯誤:
不要先清快取、重裝工具或複製 Solution;先查看第一個可觀察錯誤。
立即驗證:
重新操作並留下「症狀、證據、假設、單一修正、結果」五項紀錄。
常見誤解
- 看到畫面沒有變化,不代表程式沒執行;先確認檔案、路徑、元素與狀態證據。
- 能複製 Solution 不代表理解;請先說出修改哪個檔案、預期哪個結果、用什麼證據確認。
- 本堂只使用原生 HTML、CSS、JavaScript 與本機離線狀態;先完成四頁、固定內容、圖片語意、聯絡欄位、窄版與鍵盤驗收,外部整合留待後續章節。
本堂任務與驗收
完成可離線操作的商品目錄、名稱/商品編號搜尋、購物車、checkout 與聯絡表單;所有需求欄位可輸入或顯示,重新整理後購物車仍存在,Network 沒有外部資料請求。
- 能用 商品四個需求欄位 留下可重現證據。
- 能用 只依
name/productId搜尋 留下可重現證據。 - 能用 購物車增減 留下可重現證據。
- 能用 完整訂單與聯絡欄位 留下可重現證據。
- 能用 localStorage 留下可重現證據。
- 能用 離線狀態 留下可重現證據。
- 能用 零
fetch/GAS/Sheet留下可重現證據。 - Starter、Checkpoint、Solution 的入口都能開啟並知道三者差異。
- 遇到問題時能先寫出症狀,再檢查一個最小假設。
官方延伸閱讀
正式延伸|自主學習
把「商品、購物車、結帳與聯絡的離線 Mock」拆成 4 個可獨立完成、可重設、可驗證的自主學習 module。
開啟自主學習入口|4 個獨立 topic
- 先選一個 topic,讀懂成果、檔案與定位。
- 在自己的 Starter 檔案修改一小段。
- 重新載入,用畫面、Elements、Computed、Console 或文件留下證據。
建議第一個 topic:離線商品卡與搜尋契約。
Learning map
- 離線商品卡與搜尋契約|code-edit|files/starter/js/api.js|count=4、uniqueCount=4、valid=true;搜尋鍵僅 name/productId
- 購物車與有效售價|code-edit|files/starter/js/store.js|count=3、uniqueCount=3、valid=true
- Checkout 訂單欄位與預估金額|code-edit|files/starter/js/checkout.js|count=9、uniqueCount=9、valid=true
- 聯絡五欄與零網路請求|code-edit|files/starter/contact.html|operation=encode-form、inputCount=5、remoteRequests=0