上代码
interface Options {
timer?: number;
}
type key = 'no-update' | 'update';
export class Updater {
oldScript: string[];
newScript: string[];
timer: number = 0;
dispatch: Record<string, Function[]>;
constructor(option?: Options) {
this.oldScript = [];
this.newScript = [];
this.dispatch = {};
this.init(); // 初始化
this.timing(option?.timer);
}
async init() {
const html = await this.getHtml();
this.oldScript = this.parseScript(html);
}
static restart() {
const update = new Updater();
return update;
}
async getHtml() {
const html = await fetch('/').then((res) => res.text());
return html;
}
parseScript(html: string) {
const reg = new RegExp(/<script(?:\s+[^>]*)?>(.*?)<\/script\s*>/gi);
return html.match(reg) as string[];
}
// 发布订阅
on(key: key, fn: Function) {
(this.dispatch[key] || (this.dispatch[key] = [])).push(fn);
return this;
}
compare(oldArr: string[], newArr: string[]) {
if (oldArr.length === 0 || newArr.length === 0) return;
const base = oldArr.length;
const arr = Array.from(new Set(oldArr.concat(newArr)));
// 长度相同就没触发更新
if (base === arr.length) {
this.dispatch['no-update'] &&
this.dispatch['no-update'].forEach((fn) => fn());
} else {
// 反之跟新了
this.dispatch['update'] &&
this.dispatch['update'].forEach((fn) => fn());
}
}
timing(time = 0.5 * 60 * 1000) {
//
this.timer = setInterval(async () => {
const newHtml = await this.getHtml();
this.newScript = this.parseScript(newHtml);
this.compare(this.oldScript, this.newScript);
}, time);
}
clearIntervalFunc() {
clearInterval(this.timer);
this.oldScript = [];
this.newScript = [];
}
// stop 停止轮询 页面隐藏
stop() {
clearInterval(this.timer);
}
// 页面可见 重启
start() {
this.timing();
}
}
- 需要注意的地方
**调用取消之后需要情况定时器,然后重新init初始化 重新timing**
<script setup lang="ts">
import { Updater } from "@/hooks/versionUpdate/updater";
import { onMounted, ref } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus';
const count = ref(0)
const update = ref()
const messageBoxFunc = () => {
count.value += 1
ElMessageBox.confirm(
'版本更新,是否刷新',
'Warning',
{
confirmButtonText: '刷新',
cancelButtonText: '忽略',
type: 'warning',
}
)
.then(() => {
ElMessage({
type: 'success',
message: '刷新成功',
})
clearLocalStorage()
location.reload();
})
.catch(() => {
update.value.clearIntervalFunc()
update.value.init()
update.value.timing()
count.value = 0
})
}
const initUpdater = () => {
console.log(import.meta.env);
if (import.meta.env.PROD) {
update.value = new Updater()
update.value.on('update', () => {
console.log('update');
if (count.value === 0) messageBoxFunc()
})
update.value.on('no-update', () => {
console.log('no-update');
})
}
}
// localStorage 清空处token以外的缓存
function clearLocalStorage() {
const preservedKeysRegex = /^(useToken)$/;
for (let i = localStorage.length - 1; i >= 0; i--) {
const key = localStorage.key(i) as string;
if (!preservedKeysRegex.test(key)) {
localStorage.removeItem(key);
}
}
}
// 检测页面是否可见
const initPageIsshowFunc = () => {
// 判断浏览器是否支持 Page Visibility API
if (typeof document.hidden !== "undefined" && import.meta.env.PROD) {
// 添加可见性状态改变的事件监听器
document.addEventListener("visibilitychange", handleVisibilityChange);
}
}
// 处理可见性状态改变的函数
const handleVisibilityChange = () => {
if (document.hidden) {
// 页面被隐藏,执行相应的操作
console.log("页面隐藏");
update.value.stop()
} else {
// 页面重新可见,执行相应的操作
console.log("页面重新可见");
update.value.start()
}
}
onMounted(() => {
initUpdater()
initPageIsshowFunc()
});
</script>