本项目实现了一个 Monitor 类,用于监控网页的脚本和 ETag 变化。通过定期检查页面的 script 和 ETag,可以在页面内容发生变化时触发回调函数。
这边前端代码发包,用户侧在监听到不一致的时候,就能直接提示用户刷新页面获取新版本前端数据。
目的
- 实时监控:实时监控网页的脚本和 ETag 变化。
- 回调通知:在检测到变化时,通过注册的回调函数通知用户。
- 暂停功能:提供暂停监控的功能,以便在需要时停止监控。
技术方案及原理
- 异步请求:使用 fetch API 异步获取页面的 HTML 和 ETag。
- 正则解析:使用正则表达式解析 HTML 中的
<script>
标签内容。 - 事件监听:通过 on 方法注册回调函数,当检测到变化时调用这些回调函数。
- 状态管理:通过类的属性管理当前和旧的脚本内容及 ETag,以及监控状态。
运行步骤
引入模块
import {
monitor } from "./path/to/monitor";
注册回调函数
monitor.on("update", () => {
console.log("页面内容发生变化, 执行操作函数");
});
启动监控
setInterval(() => {
monitor.check();
}, 5000); // 每 5 秒检查一次
暂停监控
monitor.pause(); // 暂停监控
通过 Monitor 类,我们可以实现实时监控网页的脚本和 ETag 变化,并在检测到变化时通过回调函数通知用户。该类提供了灵活的注册回调、暂停监控等功能,适用于需要实时监控页面变化的场景。
完整 JS 代码
class Monitor {
constructor() {
this.oldScript = []; // 存储旧的脚本内容
this.newScript = []; // 存储新的脚本内容
this.oldEtag = null; // 存储旧的 ETag
this.newEtag = null; // 存储新的 ETag
this.dispatch = {
}; // 存储注册的回调函数
this.stop = false; // 控制监控状态
this.init(); // 初始化监控
}
async init() {
console.log("初始化版本监听器");
// 获取初始 HTML
const html = await this.getHtml();
// 解析初始脚本内容
this.oldScript = this.parserScript(html);
// 获取初始 ETag
this.oldEtag = await this.getEtag();
}
async getHtml() {
// 发送 GET 请求获取页面 HTML
const response = await fetch("/");
// 读取响应内容
const html = await response.text();
return html;
}
async getEtag() {
// 发送 GET 请求获取页面 ETag
const response = await fetch("/");
// 从响应头中提取 ETag
const etag = response.headers.get("etag");
return etag;
}
parserScript(html) {
// 正则表达式匹配 <script> 标签
const reg = /<script(?:\s+[^>]*)?>(.*?)<\/script\s*>/gi;
// 匹配所有 <script> 标签内容
const matches = html.match(reg);
// 返回匹配结果,如果没有匹配到则返回空数组
return matches || [];
}
on(key, fn) {
if (key === "update") {
// 只处理 "update" 事件
if (!this.dispatch[key]) {
// 如果没有注册过 "update" 事件
this.dispatch[key] = []; // 初始化回调函数数组
}
this.dispatch[key].push(fn); // 将回调函数添加到数组中
}
return this; // 支持链式调用
}
pause() {
this.stop = !this.stop; // 切换监控状态
}
get value() {
return {
oldEtag: this.oldEtag, // 返回旧的 ETag
newEtag: this.newEtag, // 返回新的 ETag
oldScript: this.oldScript, // 返回旧的脚本内容
newScript: this.newScript, // 返回新的脚本内容
};
}
compare() {
// 如果监控已暂停,则返回
if (this.stop) return;
// 获取旧脚本内容的长度
const oldLen = this.oldScript.length;
// 合并旧脚本和新脚本内容
const newSet = new Set(this.oldScript.concat(this.newScript));
// 获取合并后的内容长度
const newLen = newSet.size;
if (this.oldEtag !== this.newEtag || newLen !== oldLen) {
// 检查 ETag 或脚本内容是否有变化
this.dispatch.update.forEach((fn) => {
// 遍历注册的回调函数
fn(); // 调用每个回调函数
});
}
}
async check() {
// 获取新的 HTML
const newHtml = await this.getHtml();
// 解析新的脚本内容
this.newScript = this.parserScript(newHtml);
// 获取新的 ETag
this.newEtag = await this.getEtag();
// 打印新的 ETag
console.log("etag版本检查", this.newEtag);
// 比较旧内容和新内容
this.compare();
}
}
// 导出实例化的 Monitor 对象
export const monitor = new Monitor();