前言
新版 Shopify Customer Accounts 已經有訂單列表、單筆訂單狀態與個人資料頁。案例要加的是一個共用「服務中心」:在三個頁面提供保固、維修、文件與客服入口,又要讓單筆訂單頁可以帶入當下訂單 context。
上一篇 處理的是 Theme 升級;Customer Account UI Extension 則不再把會員功能塞進 Theme customer templates。這篇先說清楚三個 targets 如何分工,以及目前 source 能證明的只是 UI block,不是完整售後系統。
原本的做法與問題症狀
第一個直覺是寫一個 component,然後假設每個 Customer Account 頁面都能拿到相同資料。這會立刻遇到兩個問題:
- Order Status 對應一筆明確訂單,Order Index 與 Profile 沒有同樣的單筆 order context。
- Block target 不是固定插在某個 DOM selector;merchant 要在 checkout and accounts editor 選擇 placement。
若 component 不知道自己在哪個 target,它可能在 Profile 顯示「這筆訂單」、在 Order Index 誤讀不存在的 order,或把尚未取得的顧客資料當成一定可用。
另一個錯誤方向是把三個頁面的 code 完全複製。短期看起來簡單,之後每次改服務連結、文案、loading state 或權限提示,都要同步三份。
問題是怎麼推敲出來的?
案例的 extension configuration 宣告三個 block targets:
customer-account.order-index.block.render
customer-account.order-status.block.render
customer-account.profile.block.render
每個 target 指向不同 module,再由 module 傳入 order-index、order-status 或 profile context,共用同一個 Service Center component。
Source inspection 顯示,共用 component 只在 order-status context 讀取 shopify.order?.value;其他兩個頁面顯示通用服務入口。呼叫關係也很單純:三個 entry modules 都只 render 同一個 component,component 本身沒有 API query 或 external fetch。
這符合現行 Shopify target model:target 決定 extension 出現在哪裡,也決定可用 target APIs。不能把「同一份 UI」誤解成「三個頁面有完全相同的資料契約」。
最後採用的架構
shopify.extension.toml
├─ OrderIndexBlock.jsx
│ └─ ServiceCenter(context="order-index")
├─ OrderStatusBlock.jsx
│ └─ ServiceCenter(context="order-status")
│ └─ optional shopify.order
└─ ProfileBlock.jsx
└─ ServiceCenter(context="profile")
責任分配是:
| 層級 | 責任 |
|---|---|
| target module | 決定 surface 與 context |
| shared component | 共用版面、連結、空值與狀態 |
| Shopify target API | 提供該 surface 允許的 order/account context |
| checkout and accounts editor | 由 merchant 加入、移動與發布 block |
| backend/Shopify data | 真正的保固、維修、歷史資料或外部服務 |
Block target 可以建議 default placement,但最後 placement 與啟用狀態仍是 merchant configuration,不能只靠 deploy source 完成。
關鍵實作
每個 target 保持薄:
export default async () => {
render(
<ServiceCenter context="order-status" />,
document.body,
);
};
共用 component 只在正確 context 讀 order:
export function ServiceCenter({context}) {
const order = shopify.order?.value;
const isOrderStatus = context === 'order-status';
return (
<s-section>
<s-heading>售後服務中心</s-heading>
<s-text>
{isOrderStatus && order?.name
? `這筆訂單 ${order.name} 可查詢售後資訊`
: '從這裡進入保固、維修與文件服務'}
</s-text>
</s-section>
);
}
公開範例只顯示 placeholder order name。正式實作不應把 order、customer、email 或 phone 寫入 log,也不能因為 UI object 有某個欄位,就忽略 protected customer data access。
若只是連回 Customer Accounts 內建訂單頁,使用 Shopify custom protocol 比 hardcode 帳號網域穩定:
<s-link href="shopify:customer-account/orders">
查看所有訂單
</s-link>
實際驗證
2026-07-30 的 source 與官方文件交叉檢查結果:
- TOML 確實宣告 Order Index、Order Status、Profile 三個 block targets。
- 每個 target 有獨立 module,三者共用同一個 Service Center。
- 只有 Order Status path 讀單筆 order context,而且有 optional guard。
- Service Center 沒有 Storefront API query、Customer Account API request 或 external fetch。
api_access關閉,network_access未啟用。- 現有 compiled bundle 約 23 KB,低於官方 64 KB 單一 UI extension bundle 上限。
- Shopify 最新 2026-07 target reference 仍列出這三個 block targets。
- 官方 plan table 顯示 Customer Account pages 的 UI extensions 可用於 Basic 或以上,不是 Shopify Plus 專屬。
既有 dist artifact 只能證明曾產生 bundle,不能證明當日 source clean build、App Version deploy、merchant placement 或 production rendering。本次沒有連接商店重新驗證。
仍然存在的限制
第一,目前 UI 是示範入口。保固登錄、維修申請與文件下載仍是 links;沒有完成 form、authorization、persistence、status tracking 或 error recovery。
第二,Order Index block 沒有「目前這一筆訂單」。若需求是每筆訂單旁都有 action,應評估 order action menu/modal targets,不能把 Order Index page block 當 row extension。
第三,讀取 customer identity、email、phone 或更完整 order data,取決於 target API 與 protected customer data level。Target 可掛載不等於 data access 已核准。
第四,merchant 可以移動、停用或按 market 設定 extension。程式不能假設 block 一定存在,也不應用它承載沒有 fallback 的法定或關鍵服務通知。
第五,source 使用 2026-04 API 與 Polaris web components;查核日 latest reference 已是 2026-07。2026-04 仍在版本支援窗口,但升版前要重跑 type、bundle、placement 與 visual tests。
最後,component 裡的 storefront relative links 是否存在、是否允許未登入存取、是否有正確 support ownership,都屬於 Store Config/營運責任,不是 extension bundle 能自行保證。
可以延伸到哪些情境?
三個 targets 共用 component 的原則是「共享 presentation,不共享不存在的 context」:
shared shell
+ target-specific adapter
+ explicit data capability
+ merchant-controlled placement
這也適用 loyalty、subscription、invoice、returns 與 product registration。先從讀取能力最小的 block 開始,再為真正需要的 data 與 action 增加 target-specific implementation,比一開始把所有 customer/order 權限都打開更容易審查。
下一篇繼續追問架構邊界:沒有獨立後端的 Customer Account Extension 能做到什麼?
參考資料
- Shopify:Customer account UI extensions
- Shopify:Customer account extension targets
- Shopify:Decide on extension placement
- Shopify:About inline UI extensions
- Shopify:Protected customer data
- Shopify Help Center:Checkout and customer account app availability
以上平台文件查核日期:2026-07-30。
