微应用如何实现自动更新提示

news/2024/11/24 6:34:23/

首先, 先讲一下本次文章所讲的场景, 经过调研, 公司内部使用后台, 当有需求功能迭代的时候, 通常使用者会没有感知, 使用者只会在浏览器内一直打开这个页面, 当需要使用的时候, 再切换这个tab来使用.

这就导致使用者一直不知道系统更新了, 一直没有访问最新的页面(由于最新页面需要刷新浏览器, 重新对静态资源进行读取)

其实这个时候刷新一下网页就可以实现去访问新资源了

效果如下:

什么是微应用

先来介绍一下微应用的特点.

微应用就是有一个主应用服务负责登录和管理各个子应用, 通常企业中的后台就是这样的

比如有一个主应用app, 主应用中有很多很多子后台, 比如: 管理订单的(orderCenter), 管理用户信息的(userCenter), 管理考勤的(attendanceCenter)

前端微应用框架中, qiankun最受欢迎, 使用量和star数量都很高

如它的介绍: Blazing fast, simple and complete solution for micro frontends.

快速、简单、完整的微前端解决方案。

介绍就到这里, 如果还需要详细了解, 可以查看这篇文章:

基于 qiankun 的微前端应用实践

何时自动更新

当然解决问题的之前, 需要考虑清楚本次需求, 也就是最终要实现的效果

我们当然希望在项目重新构建完成之后, 在构建之前打开的网页能够知道项目重新构建了,然后提示用户去更新页面.

但是这个时候要能够选择是否更新, 因为如果用户正在填写表单, 那一更新页面, 表单数据没有提交, 用户肯定这个时候不希望再重新填写一遍. 这个时候要能够忽略更新, 然后不在提示, 等用户填写完成之后, 手动刷新就可以了.

如何实现自动更新

这部分其实就存在两个难点:

1.如何知道服务重新部署了?

方案1

在node端监听远程文件是否更改

如何监听文件是否更改?

node端可以很轻易监听本地文件是否变化, chokidar读取文件更改,不能监听远程服务的文件

但是当node服务与要监听的文件不是在同一台服务器, 需要去监听远程文件的变化, 就不简单

解决方案

可以通过get请求去读取远程服务器下的文件夹,会返回一个html,html中有最后修改时间和文件名

可以通过请求响应头里面的last-modified来判断是否更新

缺点:

1.需要有访问文件的权限, 通常在nginx层设置了不允许访问

2.需要node端轮询去查文件夹的最后更改时间

方案2:

项目部署之后, 通知node服务哪些系统重新部署了

如何通知?

可以请求一个node服务的接口, 参数传递哪个系统重新部署, 什么时候部署

缺点: 需要其他部门配合

2.如何通知应用更新?

方案1:

接入socket

什么是socket?

Socket 是网络编程中一种低级别的网络通信方式,它可以实现双向通信和实时通信。

这里主应用肯定是通讯的一方, 还需要有另一方, 来监听服务重新部署,所以这个方案需要有一个node后端服务, 来做通讯的另一方, 这个node服务可以与主应用进行通信.

但是socket是双向通信, 其实我们只需要服务端通知客户端就行了, 客户端无需与服务端通信, 所以这个方案不符合场景.

所以就有了第二种方案.

方案2:

SSE 全称是 Server Sent Event,翻译过来的意思就是 服务器派发事件。

一个网页获取新的数据通常需要发送一个请求到服务器,也就是向服务器请求的页面。

使用 server-sent 事件,服务器可以在任何时刻向我们的 Web 页面推送数据和信息。

这些被推送进来的信息可以在这个页面上作为 Events [0] + data 的形式来处理。

服务端

import { Router } from "express";
// import fetch from "node-fetch";const sseServer = Router();
const sendData = { time: '' }sseServer.all("*", function(req, res, next) {res.header("Access-Control-Allow-Origin", "*");res.header('Access-Control-Allow-Methods', 'PUT, GET, POST, DELETE, OPTIONS');res.header("Access-Control-Allow-Headers", "X-Requested-With");res.header('Access-Control-Allow-Headers', 'Content-Type');next();
})sseServer.get('/events', (req, res) => {// 设置响应头res.setHeader('Content-Type', 'text/event-stream');// res.setHeader('Cache-Control', 'no-cache');res.setHeader('Connection', 'keep-alive');setInterval(() => {// console.log(sendData)res.write(`data: ${JSON.stringify(sendData)}\n\n`);}, 5000);
});

