目录
提出场景
一个网页有三个tab页,普通人登录只能访问tab. A,管理人员登录可以访问A,B,而超级管理员可以访问A,B,C。
现在要求做一个提示功能,即所有人如果未登录,会提示未登录;普通人登录并访问B或C,会提示无权限访问B接口、C接口;管理人员登录并访问C,会提示无权限访问C接口;
解决办法
理论上,前端发起fetch请求,根据后端的报错信息可以进行提示,但是因为该项目已经开发完毕;并且包含很多个fetch请求,逐个更改很麻烦,所以使用service worker,把所有的请求与响应全部拦截,然后将根据响应的状态码,提示报错信息。
service worker的作用
一个:可以处理浏览器缓存
二个:可以拦截请求与响应
service worker的基础是web worker,属于多线程,是项目之外的其他线程,所以需要在项目中注册,安装,激活,使用。
注册
//register.js
const start = () => {
if ('serviceWorker' in navigator) {
window.addEventListener('load', function() {
navigator.serviceWorker.register('/serviceWorker.js', {scope: '/'})
.then(function(registration) {
console.log('ServiceWorker registration successful成功 with scope: ', registration.scope);
})
.catch(function(err) {
console.log('ServiceWorker registration failed失败: ', err);
});
});
}
};
⚠️ :在项目中注册serviceWorker.js,注册者(register.js文件)引入被注册者(serviceWorker.js文件) ,路径不是相对路径,而是以项目的根目录为基础。
比如一个react项目的根目录是public文件夹,那么
navigator.serviceWorker.register('/serviceWorker.js', {scope: '/'})
这句话就表示 serviceWorker.js在项目中的位置是:public/serviceWorker.js;
scope: '/':表示的是service Worker监听所有public文件夹下的请求、响应与缓存。
规定,scope只能设置为serviceWorker.js所在文件夹或者子目录。比如你的项目结构:
public
---son1
------grandson
---son2
---son3
---serviceWorker.js
src
那么scope只能设置为:
'/',‘/son1',‘/son2',‘/son3',‘/son1/grandson'
而不能设置为:
'../src'
安装
//serviceWorker.js
self.addEventListener('install', function(event) {
self.skipWaiting();
});
激活
//serviceWorker.js
self.addEventListener('activate', function(event) {
self.clients.claim();
});
网页第一次注册安装service worker的时候,发起的fetch请求不能被service worker拦截,只有第二次起可以,为了解决这个问题,使用了:self.clients.claim();
使用
service worker的两个主要作用,一个是制定浏览器的缓存策略,一个是拦截请求与响应
制定缓存策略
在安装阶段,可以将第一次发起的请求数据(js,html,png等)缓存到本地
在激活阶段可以清除缓存
//安装阶段缓存内容
const CacheName = 'devbup1.1';
const CacheUrls = [
'/index.html',
’/index.js'
];
self.addEventListener('install', function(event) {
event.waitUntil(
caches.open(CacheName).then(function(cache) {
return cache.addAll(CacheUrls);
}),
);
});
//在激活阶段清除本地缓存
self.addEventListener('activate', function(event) {
event.waitUntil(
Promise.all([
this.clients.claim(),
caches.keys().then(function(cachelist) {
return Promise.all(
cachelist.map(function(cacheName) {
if (cacheName !== 'devbup1.1') {
return caches.delete(cacheName);
}
}),
);
}),
]),
);
});
拦截请求与响应
self.addEventListener('fetch', function(event) {
event.respondWith(
fetch(event.request).then((response)=>{
console.log(response.status);
return response;
})
);
});
与项目进行通信
以上我们拦截了请求并能够返回一个响应,但是我们判断出响应的状态码,并不能使用alert进行信息提示(因为service worker是基于web worker无法操作dom),所以需要使用web worker的postmessage进行通信
//serviceWorker.js
self.addEventListener('fetch', function(event) {
event.respondWith(
fetch(event.request).then((response)=>{
const {status} = response;
self.clients.matchAll().then(function(clients) {
clients.forEach(function(client) {
client.postMessage(status);
});
});
return response;
})
);
});
在注册文件中监听
if ('serviceWorker' in navigator && navigator.serviceWorker) {
navigator.serviceWorker.addEventListener('message', function(event) {
alert(event.data);
});
}
友情链接:好文章