Progressive Web App, 简称 PWA,是提高Web App
的体验的一种新方法,能给用户原生应用的体验,致力于用前沿的技术开发,让网页使用如同原生App般的体验的一系列方案。javascript
用来自Google Developers的解答Progressive Web Apps
:css
Service Worker
可以在离线或者网络较差的状况下正常访问App Shell
模型基础上开发,由于应具备 Native App
的交互和导航,给用户 Native App
的体验HTTPS
协议提供服务,防止窥探和确保内容不被篡改Service Worker
可让搜索引擎索引到,从而将其识别为『应用』咱们也能够经过一个DEMO看看实际效果=>天气 PWAhtml
而其中,PWA方案的最主要核心功能都是依赖于Service Worker
这个API来实现的.前端
W3C 组织早在 2014 年 5 月就提出过 Service Worker
这样的一个 HTML5 API ,主要用来作持久的离线缓存。html5
固然这个 API 不是凭空而来,至于其中的由来咱们能够简单的捋一捋:java
浏览器中的javaScript
都是运行在一个单一主线程上的,在同一时间内只能作一件事情。随着 Web 业务不断复杂,咱们逐渐在 js 中加了不少耗资源、耗时间的复杂运算过程,这些过程致使的性能问题在 WebApp 的复杂化过程当中更加凸显出来。webpack
W3C 组织早早的洞察到了这些问题可能会形成的影响,这个时候有个叫Web Worker
的 API 被造出来了,这个 API 的惟一目的就是解放主线程,Web Worker
是脱离在主线程以外的,将一些复杂的耗时的活交给它干,完成后经过 postMessage
方法告诉主线程,而主线程经过 onMessage
方法获得 Web Worker
的结果反馈。git
一切问题好像是解决了,但 Web Worker 是临时(即浏览器关闭后就关闭了)的,咱们能不能有一个东东是一直持久存在的,而且随时准备接受主线程的命令呢?基于这样的需求推出了最第一版本的 Service Worker
,Service Worker
在 Web Worker
的基础上加上了持久离线缓存能力.github
Service Worker
有如下功能和特性:web
因此咱们基本上知道了 Service Worker
的伟大使命,就是让缓存作到优雅和极致,让 Web App 相对于 Native App 的缺点更加弱化,也为开发者提供了对性能和体验的无限遐想。
Service Worker
的技术核心是Service Worker
脚本,它 是一种由Javascript
编写的浏览器端代理脚本。
前端页面向内核发起注册时会将脚本地址通知内核,内核会启动独立进/线程加载Service Worker
脚本并执行Service Worker
安装及激活动做。成功激活后便进入空闲等待状态,若当前的Service Worker
进/线程一直没有管辖的页面或者事件消息时会自动终止(具体的终止策略视不一样浏览器及版本而定,不会影响前端编写逻辑,但前端勿在Service Worker
脚本中保存须要持久化的信息,能够借助localstorage
),当打开新的可管辖页面或者已管辖页面发起message
等消息时,Service Worker
进/线程会被从新唤起。
每当已安装的Service Worker
有管辖页面被打开时,便会触发Service Worker
脚本更新,当Service Worker
脚本发生了更改,便会忽略本地网络cache
的Service Worker
脚本直接从网络拉取。若网络拉取的与本地有一个字节的差别都会触发Service Worker
脚本的更新,更新流程与安装相似,只是在更新安装成功后不会当即进入active
状态,须要等待旧版本的Service Worker
进/线程终止。
// 在html里注册service-worker
if (navigator.serviceWorker != null) {
navigator.serviceWorker.register('sw.js')
.then(function(registration) {
console.log('Registered events at scope: ', registration.scope);
});
}
复制代码
// 首先定义须要缓存的路径, 以及须要缓存的静态文件的列表。
var cacheStorageKey = 'minimal-pwa-8'
var cacheList = [
'/',
"index.html",
"main.css",
"e.png",
"*.png"
]
// 借助 Service Worker, 能够在注册完成安装 Service Worker 时, 抓取资源写入缓存:
window.addEventListener('install', function(e) {
console.log('Cache event!')
e.waitUntil(
caches.open(cacheStorageKey).then(function(cache) {
console.log('Adding to Cache:', cacheList)
return cache.addAll(cacheList)
}).then(function() {
console.log('Skip waiting!')
return self.skipWaiting()
})
)
})
// 网页抓取资源的过程当中, 在 Service Worker 能够捕获到 fetch 事件, 能够编写代码决定如何响应资源的请求:
window.addEventListener('fetch', function(e) {
// console.log('Fetch event:', e.request.url)
e.respondWith(
caches.match(e.request).then(function(response) {
if (response != null) {
console.log('Using cache for:', e.request.url)
return response
}
console.log('Fallback to fetch:', e.request.url)
return fetch(e.request.url)
})
)
})
复制代码
install 事件:当前Service Worker
脚本被安装时,会触发 install 事件。
push事件: push 事件是为推送通知而准备的。不过首先你须要了解一下 Notification API
和 PUSH API
。
经过 PUSH API
,当订阅了推送服务后,可使用推送方式唤醒 Service Worker
以响应来自系统消息传递服务的消息,即便用户已经关闭了页面。
online/offline事件: 当网络状态发生变化时,会触发 online
或 offline
事件。结合这两个事件,能够与 Service Worker
结合实现更好的离线使用体验,例如当网络发生改变时,替换/隐藏须要在线状态才能使用的连接导航等。
fetch 事件: 当咱们安装完Service Worker
成功并进入激活状态后即运行于浏览器后台,咱们的这个线程就会一直监控咱们的页面应用,若是出现HTTP
请求,那么就会触发fetch
事件,而且给出本身的响应。 这个功能是十分强大的,借助 Fetch API
和 Cache API
能够编写出复杂的策略用来区分不一样类型或者页面的资源的处理方式。它可以提供更加好的用户体验: 例如能够实现缓存优先、降级处理的策略逻辑:监控全部 http 请求,当请求资源已经在缓存里了,直接返回缓存里的内容;不然使用 fetch API 继续请求,若是是 图片或 css、js 资源,请求成功后将他们加入缓存中;若是是离线状态或请求出错,则降级返回预缓存的离线内容。
看到这里不少人会有疑问了,既然能够经过service-worker
缓存资源,那若是一个正式项目,在项目迭代后,并将代码推送到正式环境后,前端怎么实时知道并从新缓存新的资源呢?
第一种方式,就是每次修改都手动去更改sw文件的版本号,触发更新。
第二种就是使用webpack
插件自动化处理
事实上,在咱们真实的用webpack
生成的项目中,若是按照第一种方式手动去写Service-worker.js
文件的话,会遇到两个问题:
webpack
生成的资源多会生成一串hash,Service-worker.js
的资源列表里面须要同步更新这些带hash的资源;service-worker
文件版本号来通知客户端对所缓存的资源进行更新.看到这里就该让用webpack
插件:offline-plugin 登场了,官方同时也推荐sw-precache-webpack-plugin ,offline-plugin不只可以解决刚刚那个提到的缓存更新的问题,同时还具有如下的优势:
manifest
文件。部署到项目中也十分的简单
npm install offline-plugin [--save-dev]
复制代码
第一步,进入webpack.config.js
:
// webpack.config.js example
var OfflinePlugin = require('offline-plugin');
module.exports = {
// ...
plugins: [
// ... other plugins
// it's always better if OfflinePlugin is the last plugin added
new OfflinePlugin()
]
// ...
}
复制代码
import * as OfflinePluginRuntime from 'offline-plugin/runtime';
OfflinePluginRuntime.install();
复制代码
通过上面的步骤,offline-plugin
已经集成到项目之中,经过webpack
构建便可。
具体代码也可查看 demo
毕竟是自家产品,chrome浏览器确定是支持度最高的浏览器,chrome64版本是基本支持全部PWA功能API
的。
但国内的浏览器·支持状况相对差一些,并且chrome移动版的使用人群仍是偏少的,不过在UC的支持程度也不低