客户端:

const configEnv = import.meta.env
const source = new EventSource(`${configEnv.VITE_BASE_API}/sseServer/events`);
let oldStr = ''
// 监听 message 事件
source.onmessage = event => {let time = JSON.parse(event.data).timeif (oldStr.length === 0) {oldStr = timeconsole.log('connect success')}else if(oldStr !== time) {window.alert('系统更新啦~~~')oldStr = time}
}

SSE 与 Socket 有什么区别?

方式协议交互通道内容编码重连事件类型总结
SSEHTTP服务端单向推送默认文本默认支持断线重连支持自定义消息类型轻量级
WebSocketWS(基于 TCP 传输层的应用层协议,RFC6455[1] 对于它的定义标准)双向推送默认二进制手动实现NO扩展性、功能性强大

缺点:

Server Sent Events(SSE)对服务器端确实有一定要求:

  1. 支持长连接 - SSE 依赖于 HTTP 长连接,服务器需要能够保持连接,定期推送数据。

  2. 高并发 - 由于是长连接,每个客户端都会占用服务器的连接资源,高并发场景下会对服务器产生较大压力。

  3. 数据序列化 - 服务器需要将数据序列化为 SSE 格式的文本流进行推送,这会带来一定的 CPU 和内存消耗。

  4. 心跳机制 - 为了防止长时间空闲的连接被中间件关闭,服务器需要定期推送空数据给客户端(心跳数据)。

以上方案流程如下:

前端服务轮询实现

上面的解决方案, 都是需要node服务的

但是有没有更加简洁和方便的方案呢?

比如说: 只需要前端服务进行修改就能实现这样的需求

然后我看到了一篇文章的解决方案: 前端重新部署如何通知用户刷新网页?

根据打完包之后生成的script src 的hash值去判断,每次打包都会生成唯一的hash值,只要轮询去判断不一样了,那一定是重新部署了.

缺点:

1.需要将所有子应用配置更改

2.需要轮询去查看文化hash值是否发生改变

最佳解决方案

基于上面这个方法, 我想到了一种更加可行的方案

服务build的时候肯定是有node环境的, node环境是可以写文件的, build就是将静态资源打包

那如果build的时候维护一个版本号, 这个版本号每次build都不一样, 那是不是就可以用来判断是否更新啦

而且build的时候可以将这个版本号跟静态资源一同保存起来

所以写一个打包插件, 就能够将最新的版本号保存起来.那什么作为版本号呢? 思来想去, 其实代码提交的时候就会有一个commitId, 这个id是唯一, 且不会重复的.

node端去读取git提交时候的commitId, 代码如下:

import { execSync } from 'node:child_process'
export function getGitCommitHash() {try {return execSync('git rev-parse --short HEAD').toString().replace('\n', '').trim()}catch (err) {console.warn(`[web-auto-notify-plugin] Not a git repository!`)throw err}
}

由于公司中微应用是vue搭建的, 所以打包工具是vue-cli, 所以只需要写一个webpack插件就可以了

