客戶改了數量,後端重算一遍,把完整的購物車丟回來。
購物車的前端有一個很大的誘惑:數量 +1,前端自己把小計乘一乘就好了嘛。
不要。
因為那個小計不是乘一乘就有的。它牽涉到滿額折扣有沒有跨門檻、滿額禮要不要出現、折扣碼的折扣基準要不要跟著變、點數折抵上限要不要重算。你在前端補完這一整套,就等於把促銷邏輯寫了第二份。
然後那兩份會慢慢長得不一樣。畫面顯示 2,840,結帳收 2,900,客服接電話。
購物車的每一個操作,都是一次 POST,後端回傳完整的最新購物車:
const data = await post(urls.itemUpdate, { sku, quantity });
回傳的 payload 裡面有全部:品項、加購、贈品、每一筆折扣、小計、應付金額、點數、滿額進度提示。
前端收到之後,整份重畫。前端一個數字都不算,它只負責顯示。
我在購物車頁的第一行註解就寫了這件事,提醒未來的自己:
// 初始狀態由後端渲染;之後每次操作都以後端回傳的 payload 為準,前端不自行算價。
一次 POST 大概一百毫秒,而客戶在購物車頁按 +1 的頻率,遠遠沒有到需要優化的程度。
真的在意的話,可以做樂觀更新——但要很清楚:你更新的只能是「數量」這種前端確定知道的東西,不能是金額。 金額一律等後端回來。
權限與庫存驗證。 客戶把數量改成 999、或是塞一個不存在的 SKU,後端本來就得擋。既然一定要跟後端來回一趟,前端就沒有理由自己先算一份。
追蹤事件集中。 所有購物車異動都經過同一個 post(),所以 GA4 的 add_to_cart、Meta Pixel 的 AddToCart 我全部寫在那裡面一次處理。不會有「某個按鈕忘了埋追蹤」這種事,因為那個按鈕不可能繞過 post()。
前面那一整個系列在講:金額只有一份計算、每次重算、購物車頁跟結帳頁保證一致。
如果前端自己算價,那些努力全部作廢。 你在後端做到只有一份真相,然後在瀏覽器裡開了第二份。
所以這條規則其實是後端那套設計的延伸:唯一的計價邏輯在伺服器上,瀏覽器只是它的顯示器。