来用Service worker吧

news/2024/11/28 6:26:57/

之前说了笔者写了一个微前端框架。在微前端中子应用切换前要先获取到子应用的资源(比如js、css)和 html 片段进行加载。那么这里就涉及到一个“老生常谈”的话题:资源缓存

为了缓存子应用的 js、css资源,笔者分两步进行:首先是在主应用加载时“预加载”子应用:

export const prefetch = async () => {// 获取到所有子应用的列表,但不包括当前正在显示的const list = getList().filter(item => !window.location.pathname.startsWith(item.activeRule))// 预加载剩下的所有子应用await Promise.all(list.map(async item => await parseHtml(item.entry, item.name)))
}

然后是在资源请求前后判断缓存、存入缓存:

const cache = {} //根据子应用的name做缓存export const parseHtml = async (entry, name) => {if(cache[name]) {return cache[name]}// 资源加载其实是一个get请求,我们去模拟这个过程const html = await fetchResource(entry)let allScripts = []//...// 抽离标签、link、script(src、代码)const [dom, scriptUrl, script] = await getResources(div, entry)// 获取所有的js资源const fetchedScripts = await Promise.all(scriptUrl.map(async item => await fetchResource(item)))allScripts = script.concat(fetchedScripts)cache[name] = [dom, allScripts]//...
}

这样就达到了提高响应速度的效果。

那在普通项目中呢?
Service Worker API 能够很好的帮助我们完成这个功能:资源缓存、请求缓存。

拿静态资源来说,使用时先注册:

