学着玩转PWA(渐进式web应用)
渐进式web app,其目的是通过各种web技术实现与原生App相近的用户体验。纵观现有 Web 应用与原生应用的对比差距,如离线缓存、沉浸式体验等等,可以通过已经实现的 Web 技术去弥补这些差距,最终达到与原生应用相近的用户体验效果。
- 严格依赖https,保证了一定的安全性
特性
- 安全可靠
使用 Service Work 技术实现即时下载,当用户打开应用后,页面资源的加载不再完全依赖于网络,而是使用 Service Work 缓存离线包存在本地,确保为用户提供即时可靠的体验。
- 访问更快
首屏可以部署在服务端,节省网页请求时间,加载速度更快,拥有更平滑的动态效果和快速的页面响应。
- 响应式界面
支持各种类型的终端和屏幕。
- 沉浸式体验
在支持 PWA 的浏览器和手机应用上可以直接将 Web 应用添加到用户的主屏幕上,无需从应用商店下载安装。从主屏幕上打开应用之后,提供沉浸式的全屏幕体验。
功能
手机应用配置(Web App Manifest) 可以通过 manifest.json 文件配置,使得可以直接添加到手机的桌面上。
离线加载与缓存(Service Worker+Cache API ) 可以通过 Service Worker + HTTPS +Cache Api + indexedDB 等一系列 Web 技术实现离线加载和缓存。
消息推动与通知(Push&Notification ) 实现实时的消息推送与通知
数据及时更新(Background Sync ) 后台同步,数据及时更新
PWA核心技术介绍
1.service Worker
服务工作线程,类似于web worker,一个独立于js主进程的独立进程。
一个服务器与浏览器之间的中间人角色,如果网站中注册了service worker那么它可以拦截当前网站所有的请求,进行判断,如需要向服务器发起请求就转给服务器,如果可以直接在缓存中拿到数据,那么就直接返回缓存不再转给服务器,从而大大提升浏览器体验。
- 常驻内存运行
- 代理网络请求
- 依赖HTTPS
2. promise
service worker中就存在着大量的promise API
fetch 网络请求
catch API
支持资源的缓存系统,在没有网络的情况下可以使用缓存的资源,让 web页面依然能够运行
- 缓存资源(css/script/image)
- 依赖service Worker代理网络请求
- 支持页面离线运行
Notification API
发出后台推送消息
消息推送
- 依赖用户授权
- 适合在service worker中推送
PWA核心技术学习
service-worker
注册serviceWorker
index.js
<script>
// 注册serviceWorker
navigator.serviceWorker
.register("./sw.js", {
scope: "/", //这个脚本可控制的相对路径,默认是脚本本身所在的路径
})
.then(
(registration) => {
// 成功返回registration
console.log(registration);
},
(err) => {
// 失败返回err
console.error(err);
}
);
</script>
使用
在指定的serviceWorker.js中书写对应的安装及拦截逻辑.
sw.js中有以下几点
- 不可访问dom
- 不可访问window、location等
- service worker编程就是在于service worker的生命周期打交道
- self 代表当前serverworker
- 它设计为完全异步,同步API(如XHR和localStorage)不能在service worker中使用 以下代码都是sw.js中的
1.安装worker
- 监听安装事件,install事件一般用来设置浏览器的离线缓存逻辑
- install方法,会在一个新的serviceworker脚本被安装后触发,
- 注意sw.js只要有一点点不同,浏览器就会认为是以个新的service worker,然后新的版本会被下载安装,并不会立即生效,还是上一版本的生效
- install和activate 第一次安装会被触发,再次刷新页面,则不会被触发,因为已经被安装了。除非脚本发生变化。
self.addEventListener("install", (event) => {
// event.waitUntill(promise)当传入的promise完成之后才是真正的完成,他会推迟后面的activate的触发,waitUntil一般会结合一些特定的行为
// 比如
// self.skipWaiting() 强制停止旧的serviceworker,启动新的serviceworker
// 以下写法可以在install新版本的之后强制停止掉旧的serviceWorker,使用新的,
// 因为新的版本不会立即生效
event.waitUntil(self.skipWaiting());
});
2. activate 激活serviceWorker
这一步是让serviceWorker完成安装,让所有页面收到serviceWorker的控制;或者是清除之前worker留下来的一些相关资源,比如遗留下的无用缓存
self.addEventListener("activate", (event) => {
console.log("activate", event);
// 让所有页面受到serviceWorker的控制
event.waitUntil(slef.client.claim());
//
});
以上两步之后,serviceWorker就完全的可以控制所有的页面了。
3. fetch方法
在 fetch 我们可以拦截到到全站的所有请求。 在这里我们可以通过与缓存对比,然后判断是否发送请求还是返回缓存。
self.addEventListener("fetch", (event) => {
console.log("fetch", event);
});
cache API
有了缓存之后离线也可以看到资源 cache API 不一定要在serviceworker中使用,也可以在页面中写缓存
const Cache_Name = "Cache_v1";
self.addEventListener("install", (event) => {
event.waitUntil(
//
/* 创建一个名叫Cache_V1的缓存版本 */
caches.open("v1").then(function (cache) {
/* 指定要缓存的内容,地址为相对于跟域名的访问路径 */
cache.addAll(["/", "./index.css"]);
})
);
});
self.addEventListener("activate", (event) => {
// 如果一个新版本的serviceWorker 那么缓存可能会发生变化
// 需要清理不必要的缓存,activate这个位置就可以很好的清理缓存
event.waitUntil(
caches.keys().then((cacheNames) => {
return Promise.all(cacheNames).then((cacheName) => {
if (cacheName !== Cache_Name) {
// 注意要return 因为caches.delete也是要返回一个promise的
return caches.delete(cacheName);
}
});
})
);
});
// fetch到一些请求
self.addEventListener("fetch", (event) => {
event.respondWith(
/* 在缓存中匹配对应请求资源直接返回 */
caches.open(Cache_Name).then((cache) => {
return cache.match(event.request).then((response) => {
// response 存在命中缓存,直接返回缓存
if (response) {
return response;
}
// response不存在,则发送请求得到数据,放入缓存
return fetch(event.request).then((response) => {
// response 是流式的需要clone一份出来
cache.push(event.request, response.clone());
return response;
});
});
})
);
});
serviceWorker学习引路:developer.mozilla.org/zh-CN/docs/…
notification API
通知API --用于接口用于向用户配置和显示桌面通知。 他在sw脚本中是禁止的 以下需要在页面中设置通知
let notification = new Notification(title, options)
-
title
-
一定会被显示的通知标题
-
options
可选 -
一个被允许用来设置通知的对象。它包含以下属性:
-
dir
: 文字的方向;它的值可以是auto(自动)
,ltr(从左到右)
, orrtl
(从右到左) -
lang
: 指定通知中所使用的语言。 -
body
: 通知中额外显示的字符串 -
tag
: 赋予通知一个ID,以便在必要的时候对通知进行刷新、替换或移除。 -
icon
: 一个图片的URL,将被用于显示通知的图标。
-
notification的一些属性
1. Notification.permission
只读
获取通知的授权状态
-
denied
(用户拒绝了通知的显示)。 -
granted
(用户允许了通知的显示)。 -
default
(因为不知道用户的选择,所以浏览器的行为与 denied 时相同)。
notification 的一些方法
1. Notification.requestPermission()
用于当前页面向用户申请显示通知的权限。
2. Notification.close()
关闭通知
在sw脚本中想使用怎么办?
- 在页面中授权为允许 notification.requestPermission()
- 在sw脚本文件中通过 self.registration.showNotification() 使用
推荐阅读