一、传统web 应用

当前web应用在移动时代并没有达到其在桌面设备上流行的程度,下面有张图来对比与原生应用之间的差别。

究其原因,无外乎下面不可避免的几点:

  • 移动设备网络限制-不可忽略的加载时间
  • web应用依赖于浏览器作为入口
  • 体验与原生的差距

假如能解决以上的几点,对web app 来说会有多大的提升可以想象。

二、PWA是什么

PWA 全称Progressive Web Apps(渐进式Web应用程序),旨在使用现有的web技术提供用户更优的使用体验。
基本要求

  • 可靠(Reliable)
    即使在不稳定的网络环境下,也能瞬间加载并展现
  • 快速响应(Fast)
    快速响应,并且有平滑的动画响应用户的操作
  • 粘性(Engaging)
    像设备上的原生应用,具有沉浸式的用户体验,用户可以添加到桌面

PWA 本身强调渐进式,并不要求一次性达到安全、性能和体验上的所有要求,开发者可以通过 PWA Checklist 查看现有的特征。

除以上的基准要求外,还应该包括以下特性:

  • 渐进式 - 适用于所有浏览器,因为它是以渐进式增强作为宗旨开发的
  • 连接无关性 - 能够借助 Service Worker 在离线或者网络较差的情况下正常访问
  • 类似应用 - 由于是在 App Shell 模型基础上开发,因为应具有 Native App 的交互和导航,给用户 Native App 的体验
  • 持续更新 - 始终是最新的,无版本和更新问题
  • 安全 - 通过 HTTPS 协议提供服务,防止窥探和确保内容不被篡改
  • 可索引 - 应用清单文件和 Service Worker 可以让搜索引擎索引到,从而将其识别为『应用』
  • 粘性 - 通过推送离线通知等,可以让用户回流
  • 可安装 - 用户可以添加常用的 webapp 到桌面,免去去应用商店下载的麻烦
  • 可链接 - 通过链接即可分享内容,无需下载安装

看起来有点眼花缭乱,这又是一个新的飞起的轮子吗?这里重申一下,PWA背后不是一种新的技术,而是集合当前多种web技术的一种集合。分别利用各自的功能来完成渐进式的整体需求。下面就沿着前面提出的问题分别了解一下相关技术

三、技术组成

由以下几种技术构成:

  • App Manifest
  • Service Worker
  • Notifications API
  • Push API

其中Service Worker是PWA技术的关键,它们可以让app满足上面的三基准。其他技术则是锦上添花,让app更加的强大。

3.1 service worker背景

离线缓存背景

针对网页的体验,从前到后都做了很多努力,极力去降低响应时间,这里就不表述多样的技术手段。
另一个方向的就是缓存,减少与服务器非必要的交互,不过对于离线的情况下浏览器缓存就无力了,
这样离线缓存的需求就出现了。

离线缓存的历程

web应用在离线缓存发展的过程中也不是一簇而就的,经历了逐渐完善的过程。
初期的解决方案是AppCache
然而,事实证明这是一个失败的尝试,缺陷太多,已经被废弃了。具体可以查看Application Cache is a douchebag
但是方向还是正确的,那就继续孜孜不倦的探索。

workers

持久化先放一边,来谈谈另一个问题
基于浏览器中的 javaScript 单线程的现实逐渐不能满足现代web需求的现状,例如耗时的计算,用户的交互显然会受影响。
为了将这些耗时操作从主线程中解放出来,早期W3C新增了一个Web Worker 的 API,可以脱离主线程单独执行,并且可以与主线程交互。
不过Web Worker是临时性的依赖于创建页面 ,不能满足我们持久化的需求。
冲着这个目标,下面就比较容易解决了,搞个能持久存在的就行了。
在Web Worker的基础上,W3C新增了service worker来满足我们持久化的需求。
其生命周期与页面无关,关联页面未关闭时,它也可以退出,没有关联页面时,它也可以启动
功能

Service Worker虽然满足了离线缓存来,其功能可不仅仅局限于此。 可以提供

  • 丰富的离线体验,
  • 周期的后台同步,
  • 消息推送通知,
  • 拦截和处理网络请求,
  • 管理资源缓存
    这些正好也是PWA的目的,所以说Service Worker是PWA的关键技术。

    前提条件

    Service Worker 出于安全性和其实现原理,在使用的时候有一定的前提条件。

  • 由于 Service Worker 要求 HTTPS 的环境
    当然一般浏览器允许调试 Service Worker 的时候 host 为 localhost 或者 127.0.0.1
  • Service Worker 的缓存机制是依赖 Cache API (略过)
  • 依赖 HTML5 fetch API(略过)
  • 依赖 Promise 实现
    由上可知,不是所有的浏览器都支持的,支持情况大概如下:


