頁面如何分辨自己是已安裝的 PWA 還是一般分頁:display-mode 媒體查詢、iOS 的 navigator.standalone 與各環境的差異。
同一個網頁應用,從主畫面圖示開啟和從瀏覽器分頁開啟,表現往往不一樣:沒有網址列、擁有獨立視窗,有時版面也不同。頁面並不是猜出來的,而是向瀏覽器問了一個問題:我是作為已安裝的應用程式在執行,還是只是一個一般分頁? 本文說明這個偵測如何運作、哪裡會出問題,以及它為什麼也與指紋識別有關。
重點摘要
- 基於標準的偵測方式是
display-mode媒體特性:當頁面以沒有瀏覽器介面的已安裝應用視窗執行時,window.matchMedia('(display-mode: standalone)').matches為true。 - 模式取決於 Web 應用程式資訊清單中的
display成員,但頁面看到的是它實際啟動時的模式,而不是資訊清單所要求的模式。 - iOS Safari 另有一個非標準的布林值
navigator.standalone。這是 Apple WebKit 的屬性:在 iOS 上從主畫面啟動為true,在分頁中為false,其他幾乎所有環境下為undefined。 - 可靠的程式碼會同時使用兩個訊號,並把結果當作「呈現方式」的提示,而不是身分或安全的證明。
- 已安裝的環境比分頁罕見得多;而宣稱是 iOS 卻沒有
navigator.standalone的 User-Agent 在內部自相矛盾,所以偵測工具也會讀取這些訊號。
標準方式:display-mode 媒體查詢
應用程式資訊清單透過 display 成員宣告安裝後的外觀。MDN 的 display-mode 參考列出了頁面可以比對的取值,以下是最核心的四個(另有 window-controls-overlay、picture-in-picture 等較新的模式):
| 取值 | 使用者看到的效果 |
|---|---|
fullscreen | 佔滿整個螢幕,沒有系統介面 |
standalone | 獨立的應用視窗,沒有瀏覽器網址列和分頁 |
minimal-ui | 類應用視窗,只保留少量瀏覽器控制項 |
browser | 一般瀏覽器分頁或視窗 |
同一個媒體特性在 CSS 和 JavaScript 中都能使用:
const isStandalone = window.matchMedia('(display-mode: standalone)').matches;
// react if the user installs or switches while the page is open
window.matchMedia('(display-mode: standalone)')
.addEventListener('change', (e) => console.log('standalone:', e.matches));
@media (display-mode: standalone) {
.install-banner { display: none; }
}
有兩個細節容易踩坑。第一,查詢回報的是目前模式:資訊清單寫著 standalone 的應用,如果透過連結在分頁中開啟,依然會回報 browser。第二,瀏覽器會依規範定義的退回鏈處理,所以在不允許全螢幕的平台上要求 fullscreen,最終可能得到 standalone 或 minimal-ui。要偵測你關心的那個模式,不要直接假定資訊清單裡的值。
iOS 的額外訊號:navigator.standalone
iOS 上的 Safari 早於這個媒體特性,自帶一個旗標:
// true: launched from the Home Screen; false: in a Safari tab;
// undefined: not iOS WebKit
const iosStandalone = navigator.standalone === true;
有用的地方在於區分 false 與 undefined。實作了該屬性、但不是從主畫面啟動的瀏覽器回傳 false,而 Windows 或 Android 上的 Chrome 回傳 undefined。由於所有 iOS 瀏覽器都基於 WebKit,這個屬性來自引擎本身,與瀏覽器 App 的品牌無關。
實用的偵測器會把兩者結合:
const installed =
window.matchMedia('(display-mode: standalone)').matches ||
navigator.standalone === true;
網站為什麼要檢查
- 資料分析。 依「已安裝 / 分頁」拆分工作階段,可以看出安裝後是否真被使用,以及安裝使用者的行為是否不同。
- 安裝提示。 對已經加入主畫面的人,再顯示「加入主畫面」毫無意義。
- 介面差異。 沒有返回按鈕和網址列時,獨立應用需要自己的導覽,頁面可能還要額外的安全區域內距。
- 排查問題。 當頁面記錄了自己執行在哪種環境,「分頁裡正常、變成應用就壞」這類回報會容易定位得多。
獨立應用、WebView 與分頁
讀者常把三種都沒有一般瀏覽器介面的環境混為一談:
| 環境 | display-mode: standalone | 典型特徵 |
|---|---|---|
| 一般分頁 | 否(browser) | 完整的瀏覽器介面 |
| 已安裝的 PWA | 是 | 獨立視窗,從圖示啟動 |
| App 內建瀏覽器(WebView) | 通常否 | wv 標記或特定於應用的 User-Agent 特徵 |
App 內建瀏覽器是渲染在另一個應用(如社群軟體)內部的頁面,是宿主應用而非使用者決定把它顯示在那裡的。已安裝的 PWA 則是使用者明確加入的。只檢查「有沒有網址列」的頁面會把兩者混淆,所以要把媒體查詢和 User-Agent 訊號搭配使用。
指紋識別的角度
這個訊號本身能識別身分嗎?單獨來看幾乎不能:它只有一個位元。但它會從兩個方面補充輪廓。已安裝的環境比分頁罕見,因此會稍微縮小人群範圍,與其他媒體特性訊號的作用類似。同時它是一項一致性檢查:User-Agent 說是 iPhone,而 navigator.standalone 卻是 undefined,或者在桌面引擎上出現「iOS」設定,正是指紋一致性檢查清單所描述的那類矛盾。它是行動裝置指紋版圖中又一塊小拼圖。
關於應用模型本身,MDN 的 PWA 指南介紹了資訊清單、可安裝性,以及瀏覽器如何呈現已安裝的應用。
親自驗證
在一般分頁中開啟 BrowserInsight 的 WebView 檢測,記下結果。然後把網站加入主畫面(或從瀏覽器選單安裝),從圖示開啟再執行一次。「獨立(PWA)模式」訊號讀取的正是上面兩項檢查,你可以看著它翻轉。我們的核心檢測也會把 navigator.standalone 當作 WebKit 訊號之一讀取。
常見問題
display-mode 在桌面端有效嗎?
有效。桌面版 Chromium 系瀏覽器可以把網站安裝成應用視窗,該視窗會符合 (display-mode: standalone)。各瀏覽器的支援和安裝流程不同,請用特性偵測而不要想當然。
頁面能知道應用「能否被安裝」嗎?
透過這些訊號不能。它們只描述頁面目前的執行方式。安裝提示依賴獨立的、因瀏覽器而異的事件,而從未安裝的分頁使用者看起來與其他分頁無異。
還需要 navigator.standalone 嗎?
新版 iOS 也支援該媒體查詢,所以新程式碼可以主要依賴 display-mode。許多網站仍然兩者都檢查,因為只需一行程式碼,還能涵蓋舊版行為。
使用者能偽造嗎?
User-Agent 切換器或自動化工具可以偽造字串,但要偽造得一致,還必須與引擎的真實屬性吻合,而一致性檢查正是針對這一點。請把這個訊號當作呈現方式的提示,而不是安全邊界。
總結
偵測 PWA 是否以獨立模式執行只需兩行:display-mode 媒體查詢,再加上用於 iOS 主畫面應用的 navigator.standalone。頁面由此得知的是自己的啟動方式,這對資料分析、安裝提示和版面有用,對指紋識別則略有參考價值,主要用作一致性檢查。


