PWA 渐进式网页应用

背景:

去年做保险项目时,发现雪球安卓客户端 push 新建webview 时,如果页面是用了SPA框架,加载慢 每页打开都要转圈加载。体验不好。

什么是 PWA

https://developers.google.com/web/progressive-web-apps/

Progressive Web App, 简称 PWA,是提升 Web App 的体验的一种新方法,能给用户原生应用的体验。
PWA 能做到原生应用的体验不是靠特指某一项技术,而是经过应用一些新技术进行改进,在安全、性能和体验三个方面都有很大提升,PWA 本质上是 Web App,借助一些新技术也具备了 Native App 的一些特性,兼具 Web App 和 Native App 的优点。

PWA 的主要特点包括下面三点:

  • 可靠 - 即使在不稳定的网络环境下,也能瞬间加载并展现
  • 体验 - 快速响应,并且有平滑的动画响应用户的操作
  • 粘性 - 像设备上的原生应用,具有沉浸式的用户体验,用户可以添加到桌面
    PWA 本身强调渐进式,并不要求一次性达到安全、性能和体验上的所有要求,开发者可以通过 PWA Checklist 查看现有的特征。

可靠

当用户打开我们站点时(从桌面 icon 或者从浏览器),通过 Service Worker 能够让用户在网络条件很差的情况下也能瞬间加载并且展现。
Service Worker 是用 JavaScript 编写的 JS 文件,能够代理请求,并且能够操作浏览器缓存,通过将缓存的内容直接返回,让请求能够瞬间完成。开发者可以预存储关键文件,可以淘汰过期的文件等等,给用户提供可靠的体验。
详细请看 Service Worker 介绍。

体验

如果站点加载时间超过 3s,53% 的用户会放弃等待。页面展现之后,用户期望有平滑的体验,过渡动画和快速响应。
为了保证首屏的加载,我们需要从设计上考虑,在内容请求完成之前,可以优先保证 App Shell 的渲染,做到和 Native App 一样的体验,App Shell 是 PWA 界面展现所需的最小资源。
参考 App Shell 设计规范。

粘性

  • PWA 是可以安装的,用户点击安装到桌面后,会在桌面创建一个 PWA 应用,并且不需要从应用商店下载
  • PWA 可以借助 Web App Manifest 提供给用户和 Native App 一样的沉浸式体验
  • PWA 可以通过给用户发送离线通知,让用户回流
    Web App Manifest 允许开发者控制 PWA 添加到桌面,允许定制桌面图标、URL等等。
    参考 Web App Manifest 和 Push Notification。

其他

上面讲到 PWA 是兼具 Web App 和 Native App 的特征的,Web App 无版本问题、可索引也是很重要的特性。
总结,PWA 具有下面一些特性

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

体验

雪球保险 https://baoxian.xueqiu.com

试验网站 https://dappwind.com

资源缓存,web 和app打开较快,jsbridge push 不会出现转圈圈加赞。断网也可打开(雪球保险没有加,dappwind加了), 浏览器打开时 支持添加到桌面

浏览器打开 支持添加到桌面,从桌面打开体验类似原生

1

2

3

打开演示

4

跑分

昨天改了改 dappwind.com
lighthouse 测试
progressive web app 项满分
5

实现

主要是 service worker 和 Web App Manifest

service worker

W3C 组织早在 2014 年 5 月就提出过 Service Worker 这样的一个 HTML5 API ,主要用来做持久的离线缓存。
Service Worker 有以下功能和特性:

  • 一个独立的 worker 线程,独立于当前网页进程,有自己独立的 worker context。
  • 一旦被 install,就永远存在,除非被手动 unregister
  • 用到的时候可以直接唤醒,不用的时候自动睡眠
  • 可编程拦截代理请求和返回,缓存文件,缓存的文件可以被网页进程取到(包括网络离线状态)
  • 离线内容开发者可控
  • 能向客户端推送消息
  • 不能直接操作 DOM
  • 必须在 HTTPS 环境下才能工作
  • 异步实现,内部大都是通过 Promise 实现

雪球保险PWA
当时模仿 vue-pwa
使用了个框架 https://github.com/goldhand/sw-precache-webpack-plugin#readme

当时的代码
http://git.snowballfinance.com/f2e/baoxian/commit/8396089f457f9a5326ef9ba52aa7649b593000e2
看起来比较繁琐 完全可以先不用这个包 用基础的自己弄一个

一. 注册service worker

1
2
3
4
5
6
7
8
9
10
11
if ('serviceWorker' in navigator) {
window.addEventListener('load', function() {
navigator.serviceWorker.register('sw-nasgo.js').then(function(registration) {
// 注销操作
//- registration.unregister()
console.log('sw-init-succeeded')
}).catch(function(error) {
console.log('sw-init-error')
});
})
}