iOS 内的所有的浏览器都基于 safari,所以iOS要在11.3以上
IE是放弃支持了,不过Edge好歹支持了。

3.2 Cache

Cache是Service Worker衍生出来的API,配合Service Worker实现对资源请求的缓存。
不过cache并不直接缓存字符串,而是直接缓存资源请求(css、js、html等)。
cache也是key-value形式,一般来说key就是request,value就是response

  • caches.open(cacheName) 打开一个cache
  • caches是global对象,返回一个带有cache返回值的Promise
  • cache.keys() 遍历cache中所有键,得到value的集合
  • cache.match(Request|url) 在cache中匹配传入的request,返回Promise;
  • cache.matchAll只有第一个参数与match不同,需要一个request的数组,当然返回的结果也是response的数组
  • cache.add(Request|url) 并不是单纯的add,因为传入的是request或者url,在cache.add内部会自动去调用fetch取回request的请求结果,然后才是把response存入cache;
  • cache.addAll类似,通常在sw install的时候用cache.addAll把所有需要缓存的文件都请求一遍
  • cache.put(Request, Response) 这个相当于cache.add的第二步,即fetch到response后存入cache
  • cache.delete(Request|url) 删除缓存

3.3 注册Service Worker

注册即声明sw文件的位置,显然应该在主js中引入。大概如下:

//基于promise
function registerServiceWorker(){// 注册service workerreturn navigator.serviceWorker.register('./sw1.js').then(registration => {console.log('注册成功');// 返回return registration;}).catch(err => {console.error('注册失败', err);});
}
window.onload = function () {//是否支持if (!('serviceWorker' in navigator)) {return;}registerServiceWorker()
}

3.4 生命周期

Service worker 有一个独立于web 页面的生命周期。
如果在网站上安装 serice worker ,你需要注册,注册后浏览器会在后台安装 service worker。然后进入下面的不同阶段。
激活之后,service worker 将控制所有的页面,纳入它的范围,不过第一次在页面注册 service worker 时不会控制页面,直到它再次加载。
service worker 生效之后,它会处于下面两种状态之一:

  • service worker 终止来节省内存,
  • 页面发起网络请求后,它将处理请求获取和消息事件。

由上图看知,分为这么几个阶段:

  • Installing
    发生在 Service Worker 注册之后,表示开始安装,触发 install 事件回调指定一些静态资源进行离线缓存
  • Installed
    Service Worker 已经完成了安装,并且等待其他的 Service Worker 线程被关闭。
  • Activating
    在这个状态下没有被其他的 Service Worker 控制的客户端,允许当前的 worker 完成安装
  • Activated
    在这个状态会处理 activate 事件回调 (提供了更新缓存策略的机会)。并可以处理功能性的事件 fetch (请求)、sync (后台同步)、push (推送)
  • Redundant
    被替换,即被销毁

了解声明周期其实是为了我们在不同时间段去监听事件来完成相应操作。对PWA来说主要两个事件。

  • install 事件回调:

event.waitUntil():传入一个 Promise 为参数,等到该 Promise 为 resolve 状态为止。
self.skipWaiting():self 是当前 context 的 global 变量,执行该方法表示强制当前处在 waiting 状态的 Service Worker 进入 activate 状态。

  • activate 回调:

event.waitUntil():传入一个 Promise 为参数,等到该 Promise 为 resolve 状态为止。
self.clients.claim():在 activate 事件回调中执行该方法表示取得页面的控制权, 这样之后打开页面都会使用版本更新的缓存。旧的 Service Worker 脚本不再控制着页面,之后会被停止。

