題目與範圍
頁面從伺服器串流輸出商品卡片。每張卡片要在沒有 JavaScript 時顯示有意義內容,元件樣式要隔離,客戶端腳本到達後再接入行為。請使用宣告式 shadow root,並說明不支援該屬性的瀏覽器,以及自訂元素在 HTML 已存在後才升級的情況。
核心能力是瀏覽器渲染、元件邊界與 hydration 正確性,因此歸入 frontend。
面試官考察什麼
第一,是否知道支援時,帶 shadowrootmode="open" 或 "closed" 的 template 會由 HTML 解析器轉換為 shadow root?
第二,能否說明同一 host 只有第一個宣告式 shadow root 會附著,後續實例仍是 template,需要明確降級策略?
第三,能否在伺服器 HTML 與客戶端升級間保持 slot、樣式、可存取性和表單行為?
第四,能否區分 open 與 closed?closed 只隱藏 shadowRoot 引用,不是安全邊界。
第五,能否避免 hydration 或串流傳輸中的重複渲染、重複監聽和事件遺失?
先釐清的問題
- 支援哪些瀏覽器與伺服器渲染回應模式?
- 自訂元素會立即註冊,還是可能延遲升級?
- 哪些子節點使用 slot,是否需要消費者透過
::slotted樣式化? - 測試與整合是否需要 open,還是產品要求 closed?
- 頁面會串流傳送巢狀元件,還是一次傳送完整 root?
- 不支援宣告式解析時採用什麼降級?
30 秒回答框架
「我會由伺服器輸出帶宣告式 shadow root 的元件和語意化 light DOM 降級內容,custom element 升級時重用已解析的 root,不重新生成樹。公開內容使用 slot,樣式放在 root 內;open 或 closed 是整合決策,不是安全控制。透過特性偵測處理不支援的瀏覽器,hydration 保證冪等,事件只繫結一次,測試相容性、串流順序、slot、可存取性和升級時機。」
分步作答
第一步:輸出語意化 root
伺服器輸出 host 與宣告式 template。保持文字與控制項有意義,讓回應在腳本前仍可用。工具或整合需要檢查時選擇 open;只有元件契約需要隱藏引用時才選 closed。
<product-card>
<template shadowrootmode="open">
<style>:host { display: block }</style>
<article><slot name="title"></slot><button>購買</button></article>
</template>
<span slot="title">鍵盤</span>
</product-card>slot 分配屬於公開契約。除非同時控制可存取性語意,不要在 shadow 樹和降級內容中重複標題。
第二步:定義升級契約
custom element 註冊時先偵測是否已有 shadow root,不能再次呼叫 attachShadow。只初始化一次、只繫結一次事件,並保留伺服器節點。若瀏覽器沒有建立 root,再從 template 建立或保留相容的 light DOM 降級。
第三步:特性偵測與降級
用小型解析探針或相關 template 屬性偵測支援情況。不支援的瀏覽器仍應收到語意化 light DOM;客戶端可以稍後升級,但過渡期間不能隱藏內容。
第四步:保持 slot 與樣式
slot 把 light DOM 子節點投影到 shadow 樹。文件要說明具名 slot、預設 slot 與 ::slotted 的樣式邊界。元件樣式放在 root 內,透過自訂屬性或 parts 暴露刻意的外部介面。
第五步:處理串流與巢狀
串流傳輸可能先到 host,再到巢狀 slot 子節點或 custom element 定義。把解析順序當成正常狀態:子節點到達後 slot 應更新,升級發生在串流結束前後都要安全,不能替換 host 產生第二棵樹。
第六步:無重複地接入行為
只繫結一次事件,重複卡片可在 root 層委派。初始化狀態放在私有欄位或 WeakMap,不要放在消費者能覆寫的公開屬性。接入行為時保留表單狀態與焦點。
第七步:測試瀏覽器與可存取性邊界
測試支援與不支援的解析器、open 與 closed、一個與多個宣告式 root、slot 延遲到達、升級先於或晚於解析、鍵盤焦點、標籤、表單提交和網路中斷後的 hydration,斷言只有一棵互動樹且事件不重複。
示例回答
「我會串流輸出語意化 host 和一個宣告式 shadow root,用具名 slot 放置公開內容,樣式留在 root 內。custom element 升級時先檢查已有 root,重用伺服器標記而不是替換。open 便於檢查,closed 只隱藏引用,並非安全邊界。
我會做解析支援偵測並保留 light DOM 降級。串流和延遲升級都是正常狀態,因此 slot 投影和初始化必須冪等。測試涵蓋不支援的瀏覽器、巢狀 root、晚到的 slot、焦點、表單、可存取性和重複事件。」
常見錯誤
- 每次升級都呼叫
attachShadow→ root 或狀態遺失 → 重用已解析 root。 - 把 closed 當安全機制 → 只是隱藏引用 → 按封裝能力說明。
- 重複降級與 shadow 內容 → 讀屏可能播報兩次 → 定義唯一可存取來源。
- 假設 slot 一定先到 → 串流子節點模型錯誤 → 測試晚到投影。
- 用內部選擇器樣式化 slot 內容 → 規則不能穿越邊界 → 使用
::slotted、parts 或自訂屬性。 - 每次渲染都繫結事件 → 點擊觸發多次 → 保證 hydration 冪等。
- 刪除 light DOM 降級 → 不支援瀏覽器出現空白 → 保留語意化伺服器標記。
追問
追問 1:宣告式 shadow root 一定會附著嗎?
不一定。取決於解析器支援和合法 mode。不支援時 template 仍是普通內容,需要降級或客戶端升級。
追問 2:為什麼每個 host 只有一個 root?
解析器只附著第一個宣告式 root,後續 template 留待明確處理,避免競爭 root。
追問 3:何時選擇 open?
測試、整合或受控擴充需要 root 引用時選擇 open;它不授予或撤銷安全權限。
追問 4:串流傳輸中 slot 如何工作?
host 可以先存在,slot 子節點隨後到達,投影會更新;測試兩種到達順序。
追問 5:如何避免 hydration 不匹配?
保持伺服器與客戶端契約穩定,重用現有節點,讓初始化冪等,不生成第二棵樹。
追問 6:如何測試 closed root?
透過公開契約測試可見行為、焦點、事件和可存取性,不直接讀取 shadowRoot。