具代表性的面試主題

前端面試題:如何用 Notification.navigate 設計可靠的推播深連結?

前端中等
Offer.cc 編輯團隊發佈 更新

題幹

請設計一條從推播通知進入訂單詳情的深連結流程,並解釋 navigate 與 notificationclick、action.navigate、權限和跨視窗重用的邊界。

1. 題目與適用場景

電商 Web Push 通知需要把使用者帶到訂單詳情。通知可能在應用程式未開啟、已有應用程式視窗或使用者點擊某個 action 時觸發。請使用 navigate 選項設計深連結,並說明 URL 解析、權限、點擊回退、重複開啟和 Service Worker 生命週期。

2. 面試官考察點

  • 是否知道 NotificationOptions.navigate 是導航 URL,Notification.navigate 只讀回傳解析後的絕對 URL 或空字串。
  • 是否能區分通知主體導航、action 導航和 notificationclick 事件回退。
  • 是否處理權限、HTTPS、同源策略、開放重新導向、重複通知和已有視窗重用。
  • 是否能把業務路由恢復、驗證與冪等放在應用層,而非依賴瀏覽器自動導航完成。

3. 回答前需要釐清的問題

  1. 通知由頁面腳本還是 Service Worker 的 showNotification 建立?
  2. 深連結是否允許外部 URL,訂單詳情需要登入後如何恢復?
  3. 使用者點擊通知主體與 action 時,是否要進入不同頁面或執行不同動作?
  4. 已有同源視窗時,產品希望聚焦並重用,還是建立新視窗?

4. 30 秒回答框架

我會在 HTTPS 下由 Service Worker 建立帶 navigate 的持久通知,並只寫入經 allowlist 驗證的同源 URL。點擊主體或 action 時,瀏覽器可導航到對應 URL;若 action 沒有自己的 navigate,在 notificationclick 中處理業務回退,先聚焦已有視窗,再由客戶端路由恢復訂單。權限被拒絕、URL 無效或驗證失敗都要有明確的應用層兜底。

5. 分步驟深入解答

第一步:建立經驗證的通知 URL

js
const target = new URL(`/orders/${orderId}`, self.location.origin);

await self.registration.showNotification("訂單已出貨", {
  body: "點擊查看物流進度",
  tag: `order-${orderId}`,
  navigate: target.href,
  data: { orderId },
});

navigate 會依通知建立時的 base URL 解析。服務端下發的路徑必須經同源與路由 allowlist 驗證,不能把任意使用者輸入直接當作跳轉地址。

第二步:區分主體與 action 導航

js
await self.registration.showNotification("訂單待確認", {
  body: "請選擇操作",
  navigate: "/orders/123",
  actions: [
    { action: "open", title: "查看訂單", navigate: "/orders/123" },
    { action: "help", title: "聯絡客服" },
  ],
});

點擊 action 時,action 自己的 navigate 優先;沒有該 URL 時才進入 notificationclick 事件邏輯。主體導航與 action 導航都要使用同一套 allowlist 與驗證恢復規則。

第三步:處理點擊回退與已有視窗

js
self.addEventListener("notificationclick", (event) => {
  event.notification.close();
  if (event.action === "help") {
    event.waitUntil(clients.openWindow("/support"));
    return;
  }
  event.waitUntil(clients.matchAll({ type: "window", includeUncontrolled: true })
    .then((windows) => windows[0]?.focus() ?? clients.openWindow("/orders/123")));
});

實際應用應檢查視窗 URL、等待 Service Worker 控制並傳遞可信的 data,不要固定寫死訂單號。瀏覽器實作可以重用或新建頂層視窗,產品邏輯應允許兩種結果。

第四步:權限、協定與安全邊界

通知需要使用者授予權限,持久通知還需要 Service Worker;相關 API 依賴安全上下文。權限不是業務授權,訂單 API 仍必須在開啟頁面後重新驗證。只允許同源或明確的受信任外部來源,避免開放重新導向和釣魚式深連結。

第五步:恢復應用程式狀態與冪等

頁面開啟後讀取路徑和 data.orderId,先顯示載入狀態,再請求訂單並驗證使用者身份。重複點擊同一 tag 的通知應合併或更新;客戶端路由切換、焦點恢復和埋點都要冪等,避免一次點擊建立多個請求或重複扣減狀態。

6. 高品質示範回答

我會讓 Service Worker 只產生 allowlist 內的同源 navigate URL,並給通知設定穩定 tag。主體點擊依瀏覽器導航,action 若有自己的 URL 則優先使用;沒有 URL 的 action 由 notificationclick 聚焦已有視窗或開啟回退頁。頁面開啟後重新驗證、讀取訂單參數並冪等恢復狀態。權限、HTTPS、開放重新導向和外部 URL 都單獨驗證,不能把通知導航當作業務授權。

7. 常見錯誤

  • 把使用者輸入直接放入 navigate → 開放重新導向 → 只允許同源和路由 allowlist。
  • 認為所有點擊都會觸發自訂 click 處理 → 主體可能由瀏覽器直接導航 → 僅對沒有 action URL 的分支依賴 notificationclick
  • 把權限當成訂單授權 → 越權查看資料 → 頁面開啟後重新驗證。
  • 每次點擊都 openWindow → 重複視窗和重複請求 → 先匹配同源視窗並讓恢復邏輯冪等。
  • 忽略 Service Worker 生命週期 → 非同步操作被中斷 → 用 event.waitUntil 管理完成的 Promise。

8. 追問及應對

追問一:Notification.navigate 回傳什麼?

它是只讀字串屬性,回傳通知導航 URL 的絕對序列化值;沒有設定有效 URL 時回傳空字串。

追問二:action 沒有 navigate 會怎樣?

該 action 不會自動導航,點擊可進入 notificationclick 事件,由 Service Worker 執行自訂邏輯。

追問三:為什麼仍要在頁面重新驗證?

通知 URL 和 data 只是導航提示,不代表使用者身份或資源權限。訂單 API 必須依目前工作階段和服務端授權重新檢查。

追問四:如何測試跨視窗行為?

覆蓋無視窗、已有同源視窗、視窗未受 Service Worker 控制、主體點擊、action 點擊、權限拒絕和無效 URL,並斷言最終只產生一次應用程式恢復請求。

公開來源

同類題目