建议sw的注册放在 html里,防止放在js文件里 被不小心缓存了就不好弄了.

并增加支持注销操作 出问题可以发版注销

scope: 标明sw作用域 不标明的话 sw只在这个js的路径下生效

关于 navigator.serviceWorker.register('xq-fund-sw.js', {scope: './'}) 的位置

sw-fund-sw.js路径会从 当前页的上级目录开始。如果sw注册在 /f/event ,所以 swjs 路径就是 /f/sw-fund-sw.js swjs在当前路径下,就不用再配置scope了 。然后手动配置这个js文件 不走cdn.

https://gitlab.com/yuxizhe/express-generate/commit/5d5b8a12151d70921f85aa7d1bd8bbde46538c82

这个文章也解释的很好 https://zhuanlan.zhihu.com/p/28161855

二 . 写sw.js 文件

1.方法1 自己写原生js

5

2.方法2 用库 谷歌出品

sw-toolbox 谷歌离线缓存

https://github.com/GoogleChromeLabs/sw-toolbox

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
importScripts('sw-toolbox.js')

self.addEventListener('install', event => {
//取消更新后的等待
self.skipWaiting();
});

toolbox.router.get(/(.png|.jpg)/, toolbox.cacheFirst, {
cache: {
name: "sw-nasgo-img"
}
})

toolbox.router.get(/http.*(.js|.css)/, toolbox.cacheFirst, {
cache: {
name: "sw-nasgo-jscss"
}
})

// 首页缓存
toolbox.router.get('/', toolbox.networkFirst, {
cache: {
name: "html"
}
})

// 接口缓存
toolbox.router.get(/list/, toolbox.networkFirst, {
cache: {
name: "json"
}
})

其中 self.skipWaiting(); 是因为 文件更新后,不会自动更新 会进入等待更新的状态

https://googlechromelabs.github.io/sw-toolbox/api.html#options

service worker.js 增加

1
2
3
4
self.addEventListener('install', event => {
//取消更新后的等待
self.skipWaiting();
});

即可

状态管理

https://developers.google.com/web/fundamentals/primers/service-workers/lifecycle?hl=zh-cn

sw-toolbox 缓存策略

Handlers
There are five built-in handlers to cover the most common network strategies. For more information about offline strategies see the Offline Cookbook.

toolbox.networkFirst

Try to handle the request by fetching from the network. If it succeeds, store the response in the cache. Otherwise, try to fulfill the request from the cache. This is the strategy to use for basic read-through caching. It’s also good for API requests where you always want the freshest data when it is available but would rather have stale data than no data.

toolbox.cacheFirst

If the request matches a cache entry, respond with that. Otherwise try to fetch the resource from the network. If the network request succeeds, update the cache. This option is good for resources that don’t change, or have some other update mechanism.

toolbox.fastest

Request the resource from both the cache and the network in parallel. Respond with whichever returns first. Usually this will be the cached version, if there is one. On the one hand this strategy will always make a network request, even if the resource is cached. On the other hand, if/when the network request completes the cache is updated, so that future cache reads will be more up-to-date.

toolbox.cacheOnly

Resolve the request from the cache, or fail. This option is good for when you need to guarantee that no network request will be made, for example saving battery on mobile.

toolbox.networkOnly

Handle the request by trying to fetch the URL from the network. If the fetch fails, fail the request. Essentially the same as not creating a route for the URL at all.

sw-precache 预缓存

https://github.com/GoogleChromeLabs/sw-precache

加载CDN资源会有跨域问题,要自己更改模板
经过保险的测试 可控性不太好 容易出问题 比如环境切换时,最后只用了sw-toolbox

Web App Manifest

添加到主屏幕
PWA 提供了 manifest.json 配置文件,可以让开发者自定义添加至桌面时的图标、显示名称、启动方式等等信息,并提供 API 方便开发者管理网络应用安装横幅,让用户可以方便快捷地将您的站点添加到主屏幕中。

网络推送通知
即使在浏览器关闭的情况下,网络推送通知也可以像原生 APP 那样进行消息推送,并且将推送的消息显示在通知栏里。推送通知将分为以下两部分内容进行说明:

  • 如何实现消息推送:使用入门
  • 如何实现消息通知:使用入门

4

配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
"name": "DappWind",
"short_name": "DappWind",
"start_url": ".",
"display": "standalone",
"background_color": "#fff",
"theme_color": "#3fadc5",
"icons": [
{
"src": "https://xqimg.imedao.com/164efd7cebf1ab3fd3636361.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "https://xqimg.imedao.com/164efd789df1bb3fbcf194ec.png",
"sizes": "512x512",
"type": "image/png"
}
]
}

发现ios 也部分支持