Trang web có thể biết nó chạy như PWA đã cài hay trong tab thường: media query display-mode, navigator.standalone trên iOS và mỗi ngữ cảnh để lộ điều gì.
Mở cùng một ứng dụng web từ biểu tượng trên Màn hình chính và từ tab trình duyệt, trang thường hoạt động khác nhau: không có thanh địa chỉ, có cửa sổ riêng, đôi khi bố cục cũng khác. Trang không đoán mò. Nó hỏi trình duyệt một câu: tôi đang chạy như ứng dụng đã cài, hay chỉ là một tab thường? Bài này giải thích cách kiểm tra đó hoạt động, chỗ nào dễ sai, và vì sao nó cũng liên quan đến dấu vân tay trình duyệt.
Tóm tắt nhanh
- Cách kiểm tra theo chuẩn là media feature
display-mode:window.matchMedia('(display-mode: standalone)').matcheslàtruekhi trang chạy trong cửa sổ ứng dụng đã cài, không có giao diện trình duyệt. - Chế độ xuất phát từ trường
displaycủa web app manifest, nhưng trang thấy chế độ mà nó thực sự được khởi chạy, không phải chế độ manifest yêu cầu. - iOS Safari có thêm thuộc tính boolean không chuẩn
navigator.standalone. Đây là thuộc tính của Apple WebKit: trên iOS nó làtruekhi mở từ Màn hình chính,falsetrong tab, vàundefinedở hầu hết nơi khác. - Mã đáng tin cậy kết hợp cả hai tín hiệu và coi kết quả là gợi ý về cách hiển thị, không phải bằng chứng về danh tính hay bảo mật.
- Ngữ cảnh đã cài hiếm hơn tab rất nhiều, và user-agent tự nhận iOS mà thiếu
navigator.standalonelà tự mâu thuẫn, nên các công cụ phát hiện cũng đọc những tín hiệu này.
Cách chuẩn: media query display-mode
Manifest khai báo giao diện của ứng dụng sau khi cài qua trường display. Tài liệu display-mode của MDN liệt kê các giá trị trang có thể so khớp; dưới đây là bốn giá trị cốt lõi (ngoài ra còn các chế độ mới hơn như window-controls-overlay và picture-in-picture):
| Giá trị | Người dùng thấy gì |
|---|---|
fullscreen | Toàn màn hình, không có giao diện hệ thống |
standalone | Cửa sổ ứng dụng riêng, không có thanh địa chỉ hay tab trình duyệt |
minimal-ui | Cửa sổ giống ứng dụng với vài nút điều khiển tối thiểu của trình duyệt |
browser | Tab hoặc cửa sổ trình duyệt thông thường |
Cùng một media feature dùng được trong cả CSS lẫn 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; }
}
Có hai chi tiết dễ vấp. Thứ nhất, truy vấn báo chế độ hiện tại: ứng dụng có manifest ghi standalone nhưng bị mở trong tab qua một liên kết vẫn báo browser. Thứ hai, trình duyệt lùi dần theo chuỗi dự phòng mà đặc tả quy định, nên yêu cầu fullscreen trên nền tảng không cho phép có thể kết thúc ở standalone hoặc minimal-ui. Hãy kiểm tra đúng chế độ bạn quan tâm, đừng giả định theo giá trị trong manifest.
Phần bổ sung của iOS: navigator.standalone
Safari trên iOS có trước media feature này và tự mang một cờ riêng:
// true: launched from the Home Screen; false: in a Safari tab;
// undefined: not iOS WebKit
const iosStandalone = navigator.standalone === true;
Điểm hữu ích là phân biệt false với undefined. Trình duyệt có hỗ trợ thuộc tính này nhưng không mở từ Màn hình chính trả về false, còn Chrome trên Windows hoặc Android trả về undefined. Vì mọi trình duyệt trên iOS đều dùng WebKit, thuộc tính này đến từ engine, không phụ thuộc tên thương hiệu của ứng dụng.
Một bộ phát hiện thực tế kết hợp cả hai:
const installed =
window.matchMedia('(display-mode: standalone)').matches ||
navigator.standalone === true;
Vì sao website kiểm tra
- Phân tích. Tách phiên theo "đã cài / tab" cho thấy bản cài có thực sự được dùng không và người dùng đã cài có hành vi khác không.
- Gợi ý cài đặt. Hiện "Thêm vào Màn hình chính" cho người đã thêm rồi thì vô nghĩa.
- Khác biệt giao diện. Không có nút quay lại và thanh địa chỉ, ứng dụng standalone cần điều hướng riêng, và trang có thể cần thêm đệm vùng an toàn.
- Gỡ lỗi. Báo cáo kiểu "chạy tốt trong tab, hỏng khi là ứng dụng" dễ phân loại hơn nhiều nếu trang ghi lại ngữ cảnh nó chạy.
Standalone, WebView và tab
Người đọc hay nhầm ba ngữ cảnh đều thiếu giao diện trình duyệt thông thường:
| Ngữ cảnh | display-mode: standalone | Dấu hiệu điển hình |
|---|---|---|
| Tab thường | Không (browser) | Đầy đủ giao diện trình duyệt |
| PWA đã cài | Có | Cửa sổ riêng, mở từ biểu tượng |
| Trình duyệt trong ứng dụng (WebView) | Thường không | Token wv hoặc dấu hiệu user-agent riêng của ứng dụng |
Trình duyệt trong ứng dụng là trang được hiển thị bên trong một ứng dụng khác, chẳng hạn mạng xã hội, và chính ứng dụng chủ chứ không phải người dùng quyết định hiển thị nó ở đó. PWA đã cài là thứ người dùng chủ động thêm vào. Trang chỉ kiểm tra "có thanh địa chỉ không?" sẽ nhầm hai loại này, vì vậy media query được dùng cùng các tín hiệu user-agent.
Góc nhìn dấu vân tay
Tín hiệu này có giúp nhận diện người dùng không? Riêng nó thì hầu như không: chỉ là một bit. Nhưng nó bổ sung bức tranh theo hai cách. Ngữ cảnh đã cài hiếm hơn tab nên thu hẹp đám đông đôi chút, giống các tín hiệu media feature khác. Và nó là một phép kiểm tra nhất quán: user-agent nói iPhone mà navigator.standalone là undefined, hoặc hồ sơ "iOS" trên engine máy tính, chính là kiểu mâu thuẫn được mô tả trong danh sách kiểm tra tính nhất quán của dấu vân tay. Đây là một mảnh ghép nhỏ nữa của bề mặt dấu vân tay trên di động.
Về chính mô hình ứng dụng, hướng dẫn Progressive web apps của MDN trình bày về manifest, khả năng cài đặt và cách trình duyệt hiển thị ứng dụng đã cài.
Tự kiểm chứng
Mở kiểm tra WebView của BrowserInsight trong tab thường và ghi lại kết quả. Sau đó thêm trang vào Màn hình chính (hoặc cài từ menu trình duyệt), mở từ biểu tượng và chạy lại. Tín hiệu "Chế độ độc lập (PWA)" đọc đúng hai phép kiểm tra ở trên, nên bạn sẽ thấy nó đổi giá trị. Kiểm tra nhân trình duyệt của chúng tôi cũng đọc navigator.standalone như một tín hiệu WebKit.
Câu hỏi thường gặp
display-mode có hoạt động trên máy tính không?
Có. Trình duyệt Chromium trên máy tính có thể cài một website thành cửa sổ ứng dụng, và cửa sổ đó khớp (display-mode: standalone). Mức hỗ trợ và cách cài khác nhau giữa các trình duyệt, nên hãy kiểm tra tính năng thay vì giả định.
Trang có biết ứng dụng "có thể cài" hay không?
Không qua các tín hiệu này. Chúng chỉ mô tả cách trang đang chạy lúc này. Gợi ý cài đặt dựa trên các sự kiện riêng, tùy trình duyệt, và người dùng tab chưa từng cài trông giống mọi tab khác.
Còn cần navigator.standalone không?
iOS hiện đại cũng hỗ trợ media query, nên mã mới có thể dựa vào display-mode. Nhiều site vẫn kiểm tra cả hai vì chỉ tốn một dòng và bao phủ hành vi cũ.
Người dùng có giả mạo được không?
Công cụ đổi user-agent hoặc tự động hóa có thể giả chuỗi, nhưng một bản giả nhất quán còn phải khớp thuộc tính thật của engine, đúng thứ mà các phép kiểm tra nhất quán tìm kiếm. Hãy coi tín hiệu này là gợi ý về cách hiển thị, không phải ranh giới bảo mật.
Kết luận
Phát hiện PWA ở chế độ standalone chỉ cần hai dòng: media query display-mode, cộng với navigator.standalone cho ứng dụng Màn hình chính trên iOS. Trang biết được cách nó được khởi chạy, hữu ích cho phân tích, gợi ý cài đặt và bố cục, và có chút giá trị với dấu vân tay, chủ yếu như một phép kiểm tra nhất quán.


