PWA(Progressive Web App)渐进式Web APP,它并不是单只某一项技术,而是一系列技术综合应用的结果,其中主要包含的相关技术就是Service Worker、Cache Api、Fetch Api、Push API、Notification API 和 postMessage API。使用PWA可以给我们带来什么好处呢?主要体现在如下几方面
1 离线缓存
2 web页面添加桌面快速入口
3 消息推送
相关知识
Service Worker
简单来说,Service Worker 是一个可编程的 Web Worker,它就像一个位于浏览器与网络之间的客户端代理,可以拦截、处理、响应流经的 HTTP 请求。它没有调用 DOM 和其他页面 api 的能力,但他可以拦截网络请求,包括页面切换,静态资源下载,ajax请求所引起的网络请求。Service Worker 是一个独立于JavaScript主线程的浏览器线程。Service Worker有如下特性:
// self.clients.matchAll方法获取当前serviceWorker实例所接管的所有标签页,注意是当前实例 已经接管的
self.clients.matchAll().then(clientList => {
clientList.forEach(client => {
client.postMessage('Hi, I am send from Service worker!');
})
});
Web Storage(例如 LocalStorage 和 SessionStorage)是同步的,不支持网页工作线程,并对大小和类型(仅限字符串)进行限制。 Cookie 具有自身的用途,但它们是同步的,缺少网页工作线程支持,同时对大小进行限制。WebSQL 不具有广泛的浏览器支持,因此不建议使用它。File System API 在 Chrome 以外的任意浏览器上都不受支持。目前正在 File and Directory Entries API 和 File API 规范中改进 File API,但该 API 还不够成熟也未完全标准化,因此无法被广泛采用。
同步的问题 就是负担大,如果有大量请求缓存在本地缓存中,如果是同步,可能负担重
在将相应存在cache中并返回给浏览器报错
resulted in a network error response: a Response whose "body" is locked cannot be used to respond to a request
PWA学习笔记
简单介绍
PWA(Progressive Web App)渐进式Web APP,它并不是单只某一项技术,而是一系列技术综合应用的结果,其中主要包含的相关技术就是Service Worker、Cache Api、Fetch Api、Push API、Notification API 和 postMessage API。使用PWA可以给我们带来什么好处呢?主要体现在如下几方面
1 离线缓存 2 web页面添加桌面快速入口 3 消息推送
相关知识
Service Worker
简单来说,Service Worker 是一个可编程的 Web Worker,它就像一个位于浏览器与网络之间的客户端代理,可以拦截、处理、响应流经的 HTTP 请求。它没有调用 DOM 和其他页面 api 的能力,但他可以拦截网络请求,包括页面切换,静态资源下载,ajax请求所引起的网络请求。Service Worker 是一个独立于JavaScript主线程的浏览器线程。Service Worker有如下特性:
注册Service Work
我们需要在主线程中注册Service Worker,并且一般是在页面触发load事件之后进行注册。当Service Worker注册成功后便会进入其生命周期。scope代表Service Worker控制该路径下的所有请求,如果请求路径不是在该路径之下,则请求不会被拦截。
Service Worker生命周期
Service Worker生命周期大致如下
在Service Worker注册成功之后就会触发install事件,在触发install事件后,我们就可以开始缓存一些静态资。waitUntil方法确保所有代码执行完毕后,Service Worker 才会完成Service Worker的安装。需要注意的是只有CACHE_LIST中的资源全部安装成功后,才会完成安装,否则失败,进入
redundant
状态,所以这里的静态资源最好不要太多。如果 sw.js 文件的内容有改动,当访问网站页面时浏览器获取了新的文件,它会认为有更新,于是会安装新的文件并触发 install 事件。但是此时已经处于激活状态的旧的 Service Worker 还在运行,新的 Service Worker 完成安装后会进入 waiting 状态。直到所有已打开的页面都关闭,旧的 Service Worker 自动停止,新的 Service Worker 才会在接下来打开的页面里生效。为了能够让新的Service Worker及时生效,我们使用skipWaiting
直接使Service Worker跳过等待时期,从而直接进入下一个阶段。在安装成功后,便会触发
activate
事件,在进入这个生命周期后,我们一般会删除掉之前已经过期的版本(因为默认情况下浏览器是不会自动删除过期的版本的),并更新客户端Service Worker(使用当前处于激活状态的Service Worker)。Service Worker 拦截请求
之前说过,Service Worker 是可以拦截请求的,那么一定就会存在一个拦截请求的事件fetch。我们需要在sw.js去监听这个事件。
这里我们在fetch事件中监听请求事件,我们通过cache.match来进行请求的比较,如果存再这个请求的响应我们就直接返回缓存结果,否则就去请求。在这里我们通过cache.add来添加新的缓存,他实际上内部是包含了fetch请求过程的(注意:Cache.put, Cache.add和Cache.addAll只能在GET请求下使用)。在match的时候,需要请求的url和header都一致才是相同的资源,可以设定第二个参数ignoreVary:true。
caches.match(event.request, {ignoreVary: true})
表示只要请求url相同就认为是同一个资源。另外需要提到一点,Fetch 请求默认是不附带 Cookies 等信息的,在请求静态资源上这没有问题,而且节省了网络请求大小。但对于动态页面,则可能会因为请求缺失 Cookies 而存在问题。此时可以给 Fetch 请求设置第二个参数。示例:fetch(fetchRequest, { credentials: 'include' } );
Cache API
Cache API 不仅在Service Worker中可以使用,在主页面中也可以使用。我们通过 caches.open(cacheName)来打开一个缓存空间,在,默认情况下,如果我们不手动去清除这个缓存空间,这个缓存会一直存在,不会过期。在使用Cache API之前,我们都需要通过caches.open先去打开这个缓存空间,然后在使用相应的Cache方法。这里有几个注意点:
在使用cache.add和cache.addAll的时候,是先根据url获取到相应的response,然后再添加到缓存中。过程类似于调用 fetch(), 然后使用 Cache.put() 将response添加到cache中
详细MDN文档
Fetch API
Fetch API不仅可以在主线程中进行使用,也可以在Service Worker中进行使用。fetch 和 XMLHttpRequest有两种方式不同:
当接收到一个代表错误的 HTTP 状态码时,从 fetch()返回的 Promise 不会被标记为 reject, 即使该 HTTP 响应的状态码是 404 或 500。相反,它会将 Promise 状态标记为 resolve (但是会将 resolve 的返回值的 ok 属性设置为 false ),仅当网络故障时或请求被阻止时,才会标记为 reject。
默认情况下,fetch 不会从服务端发送或接收任何 cookies, 如果站点依赖于用户 session,则会导致未经认证的请求(要发送 cookies,必须设置 credentials 选项)
更多信息请查阅:使用 Fetch
Notification
Notification API 用来进行浏览器通知,当用户允许时,浏览器就可以弹出通知。这个API在主页面和Service Worker中都可以使用,MDN文档
我们可以看见当我们在Service Worker中进行消息提示时,用户可能关闭了消息提示的功能,所以我们首先要再次询问用户是否开启消息提示的功能,但是在Service Worker中是不能够直接询问用户的,我们必须要在主页面中去询问,这个时候我们可以通过postMessage去发送一个信号量,根据这个信号量的类型,来做响应的处理(例如:询问消息提示的权限,DOM操作等等)
在这里我们只向打开的标签页发送该信号量,避免重复询问
message 事件
由于Service Worker是一个单独的浏览器线程,与JavaScript主线程互不干扰,但是我们还是可以通过postMessage实现通信,而且可以通过post特定的消息,从而让主线程去进行相应的DOM操作,实现间接操作DOM的方式。
在 ServiceWorker 内部,可以通过监听 message 事件即可获得消息:
在主页面中监听
Client.postMessage
manifest
3 manifest.json 作用 PWA 添加至桌面的功能实现依赖于 manifest.json,也就是说如果要实现添加至主屏幕这个功能,就必须要有这个文件
<link rel="manifest" href="path-to-manifest/manifest.json">
详细配置
MDN详细配置
manifest验证
相关问题
使用workbox,如果使用webpack进行项目打包,我们可以使用workbox-webpack-plugin插件
同步的问题 就是负担大,如果有大量请求缓存在本地缓存中,如果是同步,可能负担重
resulted in a network error response: a Response whose "body" is locked cannot be used to respond to a request
这是因为在使用put的时候,是流的一个pipe操作,流是只能被消费一次的。我们可以clone这个response或者reques参考文章
使用某些webpack插件,例如offline-plugin或者webpack-workbox-plugin
代码示例
pwa-study
pwa-webpack-study
参考资料
pwa系列参考文章
百度Lavas教程
workbox-3博客文档
workbox 官方文档
网站渐进式增强体验(PWA)改造:Service Worker 应用详解
如何优雅的为 PWA 注册 Service Worker
Universal PWA Builder
HTML5 桌面通知:Notification API
Service Worker学习与实践(三)——消息推送
PWA-推送技术
最后(欢迎大家关注我)
DJL箫氏个人博客
博客GitHub地址(欢迎star)
简书
掘金
个人公众号