223 lines
5.2 KiB
JavaScript
223 lines
5.2 KiB
JavaScript
/*!
|
|
* 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 };
|