CY

使生如夏花之绚烂,死如秋叶之静美

  1. 1. 它是什么 ?
  2. 2. 创建 Web App Manifest
    1. 2.1. 判断用户是否安装此应用
  3. 3. 添加离线缓存
  4. 4. 什么是 Service Worker
  5. 5. 注册 Service Worker
  6. 6. Service Worker 编写
    1. 6.1. Install
    2. 6.2. activate
    3. 6.3. fetch
  7. 7. 大公告成

它是什么 ?

Progressive Web App, 简称 PWA,是提升 Web App 的体验的一种新方法,能给用户(类)原生应用的体验。

PWA 的主要特点:

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

创建 Web App Manifest

PWA 添加至桌面的功能实现依赖于 manifest.json,所以我们首先来创建它。

用于添加到桌面的描述等,提升 PWA 从主屏幕启动时的应用体验。
如:

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
{
"short_name": "短名称", //用于主屏幕显示
"name": "这是一个完整名称", //用于安装横幅显示
"background_color": "#0000ff", //启动背景颜色
"display": "standalone", //启动显示类型
"theme_color": "blue", //主题颜色, 启动画面上状态栏、内容页中状态栏、地址栏的颜色,会被 theme_color 所影响。
"icons": [
{
"src": "images/icon/icon-48.png",
"type": "image/png",
"sizes": "48x48"
},
{
"src": "images/icon/icon-64.png",
"sizes": "64x64"
},
{
"src": "images/icon/icon-114.png",
"type": "image/png",
"sizes": "114x114"
},
{
"src": "images/icon/icon-128.png",
"type": "image/png",
"sizes": "128x128"
}
],
"start_url": "/?from=pwa"
}

应用安装提醒
浏览器在 PWA 站点满足以下条件时会自动显示横幅,提醒你将站点添加到桌面:

  • 站点部署 manifest.json,该文件需配置如下属性:

    • short_name (用于主屏幕显示)
    • name (用于安装横幅显示)
    • icons (其中必须包含一个 mime 类型为 image/png 的图标声明)
    • start_url (应用启动地址)
    • display (必须为 standalone 或 fullscreen)
  • 站点注册 Service Worker。

  • 站点支持 HTTPS 访问。

  • 站点在同一浏览器中被访问至少两次,两次访问间隔至少为 5 分钟

  • 如果满足以上条件浏览器也提供了一些事件接口供网站开发者使用。

判断用户是否安装此应用

beforeinstallprompt 事件返回一个名为 userChoice 的 Promise 对象,并在当用户对横幅进行操作时进行解析。promise 会返回属性 outcome,该属性的值为 dismissed 或 accepted,如果用户将网页添加到主屏幕,则返回 accepted。

1
2
3
4
5
6
7
8
9
window.addEventListener('beforeinstallprompt', function(e) {
e.userChoice.then(function(choiceResult) {
if (choiceResult.outcome === 'dismissed') {
console.log('用户取消安装应用')
} else {
console.log('用户安装了应用')
}
})
})

添加离线缓存

PWA 主要解决的就是离线访问,即使再没网的情况下也能查看之前访问过的资源,实现这些功能可以使用 indexDB,service worker,localstorage 等,下面我们使用 SW 实现。

什么是 Service Worker

基于 Cache API 实现,主要用来做持久的离线缓存。在你首次访问网站,将部分资源离线缓存,在你网络不好或者无网状态下将会加载这些缓存资源。

注册 Service Worker

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!DOCTYPE html>
<html>
<body></body>
<script>
if ('serviceWorker' in navigator) {
navigator.serviceWorker
.register('sw.js', { scope: '/' })
.then(function(reg) {
console.log('Registration succeeded. Scope is ' + reg.scope)
})
.catch(function(error) {
console.log('Registration failed with ' + error)
})
}
</script>
</html>

注册成功后在控制台 Application -> Service Workers 将看到:
在你的 service worker 注册之后,浏览器会尝试为你的页面或站点安装并激活它。

Service Worker 编写

service worker 主要有三个事件: install,activate 和 fetch。

Install

install: 这个状态发生在 Service Worker 注册之后,表示开始安装,触发 install 事件回调指定一些静态资源进行离线缓存
添加 sw.js ,缓存网站静资源:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const cacheVersion = '2018518'
const staticeCacheName = 'static' + cacheVersion //缓存版本(分组)
const cacheList = [
//缓存列表
'/',
'img.png'
]
self.addEventListener('install', function(event) {
console.log('service worker: install')
event.waitUntil(
//确保以下执行后完成 sw 安装。
caches
.open(staticeCacheName)
.then(function(cache) {
return cache.addAll(cacheList)
})
.then(() => self.skipWaiting()) //安装阶段跳过等待,直接进入 active, 保证每次 /sw.js
)
})
  • event.waitUntil:执行完内部代码(缓存资源),进行 sw 安装。
  • caches.open:添加缓存版本(组)。

刷新页面,你见看到资源已经被缓存。

activate

当 install 完成后, service worker 进入 active 状态,这个事件立刻执行。如果你的 service worker 已经被安装,但是刷新页面时有一个新版本的可用,新版的 service worker 会在后台安装,但是还没激活。这时候就需要删除旧版本的缓存并注册新的 sw 。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
self.addEventListener('activate', function(event) {
console.log('service worker: activate')
const newCaches = 'newCaches'
event.waitUntil(
self.clients.claim(), //更新客户端
caches.keys().then(function(keyList) {
return Promise.all(
keyList.map(function(key) {
if (newCaches.indexOf(key) === -1) {
return caches.delete(key)
}
})
)
})
)
})

fetch

每次任何被 service worker 控制的资源被请求到时,都会触发 fetch 事件,这些资源包括了指定的 scope 内的文档,和这些文档内引用的其他任何资源(比如 index.html 发起了一个跨域的请求来嵌入一个图片,这个也会通过 service worker 。
我们可以利用它来进行监听 http 请求,更改内容。

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
self.addEventListener('fetch', function(event) {
console.log('service worker: fetch')
event.respondWith(
caches
.match(event.request)
.then(function(resp) {
if ((event.request.url = 'xxxxx')) {
//doSomeThing
}
return (
resp ||
fetch(event.request).then(function(response) {
//↑ 如果缓存有直接返回,否则发起网络请求
return caches.open('fetch').then(function(cache) {
//↑ 创建新本,缓存新的请求资源
if (!(event.request.url.indexOf('http') === -1)) {
//排除 Chrome 扩展
cache.put(event.request, response.clone()) //将新的新的请求资源推入缓存
return response
} else {
return response
}
})
})
)
})
.catch(function() {
return caches.match('/error.html') //没有缓存并且请求错误
})
)
})

大公告成

你可以尝试将本站点添加至桌面即可看到效果。

本文作者 : CY
本文使用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 协议
本文链接 : https://runtua.cn/2019/05/18/pwa/

本文最后更新于 天前,文中所描述的信息可能已发生改变