Progressive Web Application,全称“渐进式网页应用”,让你的网页,可以通过某种方式达到离线使用,可以让网页做到和app功能类似的断网链接,优化用户体验
听起来很神奇,今天做一个简单介绍
使用这项技术,需要2个关键性的文件(manifest.json 和 sw.js)
1. manifest.json
其实就是配置文件,主要介绍文件的图标,文件名称等(谷歌的浏览器插件也是这个套路)
ios 中的 Safari 有一些特殊,单独配置
ios meta/link 私有属性设置
图标icon
<link rel="apple-touch-icon" href="apple-touch-icon-iphone.png"/>
添加到主屏后的标题 和 short_name一致
<meta name="apple-mobile-web-app-title" content="标题">
隐藏safari地址栏 standalone模式下默认隐藏
<meta name="apple-mobile-web-app-capable" content="yes" />
设置状态栏颜色
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
service worker
特点
- 不能访问/操作 dom,毕竟是开了一个新的线程
- 离线缓存内容开发者可控
- 必须在https或者localhost 使用
- 使用 api 都是基于Promise
生命周期
sevice worker 也有和 vue 类似的生命周期
- 安装( installing ):这个状态发生在 Service Worker 注册之后,表示开始安装,触发 install 事件回调指定一些静态资源进行离线缓存。
- 安装后( installed ):Service Worker 已经完成了安装,并且等待其他的 Service Worker 线程被关闭。
- 激活( activating ):在这个状态下没有被其他的 Service Worker 控制的客户端,允许当前的 worker 完成安装,并且清除了其他的 worker 以及关联缓存的旧缓存资源,等待新的 Service Worker 线程被激活。
- 激活后( activated ):在这个状态会处理 activate 事件回调 (提供了更新缓存策略的机会)。并可以处理功能性的事件 fetch (请求)、sync (后台同步)、push (推送)。
- 废弃状态 ( redundant ):这个状态表示一个 Service Worker 的生命周期结束。
serviceWorker中的方法
- self.skipWaiting():表示强制当前处在 waiting 状态的 Service Worker 进入 activate 状态
- event.waitUntil():传入一个 Promise 为参数,等到该 Promise 为 resolve 状态为止。
- self.clients.claim():在 activate 事件回调中执行该方法表示取得页面的控制权, 这样之后打开页面都会使用版本更新的缓存。旧的 Service Worker 脚本不再控制着页面,之后会被停止。
实现浏览器离线缓存的功能
window.addEventListener('load',function(){
// 页面加载完成后 注册serviceWorker
if('serviceWorker' in navigator){
navigator.serviceWorker.register('./sw.js').then(registeration=>{
console.log(registeration.scope);
});
navigator.serviceWorker.addEventListener('controllerchange',()=>{ console.log('change') }) }
if(!navigator.onLine){ window.addEventListener('online',()=>{ console.log('更新'); }) } });
离线缓存(sw.js)
// self 当前线程中的this // 拦截用户发送的所有请求
let CACHE_NAME = `cache_version_` + 81;
let CACHAE_LIST = ["/", "/index.css", "/index.html", "main.js", "/getImage"];
// 独立的线程 可以使用fetch 但是不能使用ajax
function fetchAndSave(req) {
return fetch(req).then(res => {
// res 是流 // 做缓存操作
let r = res.clone();
caches.open(CACHE_NAME).then(cache => cache.put(req, r));
return res;
});
}
self.addEventListener("fetch", e => {
// 用相应来替换 如果获取不到才用缓存
let url = new URL(e.request.url);
if (url.origin !== self.origin) {
return;
}
// express 生成的一个接口
if (e.request.url.includes("/getImage")) {
// 调用了接口 // 如果遇到了接口 更新缓存
e.respondWith(
fetchAndSave(e.request).catch(err => {
// 如果没网 在缓存中 匹配结果 返回请求
return caches.match(e.request);
})
);
return;
}
e.respondWith(
fetch(e.request).catch(err => {
// 如果没网 在缓存中 匹配结果 返回请求
return caches.match(e.request);
})
);
});
// 用缓存替换 // serviceWorker安装的阶段
function preCache() {
return caches.open(CACHE_NAME).then(cache => {
return cache.addAll(CACHAE_LIST);
});
}
self.addEventListener("install", e => {
// 安装的过程中需要缓存
e.waitUntil(preCache().then(skipWaiting));
});
function clearCache() {
return caches.keys().then(keys => {
return Promise.all(
keys.map(key => {
if (key !== CACHE_NAME) {
return caches.delete(key);
}
})
);
});
}
self.addEventListener("activate", e => {
e.waitUntil(
Promise.all([
clearCache(),
self.clients.claim(), // 立即使serviceWorker生效
])
);
});
写在最后:这个pwa 还是兼容性还是很弱,可以点击查看,对于一些需要用户留存度较高的应用的可以试试