From 58f8586232ae68379d86c630e2b729921e5037e0 Mon Sep 17 00:00:00 2001 From: "DESKTOP-6C3NK6N\\WXS" <815034831@qq.com> Date: Tue, 13 Aug 2024 13:55:02 +0800 Subject: [PATCH] =?UTF-8?q?=E8=87=AA=E5=8A=A8=E6=A3=80=E6=B5=8B=E6=9B=B4?= =?UTF-8?q?=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main.js | 19 ++- src/utils/version-polling.esm.js | 222 +++++++++++++++++++++++++++++++ 2 files changed, 239 insertions(+), 2 deletions(-) create mode 100644 src/utils/version-polling.esm.js diff --git a/src/main.js b/src/main.js index 6e73d496..ffebb519 100644 --- a/src/main.js +++ b/src/main.js @@ -1,10 +1,9 @@ import Vue from 'vue' import 'normalize.css/normalize.css' // A modern alternative to CSS resets - +import { createVersionPolling } from "@/utils/version-polling.esm.js"; import ElementUI, { MessageBox } from 'element-ui' import { getBasicDataAllSelect, getFrontInternationalizationList, getCurrentPublishInfo } from '@/api/dictionary/dictionary' -import { sendMFAEmail } from "@/api/user.js"; import { resetReadingRestTime } from '@/api/trials/reading' // import 'element-ui/lib/theme-chalk/index.css' import './assets/css/theme-blue/index.css' // 浅绿色主题 @@ -529,5 +528,21 @@ async function VueInit() { } : () => { }, process.env.VUE_APP_LOCK_FOR_TIME) } VueInit() +createVersionPolling({ + appETagKey: "__APP_ETAG__", + pollingInterval: 5 * 1000, // 单位为毫秒 + silent: process.env.NODE_ENV === false, // 开发环境下不检测 + onUpdate: (self) => { + // 当检测到有新版本时,执行的回调函数,可以在这里提示用户刷新页面 + const result = confirm("页面有更新,点击确定刷新页面!"); + if (result) { + self.onRefresh(); + } else { + self.onCancel(); + } + // 强制更新可以用alert + // alert('有新版本,请刷新页面'); + }, +}); diff --git a/src/utils/version-polling.esm.js b/src/utils/version-polling.esm.js new file mode 100644 index 00000000..8ee5740c --- /dev/null +++ b/src/utils/version-polling.esm.js @@ -0,0 +1,222 @@ +/*! + * version-polling v1.2.6 + * (c) 2023 JoeshuTT + * @license MIT + */ +function _defineProperty(obj, key, value) { + key = _toPropertyKey(key); + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; +} +function _toPrimitive(input, hint) { + if (typeof input !== "object" || input === null) return input; + var prim = input[Symbol.toPrimitive]; + if (prim !== undefined) { + var res = prim.call(input, hint || "default"); + if (typeof res !== "object") return res; + throw new TypeError("@@toPrimitive must return a primitive value."); + } + return (hint === "string" ? String : Number)(input); +} +function _toPropertyKey(arg) { + var key = _toPrimitive(arg, "string"); + return typeof key === "symbol" ? key : String(key); +} + +/** + * 是否有值 + * @param {*} val + */ +/** + * 创建一个 Web Work 实例 + * @param func + */ +function createWorker(func) { + const blob = new Blob(["(" + func.toString() + ")()"]); + const url = window.URL.createObjectURL(blob); + const worker = new Worker(url); + window.URL.revokeObjectURL(url); + return worker; +} +function createWorkerFunc() { + let timerId = null; + let options; + self.onmessage = event => { + let code = event.data["code"]; + options = Object.assign({}, options, event.data["data"]); + const { + htmlFileUrl, + lastEtag, + appETagKey, + pollingInterval, + silentPollingInterval + } = options; + const runReq = () => { + fetch(htmlFileUrl, { + method: "HEAD", + cache: "no-cache" + }).then(response => { + if (Number(response.status) !== 200) { + return; + } + const etag = response.headers.get("etag"); + if (lastEtag !== etag) { + self.postMessage({ + appETagKey, + lastEtag, + etag + }); + } + }); + }; + const startPollingTask = () => { + timerId = setInterval(runReq, pollingInterval); + }; + const pausePollingTask = () => { + if (timerId) { + clearInterval(timerId); + timerId = null; + } + }; + if (code === "pause") { + pausePollingTask(); + } else { + runReq(); // 立即执行一次 + if (!silentPollingInterval) { + startPollingTask(); + } + } + }; + return self; +} +function closeWorker(worker) { + worker.terminate(); +} + +let APP_ETAG_KEY = "__APP_ETAG__"; +let myWorker; +const defaultOptions = { + appETagKey: APP_ETAG_KEY, + pollingInterval: 5 * 60 * 1000, + htmlFileUrl: `${location.origin}${location.pathname}`, + silent: false, + silentPollingInterval: false, + silentPageVisibility: false, + forceUpdate: false +}; +let attached = false; +/** + * 页面隐藏时停止轮询任务,页面再度可见时在继续 + */ +function handleVisibilityChange() { + if (document.visibilityState === "visible") { + myWorker.postMessage({ + code: "resume" + }); + } else { + myWorker.postMessage({ + code: "pause" + }); + } +} +class VersionPolling { + constructor(options) { + _defineProperty(this, "options", void 0); + _defineProperty(this, "appEtag", ""); + this.options = Object.assign({}, defaultOptions, options); + this.init(); + } + async init() { + const { + htmlFileUrl + } = this.options; + if (!htmlFileUrl) { + throw new Error("[version-polling]: htmlFileUrl is required"); + } + const response = await fetch(htmlFileUrl, { + method: "HEAD", + cache: "no-cache" + }); + if (Number(response.status) !== 200) { + throw new Error(`[version-polling]: status is ${response.status}`); + } + const etag = response.headers.get("etag"); + // eslint-disable-next-line no-eq-null + if (etag == null) { + throw new Error(`[version-polling]: etag is null`); + } + this.appEtag = etag; + localStorage.setItem(`${this.options.appETagKey}`, etag); + this.start(); + } + start() { + const { + appETagKey, + pollingInterval, + htmlFileUrl, + silent, + silentPollingInterval, + silentPageVisibility + } = this.options; + if (silent) { + return; + } + myWorker = createWorker(createWorkerFunc); + myWorker.postMessage({ + code: "start", + data: { + appETagKey, + pollingInterval, + htmlFileUrl, + silentPollingInterval, + lastEtag: this.appEtag + } + }); + myWorker.onmessage = event => { + const { + lastEtag, + etag + } = event.data; + if (lastEtag !== etag) { + this.stop(); + this.options.onUpdate(this); + } + }; + if (!silentPageVisibility) { + if (!attached) { + document.addEventListener("visibilitychange", handleVisibilityChange); + attached = true; + } + } + } + stop() { + if (myWorker) { + closeWorker(myWorker); + if (attached) { + document.removeEventListener("visibilitychange", handleVisibilityChange); + attached = false; + } + } + } + onRefresh() { + window.location.reload(); + } + onCancel() { + this.options.forceUpdate && this.start(); + } +} +function createVersionPolling(options) { + const versionPolling = new VersionPolling(options); + return versionPolling; +} + +export { VersionPolling, createVersionPolling };