const CURCACHE = 'CURCACHE_test_1'
const RUNTIME = 'runtime';
const CURCACHE_URLS = ['./','/asset/sw.jpg','index.js'
]
self.addEventListener('install',e=>{e.waitUntil(//存储缓存路径对应的资源caches.open(CURCACHE).then(cache=>{cache.addAll(CURCACHE_URLS)}).then(self.skipWaiting()))
})//代理请求,使用缓存,请求发送之前self.addEventListener('fetch', e => {e.respondWith(//缓存是否匹配 caches.match(e.request).then(function(response) {if (response != null) {//命中缓存返回缓存,结束请求return response}//未命中缓存,正常请求return fetch(e.request.url)}))});

更新service worker
service worker 更新步骤如下:

  • 更新 service worker 的文件
    网页打开时服务器会进行对比,保持最新
  • 新的 service worker 启动install
  • 当前页面生效的依然是老的service worker,新的 service worker 会进入 “waiting” 状态。
  • 页面关闭之后,老的 service worker 会被干掉,新的 servicer worker 接管页面
  • 新的 service worker 生效后会触发 activate 事件。
const CURCACHE = 'precache_test_1'
//假设上个版本的key为precache_test_2 反正不等于CURCACHE
self.addEventListener('activate', e => {e.waitUntil(//遍历当前缓存keyscaches.keys().then(cacheNames=>{return Promise.all(cacheNames.map(function(cacheName) {//是否等于当前key,保留自己if (cacheName !== CURCACHE) {return caches.delete(cacheName);}}))}).then(() => self.clients.claim()))
}) 

这样一个简单的service worker离线缓存完成了。控制台可以看到,来源是service worker


关闭网络之后再次访问,可以同样得到上面的结果,并且sw.js请求未能拿到,但是不影响,旧的文件依然在,这里证明了每次都回去对比sw文件以确保更新

到这里,离线缓存就实现了。

四、添加到主屏幕

允许将站点添加至主屏幕,是 PWA 提供的一项重要功能。这样就不用再依赖于浏览器作为平台,符合移动端的用户习惯。

manifest.json

需要 manifest.json 文件去配置应用的图标、名称等基本信息如下:

{//被提示安装应用时出现的文本"name": "PQJ-PWA",//添加至主屏幕后的文本"short_name":"PQJ","description": "测试demo",//添加之后,启动地址"start_url": "/index.html",//图标信息"icons": {"128": "/asset/sw.jpg"},"developer": {"name": "pqj","url": ""},"display": "standalone","background_color": "#287fc5","theme_color": "#fff","permissions": {"desktop-notification": {"description": "Needed for creating system notifications."}}
}  

然后以如下方式在html中引入

<link rel="manifest" href="/mainfest.json" />

这样完成之后,移动端安卓使用chrome(亲测),首次访问时会提示是否允许安装到主屏幕,以应用icon的形式出现。
图片和文字即由配置决定。

五、消息通知

消息通知也是使用service worker的通知功能进行的,允许服务器想用户发生通知,而非用户主动请求才去响应某些行为。
正常的通知逻辑需要服务器来参与实现,这次展示只实现功能。

  • 首先申请通知权限
  • 注册service worker
  • 处理逻辑,发送通知
function getPermission(){return new Promise((resolve, reject) => {//权限获取const permissionPromise = Notification.requestPermission(result => {resolve(result);});}).then(result => {//判断条件if (result === 'granted') {execute();}else {console.log('no permission');}});
} 

发送通知

function execute() {// 允许之后执行registerServiceWorker().then(registration => {// 通知registration.showNotification('Hello World!');});
}  

结束语

参考文档

https://lavas.baidu.com/doc
https://developer.mozilla.org/zh-CN/Apps/Progressive
至此,本文介绍就结束了,更多请参考实例虽然PWA目前来看,面对的限制还很多,但是也可以看出web组织在更好的提升web应用方向上做的努力。正如一直提到的那句话,未来可期。
目前国内百度这方面做的比较成熟,新浪微博已经有了pwa 测试版。

前端之PWA使用总结相关推荐

  1. 渐进式Web应用(PWA)带来的未来前端思考

    渐进式Web应用(PWA)带来的未来前端思考 PWA的前世今生你了解吗?未来前端发展方向,你了解吗?纵观PC桌面端发展过程,从早期的Delphi/VB/VF/VC, 到Node Webkit,比如nw ...

  2. 前端技术猜想-2018

    前言 前端技术种类繁多,在平时工作中根据业务的不同,每个人用到的前端技术也不太一样~我们还是需要对目前流行的技术做一个了解,以备不时之需.本文是对3篇大神在文章的汇总和整理~只是资料的搬运工~感兴趣的 ...

  3. 小技巧 - 收藏集 - 掘金

    十分钟学会 Fiddler - 后端 - 掘金 一.Fiddler介绍 Fiddler是一个http抓包改包工具,fiddle英文中有"欺骗.伪造"之意,与wireshark相比它 ...

  4. 前端每周清单半年盘点之 PWA 篇

    前端每周清单专注前端领域内容,以对外文资料的搜集为主,帮助开发者了解一周前端热点:分为新闻热点.开发教程.工程实践.深度阅读.开源项目.巅峰人生等栏目.欢迎关注[前端之巅]微信公众号(ID:front ...

  5. 2019前端必会黑科技之PWA

    一.背景 从2018年到现在,作为号称下一代web应用模型的PWA,逐渐成为了一个各大前端厂商争先恐后进行涉足,布局的一个新的技术, 其主要的对标物Native app,作为现在最主流的mobile端 ...

  6. PWA将带来新一轮大前端技术洗牌?

    作者 | 彭星 编辑 | 尾尾 一.回顾历史:移动时代之初,Web遭遇两大枷锁 Web 在移动时代遭遇两大枷锁1.Web 在移动时代遭遇两大枷锁 当 Web 自信满满,步入移动时代之时,它还没有做好充 ...

  7. 前端性能和加载体验优化实践(附:PWA、离线包、内存优化、预渲染)

    一.背景:页面为何会卡? 1.1 等待时间长(性能) 项目本身包/第三方脚本比较大. JavaScript 执行阻塞页面加载. 图片体积大且多. 特别是对于首屏资源加载中的白屏时间,用户等待的时间就越 ...

  8. GitChat · 前端 | Webpack 工程的 PWA 实战

    GitChat 作者:Jrain'L 原文:Webpack 工程的 PWA 实战 关注微信公众号:GitChat 技术杂谈 ,一本正经的讲技术 前言 在现代化的前端开发中,webpack已经成为了标配 ...

  9. 前端方案(时间/图片/PWA/微信公众号/图片前端压缩/动画与过渡/新兴方案/屏幕适配)

    时间 时间戳(毫秒数)不分时区,即UTC时间所累积的毫秒数,UI必须获得时间戳或者UTC时间的字符串才能正确显示浏览器本地时间. 方案1.后台数据库存放本地时间,返回时间戳给UI. 后台数据库存放本地 ...

最新文章

  1. leetcode 231. Power of Two
  2. 《微软文档管理解决方案2007》之一:安装部署 - [SharePoint Server]
  3. 重新上课,学习HTML的第一天
  4. java插入数据库字符串拼接_java中PreparedStatement解决需转义字符向数据库中插入时的转义问题 | 学步园...
  5. java定义私有变量_java – 为什么在类中声明变量私有?
  6. android scrollview listview显示不全
  7. 在Windows中使用FileZilla Server创建FTP
  8. 豆瓣已玩烂,来爬点有逼格的 ——IMDB 电影提升你的品位
  9. HTML常用标签超详细整理
  10. 3000计算机组装电脑,电脑组装教程,教您组装电脑配置清单
  11. Oracle安装时先决条件检查失败
  12. python爬取头条付费专栏视频_Python3从零开始爬取今日头条的新闻【五、解析头条视频真实播放地址并自动下载】...
  13. SKYLAKE平台安装win7步骤(U盘)
  14. 不要用战术上的勤奋掩盖战略上的懒惰
  15. Qt(c++)调用python一直报错slot、hypot等
  16. Dremel-大数据上的交互式分析
  17. ORA-00604: error occurred at recursive SQL level 1 ...——oracle服务空间不足
  18. 使用事件把子函数的消息传输到主界面中
  19. 2018年总结2019年计划
  20. 计算机游戏教学法.ppt,计算机游戏教学法第十一章.ppt

热门文章

  1. 为什么学Linux操作系统?
  2. WOS(SCI)爬虫案例
  3. WRF-Chem 编译fire_emis报错
  4. SAP MM部分常用增强-ME21N-MIGO-MB1A
  5. 【Java学习之代码学习】 Prog25_求闰年个数的问题
  6. Java 8 新特性,Optional介绍 | 春松客服
  7. linux命令 清空文件夹,linux清空文件夹命令问题
  8. linux电脑支持5g吗,支持5G的全键盘掌上电脑设备Astro Slide
  9. 蓝牙AOA到达角算法(二)
  10. 微信小程序是什么?如何快速搭建一个微信小程序?