概念
PWA 是渐进式应用的缩写,全称为:Progressvie web apps。 它是致力于现代应用和增强传统应用的一种解决方案,它具有以下几个优势:
- 可观察
- 可安装
- 独立于网络
- 渐进式增强
- 可重新接替
- 响应式
- 安全
收益
- 它会减少第二次打开的时间,节省了宽带时间
- 可放在屏幕上,微小的改动也可以直接更新应用
- 可以使用通知等功能增加用户体验
离线和缓存
在浏览器中注册代码如下:
// main.js
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js');
}
由上面的代码可知,浏览器的serviceWorker注册了一个sw文件,也就是serviceWorker文件。
而serviceWorker文件则是处理离线状态的文件:
// sw.js
self.addEventListener('install', (event) => {
if (!event) return;
event.waitUnitl(
caches.open('cache').then((cache) => cahce.add('/cache.html'))
);
});
self.addEventListener('fetch', (event) => {
event.responseWith(
fetch(event.request).catch((cache) => caches.match('/cache.html'))
);
});
观察上面的代码可以看出,service worker 监听了一个 install 事件。
self.addEventListener('install', (event) => {
if (!event) return;
event.waitUnitl(
+ caches.open('cache').then((cache) => cahce.add('/cache.html'))
);
});
然后它又调用了一个 caches 的 API,是指从哪个版本取出缓存,而add就是缓存目标了。
其中cache.html的代码是:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
Cache page
</body>
</html>
当然,为了添加离线的功能,我们该需要script和stylesheet的支持。
如果我们写的代码是这样子的:
/* style.css */
.body {
background-color: red;
height: 100%;
width: 100%;
}
然后引用到 HTML 文件中:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ <link rel="stylesheet" href="/style.css" />
<title>Document</title>
</head>
<body>
Cache page
</body>
</html>
当我们写完这些之后,刷新浏览器,得到的结果是这样子的:

然后断网重新刷新一下,直接在 Chrome 的这里可以找到,并勾选:

屏幕上就会出现这样红色背景,则代表成功。
后台同步
PWA 有一个特别厉害的功能就是,后台数据同步,而这个需要在main.js注册:
// main.js
navigator.serviceWorker.ready.then((registeration) => {
registeration.sync.reigster('sync');
});
这样就注册了一个同步信息,然后在 serviceWorker 中接受它:
self.addEventListener('sync', (event) => {
console.log(event.tag);
});
这样,就可以接受后台所传过来的数据了。
页面通信
监听事件通信
我们可以在多个页面之间进行通信,其用法是:
// main.js
navigator.serviceWorker.addEventListener('message', () => {
navigator.serviceWorker.client.postMessage('ok');
});
接着就可以直接在sw.js中直接接收到:
self.addEventListener('message', (event) => {
console.log(event.data); // ok
});
使用 MessageChannel 进行通信
直接使用一个事件管道进行通信:
const messageChannel = new MessageChannel();
navigator.serviceWorker.postMessage({ data: 1 }, [messageChannel.port2]);
这样就完成了一次消息通道的通信了。
然后在sw.js中接受它:
self.addEventListener('message', (event) => {
console.log(event.data);
});
当然,这个还没有发挥出真正的威力。在sw.js中添加如下:
self.addEventListener('message', (event) => {
console.log(event.data);
+ event.ports[0].postMessage("1");
});
这样,就可以直接发送到main.js中了,这是子父通信机制:
const messageChannel = new MessageChannel();
navigator.serviceWorker.postMessage({ data: 1 }, [messageChannel.port2]);
messageChannel.onmessage = (event) =>{
+ console.log(event.data);
}
这样main.js就可接收到来自serviceWorker的数据了。
使用IndexedDB
IndexedDB 是一种数据库,用于存储数据结构的一种数据库。
- 打开数据库
const request = window.indexedDB('user', 2);
request.onsuccess = (event) => {
console.log(event);
};
request.onerror = (event) => {
console.log(event);
};
这样就成功开启了一个indexedDB了,接着新建一个数据库。
request.onupgradeneeded = function (event) {
db = event.target.result;
const objectStore = db.createObjectStore('person', { keyPath: 'id' });
};
然后写入一个数据:
const request = db
.transaction(['person'], 'readwrite')
.objectStore('person')
.add({ id: 1, name: '张三', age: 24, email: 'zhangsan@example.com' });
查看数据:
const transaction = db.transaction(['person']);
const objectStore = transaction.objectStore('person');
const request = objectStore.get(1);
request.onsuccess = (event) => {
console.log(event.result);
};
更新数据:
const request = db
.transaction(['person'], 'readwrite')
.objectStore('person')
.put({ id: 1, name: '李四', age: 35, email: 'lisi@example.com' });