PWA的探索与应用
程序员文章站
2022-08-10 10:12:45
本文由云+社区发表 PWA(Progressive Web App)起源背景 传统的Web网页存在以下几个问题: 进入一个页面必须要记住它的url或者加入书签,入口不便捷; 没网络就没响应,不具备离线能力; 不像APP一样能进行消息推送。 Native app: 开发成本高 软件上线需要审核 即使使 ......
本文由云+社区发表
pwa(progressive web app)起源背景
传统的web网页存在以下几个问题:
- 进入一个页面必须要记住它的url或者加入书签,入口不便捷;
- 没网络就没响应,不具备离线能力;
- 不像app一样能进行消息推送。
native app:
- 开发成本高
- 软件上线需要审核
- 即使使用频率不高,想使用一个app必须先下载安装
pwa概念的提出
2016 年google i/o 大会上提出一个 next web generation 的概念。pwa是在传统web应用的基础上,结合manifest和service worker,完善web应用的一些能力,比如:
- 添加至主屏幕,点击主屏幕图标可以实现启动动画以及隐藏地址栏
- 实现离线缓存功能,即使用户手机没有网络,依然可以使用一些离线功能
- 消息推送
pwa技术点
web app manifest
web app manifest 技术实现了将pwa网页应用 添加至桌面的功能,但该项技术目前仍处于实验性阶段,各浏览器支持度不高
image.png
pwa 站点部署的 manifest.json文件满足以下条件时会自动显示横幅:
- short\\_name (主屏幕显示) - name (安装横幅显示) - icons (必须包含一个 mime 类型为 image/png 的图标声明) - start\\_url (应用启动地址) - display (必须为 standalone 或 fullscreen) - 站点注册 service worker。 - 站点支持 https 访问。 - 同一浏览器中站点至少被访问两次,间隔至少为 5 分钟。
service worker
pwa应用的离线体验、定期的后台同步以及推送通知等功能的实现依赖于service worker技术,下图为目前sw技术的支持度。
sw具有以下特征:
- 一个独立的 worker 线程,独立于当前网页进程,有自己独立的 worker context。
- 一旦被 install,就永远存在,除非被手动 unregister
- 用到的时候可以直接唤醒,不用的时候自动睡眠
- 离线内容开发者可控
- 能向客户端推送消息
- 不能直接操作 dom
- 必须在 https 环境下才能工作
- 异步实现,内部大都是通过 promise 实现
service worker生命周期
- installing:这个状态发生在 sw 注册之后开始安装,install 事件回调中执行skipwaiting()方法表示强制当前处在 waiting 状态的 service worker 进入 activate 状态。
- installed:sw已经完成了安装,等待其他的 sw 线程被关闭。
- activating:在这个状态下清除其他的worker 以及关联缓存的旧缓存资源,等待新的 sw线程被激活。在 activate 事件回调中执行self.clients.claim()方法表示取得页面的控制权, 这样之后打开页面都会使用版本更新的缓存。旧的 service worker 脚本不再控制着页面,之后会被停止。
- activated:在这个状态可以处理功能性的事件 fetch (请求)、sync (后台同步)、push (推送)。
- 废弃状态 ( redundant ):这个状态表示一个 service worker 的生命周期结束。
service worker 支持的事件
- install:service worker 安装成功后被触发的事件, 在事件处理函数中可以添加需要缓存的文件
- activate:当 service worker 安装完成后并进入激活状态,会触发 activate 事件。通过监听 activate 事件你可以做一些预处理,如对旧版本的更新、对无用缓存的清理等。
- message:service worker 运行于独立 context 中,无法直接访问当前页面主线程的 dom 等信息,但是通过 postmessage api,可以实现他们之间的消息传递,这样主线程就可以接受 service worker 的指令操作 dom。
- fetch :当浏览器在当前指定的 scope 下发起请求时,会触发 fetch 事件,并得到传有 response 参数的回调函数。fetch 事件特别重要,因为它能够定义你的缓存策略。也就是说,你可以决定何时使用缓存数据,何时使用网络请求来的数据。
- push:push 事件是为推送准备的。通过 push api,当订阅了推送服务后,可以使用推送方式唤醒 service worker 以响应来自系统消息传递服务的消息,即使用户已经关闭了页面。
- sync:sync 事件由 background sync (后台同步)发出。background sync 是 google 配合 sw 推出的 api,用于为 service worker 提供一个可以实现注册和监听同步处理的方法。但它还不在 w3c web api 标准中。在 chrome 中这也只是一个实验性功能,需要访问 chrome://flags/#enable-experimental-web-platform-features ,开启该功能,然后重启生效。sync 事件允许延迟网络任务,直到用户连接上网络,它实现的功能通常被称为后台同步。这对于在离线模式下,确保用户启动的任何有网络依赖的任务,最终都将在网络再次可用时达到其预期目的,是非常有用的。
service worker 的工作原理
service worker是基于注册、安装、激活等步骤
注册
if ('serviceworker' in navigator) { window.addeventlistener('load', function () { navigator.serviceworker.register('/jslearning/sw.js') // 默认作用域为jslearning下,也可以通过设置scope参数进行设置 .then(function (registration) { // 注册成功 console.log('serviceworker registration successful with scope: ', registration.scope); }) .catch(function (err) { // 注册失败:( console.log('serviceworker registration failed: ', err); }); }); }
安装
this.addeventlistener('install', function(event) { console.log('v1 installing…'); //需要缓存的重要的高优先级资源 var vipurlstoprefetch = [ './index.html' ]; //次重要的资源 var urlstoprefetch = [ './icon.png' ]; event.waituntil( caches.open(offline_cache_name).then(function(cache) { //urlstoprefetch非重要资源,即使有资源加载失败也不影响service worker安装 cache.addall(urlstoprefetch); //vipurlstoprefetch中资源全部请求成功,service worker安装事件才顺利完成,可以进入激活事件 return cache.addall(vipurlstoprefetch); }) ); });
激活
//service worker激活事件 this.addeventlistener('activate', function(event) { //在激活事件中清除非当前版本的缓存避免用户存储空间急剧膨胀 event.waituntil(caches.keys().then(function(cachenames) { console.log('v1 activate'); return promise.all(cachenames.map(function(cachename) { if (cachename !== offline_cache_name) { if(cachename.indexof(offline_cache_prefix) != -1) { return caches.delete(cachename); } } })); })); });
service worker更新
- 如果线程的字节与已有的sw线程字节不同,浏览器则考虑更新sw线程。
- 更新的sw线程与现有sw线程一起启动,并获取自己的 install 事件。
- 如果新工作sw线程出现不正常状态代码(例如,404)、解析失败,在执行中引发错误或在安装期间被拒,则系统将舍弃新工作线程,但当前工作线程仍处于活动状态。
- 安装成功后,更新的工作线程将 wait,直到现有工作线程控制0个客户端。
- self.skipwaiting() 可跳过等待情况,这意味着sw线程在安装完后立即激活。
service worker缓存策略
service worker缓存策略大部分在fetch与install时间中定义,对于某些固定不变的静态资源,可以在service worker初次安装的install事件中将其缓存,但资源过大或者网络不佳都会造成资源并未全部下载成功而导致service worker安装被中断安装失败。sw主要有以下几类缓存策略:
- 不影响安装的资源预缓存
- 渐进式缓存
- 仅使用缓存、仅使用网络
- 缓存优先 、网络优先
// 渐进式缓存 var addtocache = function(req) { return fetch(req.clone()).then(function(resp) { var cacheresp = resp.clone(); if (!resp.ok) { return resp; } caches.open(offline_cache_name).then(function(cache) { cache.put(req.clone(), cacheresp); }); return resp; }); }; this.addeventlistener('fetch', function(event) { event.respondwith( caches.open(offline_cache_name).then(function(cache) { return cache.match(event.request); }).then(function(response) { if (response) { return response; } else { return addtocache(event.request); } }) ); });
pwa应用可以通过开发者工具中的application进行查看调试,如下图所示:
pwa优缺点总结
优点
- 可以将app的快捷方式放置到桌面上,全屏运行,与原生app无异
- 能够在网络差和断网条件下
- 推送消息的能力
- 快速响应用户指令
缺点
- 支持率不高
- chrome在安卓移动端上的占有率很低
- 依赖的gcm服务在国内无法使用
- 微信小程序的竞争
pwa应用
- lavas 是一套基于 vue 的 pwa 解决方案,能够帮助开发者快速搭建 pwa 应用
- 新浪微博
- 饿了么
- offline wikipedia
- spotlight
- ...
参考文献
- https://developer.mozilla.org/en-us/docs/web/apps/progressive
- https://segmentfault.com/a/1190000012353473#articleheader0
此文已由作者授权腾讯云+社区在各渠道发布
获取更多新鲜技术干货,可以关注我们