import type { Options } from '../../core/src/index'
import {DIRECTORY_NAME,JSON_FILE_NAME,generateJSONFileContent,getVersion,
} from '../../core/src/index'
import type { Compilation, Compiler } from 'webpack'const pluginName = 'WebAutoNotifyPlugin'type PluginOptions = Options & {indexHtmlFilePath?: string
}class WebAutoNotifyPlugin {options: PluginOptionsconstructor(options: PluginOptions) {this.options = options || {}}apply(compiler: Compiler) {const { publicPath } = compiler.options.outputif (this.options.injectFileBase === undefined)this.options.injectFileBase = typeof publicPath === 'string' ? publicPath : '/'const { versionType, customVersion, silence } = this.optionslet version = ''if (versionType === 'custom')version = getVersion(versionType, customVersion!)elseversion = getVersion(versionType!)compiler.hooks.emit.tap(pluginName, (compilation: Compilation) => {// const outputPath = compiler.outputPathconst jsonFileContent = generateJSONFileContent(version, silence)// @ts-expect-errorcompilation.assets[`${this.options.injectFileBase}${DIRECTORY_NAME}/${JSON_FILE_NAME}.json`] = {source: () => jsonFileContent,size: () => jsonFileContent.length,}})}
}export { WebAutoNotifyPlugin }

webpack内置了很多生命周期钩子, 方便插件使用.

plugins是可以用自身原型方法apply来实例化的对象。apply只在安装插件被Webpack compiler执行一次。apply方法传入一个webpck compiler的引用,来访问编译器回调。

compiler.hooks.emit.tap中的回调就是创建一个json文件, 这个json文件中就保存一个version字段.

这样就解决了第一个难点, 如何知道服务器重新部署了.

接着来解决第二个问题, 如何通知应用更新?

在客户端引入一个npm依赖包, 然后在项目中引入就行.

更新的话, 肯定就要去请求这个静态资源了, 但是什么时候去请求呢?我觉得在以下几种情况就需要去请求了

1.首次加载页面时

2.静态资源获取失败(404)

3.页面refocus或者revisible

但是还有一种情况, 就是用户正在使用时, 这个时候也需要提示更新了, 所以要有个定时任务, 来请求

当这个页面隐藏时, 就关闭定时器就可以了.

如何判断静态资源404

// listener script resource loading error
window.addEventListener("error",err => {const errTagName = (err?.target as any)?.tagName;if (errTagName === "SCRIPT") {checkSystemUpdate();}},true
);

最终的流程如下:

好了, 全部的代码这里就不贴了, 优点占篇幅, 所以我留下git仓库地址:

https://github.com/0522skylar/web-auto-notify

可以先下载npm包体验一下:

https://www.npmjs.com/package/web-auto-notify-webpack

https://www.npmjs.com/package/web-auto-notify-client


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

相关文章

ICV报告:乘光伏新能源汽车之势,功率器件蓄势待发

前言: 电力电子器件(Power Electronic Device),又称为功率半导体器件,用于电能变换和电能控制电路中的大功率(通常指电流为数十至数千安,电压为数百伏以上)电子器件。功率器件能够承受和控制较大电流、电压…

matplotlib实操

matplotlib实操 问题1.分析离网用户的基本特征:包括但不限于地市、年龄、网龄、融合类型、套餐分布、用户价值等,年龄、网龄、用户价值(ARPU)、MOU、DOU;数据预处理处理异常值地市分布县级分布年龄分布网龄分布性别与年龄分布融合类型套餐分布用户价值(ARPU)MOU(每用…

iOS性能优化-异步绘制与异步底层View处理

前言: 基于UIKit的性能优化似乎已经到了瓶颈,无论是使用frame代理snpakit,缓存高度,减少布局层次,diff刷新,压缩图片,选择合适队列,选择高性能锁,也不能满足当前庞大而又…

家用计算机的内存容量大约是多少升,家用旧电脑最佳升级方案:8G内存、混合硬盘足够了!...

在讲加内存条、换硬盘、加固态硬盘之前,内存、硬盘、固态硬盘在广义上属于存储器的一种,它们的主要作用就是存储数据。不过他们之间爷爷很多不同之处,下面先来了解一下他们的基本工作原理。 台式机内存条 在问题中提到的内存,它是…

8g内存一般占用多少_8g内存开机占用一半|Windows操作系统内存使用率多少正常?...

Windows操作系统内存使用率多少正常?内存使用率根据不同用户的使用习惯和软件安装,笔者总结并模拟了一下资源占用情况,可以根据数据预测XP、Win7、Win8、Win8.1、Win10的开机资源占用率上下浮动,以供参考。 Windows操作系统内存使用率多少正常? 如果使用是2G内存的情况下,…

8g内存一般占用多少_你到底需要多大内存?4G、8G还是16G

1你到底需要多大内存? 很多老DIY玩家或许还依稀记得,在DDR2时代(大概2007年左右),2GB和4GB内存的游戏性能相差并不大,所以在当时很长一段时间内,看上去很美的4G容量往往会被扣上华而不实的帽子。如今,内存已…

果推断17--基于反事实因果推断的度小满额度模型学习笔记

目录 一、原文地址 二、一些问题 2.1如何从RCT随机样本过渡到观测样本因果建模? 2.2反事实学习的核心思想 2.3度小满的连续反事实额度模型 Mono-CFR 2.4Mono-CFR代码实现(待补充) 2.5CFR学习 2.5.1CFR 2.5.2DR-CFR 参考 一、原文地…

极速版手机蓝牙APP开发

极速版手机蓝牙APP开发 零、效果展示一、环境介绍二、开发过程控件布局代码逻辑蓝牙部分摇杆部分其他部分 三、整体优化四、结束语 零、效果展示 “这是一个充满科技风的手机蓝牙APP” 一、环境介绍 App Inventor是一款谷歌公司开发的手机编程软件,主要支持各种…