一、项目简介
基于 Service Worker 实现在浏览器多页面(多标签页之间)进行消息同步。
二、项目背景
我们知道,在一个大型的后台管理项目中,我们往往需要管理复杂的角色权限逻辑、管理用户的个性化配置,对于数据展示的一致性有较高的要求。
例如,当用户同时打开多个浏览器页面进行浏览和操作时,由于数据不能在多页面同步,可能会导致用户的误操作或者误解。
为了减少用户的误操作和优化用户的使用体验,我们可以基于 Service Worker 进行多页面的消息同步,使得操作一个页面可以同时刷新多个页面的信息。
三、实践过程
基本原理
Service worker 也是 worker 的一种,也就是说,它运行在 worker 上下文中,不能直接访问 DOM。Service worker 的生命周期与页面无关,它可以自动控制指定 域/请求路径 下的所有客户端页面。它的生命周期主要分为以下几个过程:
安装并调试
在 React 项目中,可以使用 create-react-app 的 pwa 模板
$ npx create-react-app my-app --template cra-template-pwa
$ npx create-react-app my-app-ts --template cra-template-pwa-typescript
安装成功,会在 src
目录下生成 service-worker.js
和 serviceWorkerRegistration.js
两个文件。
为了在本地进行测试,我们先改写下默认的 serviceWorkerRegistration.js
文件
webpack.config.js
中使用 WorkboxWebpackPlugin
来打包 service-worker.js
文件,这里我们同样把生产环境的判断移除。
在 index.js
中安装注册
如果不想使用 cra 的模板来搭建,其实也很简单,不管使用 webpack
打包或者其他工具打包,我们只要保证最终的 service-worker.js
文件可以在项目的指定路径下直接访问即可。在 webpack 中我们可以借助 WorkboxWebpackPlugin
或者 CopyWebpackPlugin
来处理。
运行 yarn start --watch
,我们看到 Service worker 已经成功注册并激活了。
在控制台的 Application
面板,我们可以看到当前域名下注册的文件,以及当前多少窗口/客户端处于Service worker 的控制下
通信
接下来,我们来看看在具体的代码中,我们如何实现多页面的消息通知。
改写 service-worker.js
文件
/* eslint-disable no-restricted-globals */
import { clientsClaim } from "workbox-core";
import { precacheAndRoute } from "workbox-precaching";
clientsClaim();
precacheAndRoute(self.__WB_MANIFEST);
// 监听来自网页客户端的消息
self.addEventListener("message", (event) => {
if (event.data && event.data.type === "SKIP_WAITING") {
self.skipWaiting();
}
var promise = self.clients.matchAll().then(function (clientList) {
var senderID = event.source.id;
// 消息不传递给发送者本身
clientList.forEach(function (client) {
if (client.id === senderID) {
return;
}
client.postMessage({
client: senderID,
message: event.data,
});
});
});
if (event.waitUntil) {
event.waitUntil(promise);
}
});
在页面中监听
const useNotification = () => {
const [data, setData] = useState({});
useEffect(() => {
if ("serviceWorker" in navigator) {
let listener = (event) => {
const clientId = event.data.client;
console.log(`receive message from ${clientId}`);
setData(event.data);
};
navigator.serviceWorker.addEventListener("message", listener);
return () => {
navigator.serviceWorker.removeEventListener("message", listener);
};
}
}, []);
const postMessage = useCallback((message) => {
if ("serviceWorker" in navigator) {
navigator.serviceWorker.controller.postMessage(message);
}
}, []);
return [data, postMessage];
};
实现效果
四、总结思考
本文只是通过一个简单的 Demo 来梳理 Service worker 进行通信的基本步骤,更加复杂的应用只需要在这个 Demo 上进一步拓展即可。
基于 Service Worker 我们还可以实现很多功能,比如后台同步、资源缓存、请求拦截等。
参考 Service Worker Cookbook
本文正在参与「掘金 2021 春招闯关活动」, 点击查看 活动详情
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!