前言
PWA的好处 我就不提了,介绍类文章已经非常多了。产品需要这个作为流量入口,不考虑离线网络访问,缓存维护等等,只想多一个用户访问链接的前提下,如何快速接入PWA成为了我的需求。
必备前提
- manifest.json 物料配置清单
- service-worker.js 静态swjs,这里可以定义较多的 缓存维护策略,请参考其他文章
以上物料建议放在网站根目录
https 环境
A2HS 介绍
A2HS(Add To Home Screen)从chrome版本68开始,开发人员将必须通过捕获 beforeinstallprompt 手动触发提示。
官方声明如下:
-
- The web app is not already installed
-
- Meets a user engagement heuristic (currently, the user has interacted with the domain for at least 30 seconds)
-
- Meets the Progressive Web App criteria:\
- (a) Includes a web app manifest that includes:
- (i) short_name or name
- (ii) icons mustinclude a 192px and a 512px sized icons
- (iii) start_url
- (iiii) display must be one of: fullscreen, standalone, or minimal-ui
- (b) Served over HTTPS (required for service workers)
- (c) Has registered a service worker with a fetch event handler
业务接入
1. 是否可以启用 serviceWorker
/* Only register a service worker if it's supported */
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/service-worker.js');
}
2. 监听 beforeinstallprompt 事件
把 beforeinstallprompt 的事件处理绑定到 一个window对象上去,建议是全局变量。因为后面会用到,请确保后续操作的作用域内可访问该变量
window.addEventListener('beforeinstallprompt', (event) => {
event.preventDefault();
console.log('👍', 'beforeinstallprompt', event);
// 这个变量后面要用
window.deferredPrompt = event;
// 你用来显示 添加到PWA 的 按钮可以显示出来了
// your code here
});
3. 处理添加到pwa的 您的DOM事件点击事件
主要是为了 调用上面绑定的变量 deferredPrompt.prompt(), 请确保您的事件是通过用户点击事件来触发调用的,否则将不会生效。
// butInstall替换为 你的DOM
const butInstall = document.getElementById('butInstall');
butInstall.addEventListener('click', () => {
console.log('👍', 'butInstall-clicked');
const promptEvent = window.deferredPrompt;
if (!promptEvent) {
// deferred prompt 不可用
return;
}
promptEvent.prompt();
promptEvent.userChoice.then((choiceResult) => {
console.log('👍', 'userChoice', choiceResult);
if (choiceResult.outcome === 'accepted') {
console.log('用户 同意了');
// 成功之后的业务回掉操作
// your code here
} else {
console.log('用户 没同意');
}
// 回收 deferredPrompt 变量, prompt() 只能被调用一次
window.deferredPrompt = null;
});
});
4. 监听处理安装事件
PWA被安装后 可以通过 浏览器的菜单栏 / 当前网站地址栏的快捷链接 / 桌面快捷方式 等方式打开,请监听处理 appinstalled 事件
window.addEventListener('appinstalled', (event) => {
console.log('👍', 'appinstalled', event);
// 回收 deferredPrompt 变量
window.deferredPrompt = null;
});
附注(示例配置如下):
示例 manifest.json 参考如下
icons 配置你有的尺寸即可
{
"short_name": "【替换成您的应用短的名称】",
"name": "【替换成您的应用名称】",
"description": "【替换成您的应用描述】",
"icons": [
{
"src": "https://via.placeholder.com/48",
"sizes": "48x48",
"type": "image/png"
},
{
"src": "https://via.placeholder.com/72",
"sizes": "72x72",
"type": "image/png"
},
{
"src": "https://via.placeholder.com/96",
"sizes": "96x96",
"type": "image/png"
},
{
"src": "https://via.placeholder.com/144",
"sizes": "144x144",
"type": "image/png"
},
{
"src": "https://via.placeholder.com/192",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "https://via.placeholder.com/512",
"sizes": "512x512",
"type": "image/png"
}
],
"start_url": "/",
"background_color": "【替换成您的背景色】",
"display": "standalone",
"scope": "/",
"theme_color": "【替换成您的主题色】"
}
示例 service-worker.js 参考如下
const CACHE_NAME = 'offline';
const OFFLINE_URL = 'offline.html';
self.addEventListener('install', function(event) {
console.log('[ServiceWorker] Install');
event.waitUntil((async () => {
const cache = await caches.open(CACHE_NAME);
// Setting {cache: 'reload'} in the new request will ensure that the response
// isn't fulfilled from the HTTP cache; i.e., it will be from the network.
await cache.add(new Request(OFFLINE_URL, {cache: 'reload'}));
})());
self.skipWaiting();
});
self.addEventListener('activate', (event) => {
console.log('[ServiceWorker] Activate');
event.waitUntil((async () => {
// Enable navigation preload if it's supported.
// See https://developers.google.com/web/updates/2017/02/navigation-preload
if ('navigationPreload' in self.registration) {
await self.registration.navigationPreload.enable();
}
})());
// Tell the active service worker to take control of the page immediately.
self.clients.claim();
});
self.addEventListener('fetch', function(event) {
// console.log('[Service Worker] Fetch', event.request.url);
if (event.request.mode === 'navigate') {
event.respondWith((async () => {
try {
const preloadResponse = await event.preloadResponse;
if (preloadResponse) {
return preloadResponse;
}
const networkResponse = await fetch(event.request);
return networkResponse;
} catch (error) {
console.log('[Service Worker] Fetch failed; returning offline page instead.', error);
const cache = await caches.open(CACHE_NAME);
const cachedResponse = await cache.match(OFFLINE_URL);
return cachedResponse;
}
})());
}
});
更多细节:
请参考: web.dev/learn/pwa/