universal-build-plugin-version-check-test
Version:
一个支持多种前端构建工具的通用插件框架,包括 Vite、Webpack、Rollup 等,提供版本检查、构建优化等功能
295 lines (255 loc) • 7.45 kB
text/typescript
/**
* 版本检查器模板文件
* 这个文件会被编译成 JavaScript 后注入到 HTML 页面中
*/
// 版本检查器配置接口
interface VersionCheckConfig {
currentVersion: string;
checkInterval: number;
enableNotify: boolean;
versionFile: string;
enableConsoleLog: boolean;
maxRetries: number;
retryDelay: number;
onVersionChange?: (oldVersion: string, newVersion: string) => void;
onError?: (error: Error) => void;
}
// 版本检查器类
class VersionChecker {
private config: VersionCheckConfig;
private timerId: number | null = null;
private retryCount = 0;
private isChecking = false;
private lastCheckTime = 0;
constructor(config: VersionCheckConfig) {
this.config = {
...config,
};
}
/**
* 启动版本检查
*/
start(): void {
if (this.timerId) {
this.log('版本检查器已经在运行中');
return;
}
this.log(
`启动版本检查器,当前版本: ${this.config.currentVersion},检查间隔: ${this.config.checkInterval}ms`
);
// 立即执行一次检查
this.checkVersion(true);
// 设置定时器
this.timerId = window.setInterval(() => {
this.checkVersion();
}, this.config.checkInterval);
}
/**
* 停止版本检查
*/
stop(): void {
if (this.timerId) {
clearInterval(this.timerId);
this.timerId = null;
this.log('版本检查器已停止');
}
}
/**
* 检查版本更新
*/
private async checkVersion(isFirstCheck = false): Promise<void> {
if (this.isChecking) {
this.log('版本检查正在进行中,跳过本次检查');
return;
}
this.isChecking = true;
this.lastCheckTime = Date.now();
try {
const versionInfo = await this.fetchVersionInfo();
if (isFirstCheck) {
this.updateVersion(versionInfo.version);
this.log('开始首次版本检查');
return;
}
// 获取旧版本信息
const oldVersion = window.localStorage.getItem('version');
// 如果没有旧版本,则不进行检查
if (!oldVersion) {
this.log('未找到旧版本信息,跳过本次检查');
this.isChecking = false;
return;
}
if (versionInfo && versionInfo.version && versionInfo.version !== oldVersion) {
// 触发版本变更回调
if (this.config.onVersionChange) {
this.config.onVersionChange(this.config.currentVersion, versionInfo.version);
}
await this.handleVersionUpdate(versionInfo);
} else {
this.log('版本检查完成,无更新');
}
// 重置重试计数
this.retryCount = 0;
} catch (error) {
this.handleError(error as Error);
} finally {
this.isChecking = false;
}
}
/**
* 获取版本信息
*/
private async fetchVersionInfo(): Promise<any> {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 10000); // 10秒超时
try {
const response = await fetch(`/${this.config.versionFile}?t=${Date.now()}`, {
method: 'GET',
headers: {
'Cache-Control': 'no-cache',
Pragma: 'no-cache',
},
signal: controller.signal,
});
clearTimeout(timeoutId);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const contentType = response.headers.get('content-type');
if (!contentType || !contentType.includes('application/json')) {
throw new Error('响应不是有效的JSON格式');
}
return await response.json();
} catch (error) {
clearTimeout(timeoutId);
throw error;
}
}
/**
* 处理版本更新
*/
private async handleVersionUpdate(versionInfo: any): Promise<void> {
// 显示通知
if (this.config.enableNotify) {
await this.showUpdateNotification(versionInfo);
}
}
/**
* 显示更新通知
*/
private async showUpdateNotification(versionInfo: any): Promise<void> {
if (!('Notification' in window)) {
this.log('浏览器不支持通知功能');
return;
}
try {
let permission = Notification.permission;
if (permission === 'default') {
permission = await Notification.requestPermission();
}
if (permission === 'granted') {
const notification = new Notification('系统更新提示', {
body: `检测到新版本 ${versionInfo.version}`,
icon: '/favicon.ico',
tag: 'version-update',
requireInteraction: false,
});
// 3秒后自动关闭通知
setTimeout(() => {
notification.close();
this.updateVersion(versionInfo.version);
}, 3000);
}
} catch (error) {
this.log('显示通知失败:', error);
}
}
/**
* 更新版本号
*/
private updateVersion(newVersion: string): void {
window.localStorage.setItem('version', newVersion ?? '');
}
/**
* 处理错误
*/
private handleError(error: Error): void {
this.retryCount++;
this.log(`版本检查失败 (${this.retryCount}/${this.config.maxRetries}):`, error.message);
// 触发错误回调
if (this.config.onError) {
this.config.onError(error);
}
// 如果达到最大重试次数,停止检查器
if (this.retryCount >= this.config.maxRetries) {
this.log('达到最大重试次数,停止版本检查');
this.stop();
return;
}
// 延迟后重试
setTimeout(() => {
this.log(`${this.config.retryDelay}ms 后重试版本检查`);
this.checkVersion();
}, this.config.retryDelay);
}
/**
* 日志输出
*/
private log(message: string, ...args: any[]): void {
if (this.config.enableConsoleLog) {
const timestamp = new Date().toLocaleTimeString();
console.log(`[VersionChecker ${timestamp}] ${message}`, ...args);
}
}
/**
* 获取检查器状态
*/
getStatus(): {
isRunning: boolean;
isChecking: boolean;
retryCount: number;
lastCheckTime: number;
currentVersion: string;
} {
return {
isRunning: this.timerId !== null,
isChecking: this.isChecking,
retryCount: this.retryCount,
lastCheckTime: this.lastCheckTime,
currentVersion: this.config.currentVersion,
};
}
}
// 初始化版本检查器
(function () {
'use strict';
// 配置将在编译时被替换
const config: VersionCheckConfig = __VERSION_CHECK_CONFIG__;
const versionChecker = new VersionChecker(config);
// 页面加载完成后启动版本检查
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', function () {
versionChecker.start();
});
} else {
versionChecker.start();
}
// 将版本检查器暴露到全局,方便调试
if (typeof window !== 'undefined') {
(window as any).__versionChecker = versionChecker;
}
// 页面卸载时停止检查器
window.addEventListener('beforeunload', function () {
versionChecker.stop();
});
// 页面可见性变化时的处理
document.addEventListener('visibilitychange', function () {
if (document.hidden) {
// 页面隐藏时停止检查
versionChecker.stop();
} else {
// 页面显示时重新启动检查
versionChecker.start();
}
});
})();