function register() {if('serviceWorker' in navigator) {// 注册,可以给一个scope参数作为作用域navigator.serviceWorker.register('/worker文件路径', {scope: '作用域路径'}).then((res) => {if(res.installing) {// 注册成功} else if (res.waiting) {// 注册过了} else if (res.active) {// 已经开启}}).catch((err) => {console.log('register failed with: ' + err)})}
}

然后进入到 worker 文件中。

一个service注册成功后必然进行install 事件,这里我们可以进行一些静态资源的 cache,以防止在后面的请求时依然对这些资源发送请求!

self.addEventListener('install', function(event) {event.waitUntil(caches.open("缓存名(key)").then(function(cache) {console.log('[SW]: Opened cache');return cache.addAll(静态资源列表);}));
});

这里还有一个需要注意的地方:
如果是第一次加载 sw ,在install后,会直接进入activated阶段,而如果 sw 进行更新,情况就会显得复杂一些:当新的 sw 进入install阶段,而老的那个还处于工作状态,新的那个就会进入waiting阶段。只有等到老的被terminated后,新的才能正常替换老的那个的工作。

如果你不想等待,可以试试把它加到install的“最前面”:

//跳过等待过程
self.skipWaiting();

然后就进入了activated阶段,激活 sw 工作。
activated阶段我们主要用于处理缓存,比如更新存储在cache中的key和value:

self.addEventListener('activate', function(event) {//清除cache中原来老的一批相同key的数据var setOfExpectedUrls = new Set(缓存列表的key-value二维数组.values());event.waitUntil(caches.open(缓存名).then(function(cache) {return cache.keys().then(function(existingRequests) {return Promise.all(existingRequests.map(function(existingRequest) {if (!setOfExpectedUrls.has(existingRequest.url)) {//cache中删除指定对象return cache.delete(existingRequest);}}));});}).then(function() {//self相当于webworker线程的当前作用域//当一个 service worker 被初始注册时,页面在下次加载之前不会使用它。claim() 方法会立即控制这些页面//从而更新客户端上的serviceworkerreturn self.clients.claim();}));
});

然后最关键的就是 fetch 阶段了。

var isPathWhitelisted = function(whitelist, absoluteUrlString) {// If the whitelist is empty, then consider all URLs to be whitelisted.if (whitelist.length === 0) {return true;}// Otherwise compare each path regex to the path of the URL passed in.var path = (new URL(absoluteUrlString)).pathname;return whitelist.some(function(whitelistedPathRegex) {return path.match(whitelistedPathRegex);});
};self.addEventListener('fetch', function(event) {if (event.request.method === 'GET') {// 标识位,用来判断是否需要缓存var shouldRespond;// 处理参数// 再次验证,判断其是否是一个navigation类型的请求var navigateFallback = '';if (!shouldRespond &&navigateFallback &&(event.request.mode === 'navigate') &&isPathWhitelisted([], event.request.url)) {url = new URL(navigateFallback, self.location).toString();shouldRespond = urlsToCacheKeys.has(url);}// 如果标识位为trueif (shouldRespond) {event.respondWith(caches.open(cacheName).then(function(cache) {//去缓存cache中找对应的url的值return cache.match(urlsToCacheKeys.get(url)).then(function(response) {//如果找到了,就返回valueif (response) {return response;}throw Error('The cached response that was expected is missing.');});}).catch(function(e) {// 如果没找到则请求该资源、return fetch(event.request);}));}}
});

先校验请求url和参数。然后判断 cache 中是否有缓存,没有的话就请求资源。

注意:上面说的是“静态资源缓存”,也就是缓存列表是固定的。但是如果你想要缓存普通请求或者其他资源,就不能只是简单的fetch一下了:

self.addEventListener('fetch', function (evt) {//处理// 在发起请求时候会触发fetch事件evt.respondWith(caches.match(evt.request).then(function (response) {// 如果 sw 已经保存了请求的响应,直接返回响应,减少http请求if (response !== undefined) {return response}// 不存在需要发起请求return fetch(evt.request).then((httpRes) => {if (!httpRes || httpRes !== 200) {// 请求出错则直接返回错误信息return httpRes}// 将响应复制一份const httpResClone = httpRes.clone()// 并且保存到安装时候的缓存对象里caches.open(缓存名).then((cache) => {cache.put(evt.request, httpResClone)})return httpRes})}))
})

当cache里面没有缓存,则使用fetch发起请求,这个Fetch发起请求的是用来代替XMLHttpRequest来发请求的方案;当请求响应了错误直接返回错误信息,当请求响应成功的情况,更新cache缓存,将新的响应存入cache缓存中,下次在访问就直接从缓存中读取。


http://www.ppmy.cn/news/2597.html

相关文章

GCN:分布式训练大规模深度图神经网络

图(Graph)数据在现实世界中非常常见,例如社交网络、交通网络、物理系统等等,近几年图神经网络的发展将图数据的分析与深度神经网络结合,在越来越多的领域发挥出重要的作用,例如电商推荐、生物化学结构分析、反恐反诈风险控制等等。数据规模也呈现越来越大之势,动辄上千万…

07、Spring中XML配置和JavaConfig配置比较

1、JavaConfig的优势 我们之前基本都是使用xm的方式进行配置,随着功能和业务的日益增加,会导致我们的配置文件过于庞大。bean之间的依赖关系也会变得更加复杂,使用起来很不方便。Spring3.0开始,官方推荐出了使用Java配置的方式来…

酵母葡聚糖硫酸酯(SPS)|葡聚糖修饰异黄酮|右旋糖酐修饰Savinase蛋白酶

酵母葡聚糖硫酸酯(SPS)|葡聚糖修饰异黄酮|右旋糖酐修饰Savinase蛋白酶 酵母葡聚糖硫酸酯(SPS) 中文名称:酵母葡聚糖硫酸酯(SPS) 纯度:95% 存储条件:-20C,避光,避湿 外观:固体或粘性液体 包装:瓶装/袋…

这可能是全网最详细的python安装教程(windows)

python安装是学习pyhon第一步,很多刚入门小白不清楚如何安装python,今天我来带大家完成python安装与配置,跟着我一步步来,很简单,你肯定能完成。 第一部分:python安装 (一)准备工作…

Wireshark高级特征

1,端点和网络会话 想要让网络通信正常进行,你必须至少拥有两台设备进行数据流的交互。端点(endpoint)就是指网络上能够发送或接收数据的一台设备。两个端点之间的通信被称之为会话(conversation)。Wiresha…

【多线程(五)】volatile关键字、原子性问题、AtomicInteger内存分析与源码分析、悲观锁和乐观锁

文章目录5.原子性5.1 volatile-问题2.2 volatile解决5.3 synchronized 解决5.4 原子性5.5 volatile关键字不能保证原子性5.6 原子性 AtomicInteger5.7 AtomicInteger-内存解析5.8 AtomicInteger-源码解析5.9 悲观锁和乐观锁小结5.原子性 5.1 volatile-问题 代码分析 package…

Linux 之七 内核架构、API/ABI 介绍、文件层次结构、Kernel 源码文件

Linux 内核最早是在 1991 年由芬兰大学生林纳斯托瓦兹为自己的个人电脑开发的,并在 GNU 通用公共许可证第 2 版(也包含了其他兼容许可证)之下发布的一种开源的类 Unix 操作系统宏内核。 注意,我们通常说的 Linux 系统是 Linux Ker…

Nginx 反向代理与负载均衡

什么是Nginx Nginx 是一款高性能的 http 服务器和反向代理服务器,官方测试 nginx 能够支支撑 5 万并发链接,并且 cpu、内存等资源消耗却非常低,运行非常稳定。 Nginx 应用场景 http 服务器:Nginx 是一个 http 服务可以独立提供…