vue-sw-updater 0.0.4 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +142 -106
- package/dist/service-worker.js +6 -6
- package/dist/vue-sw-updater.umd.js +173 -50
- package/dist/vue-sw-updater.umd.js.map +1 -1
- package/dist/vue-sw-updater.umd.min.js +3 -3
- package/dist/vue-sw-updater.umd.min.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,106 +1,142 @@
|
|
|
1
|
-
# Vue Service Worker Updater
|
|
2
|
-
|
|
3
|
-
一个基于 Vue 2 和 Ant Design Vue
|
|
4
|
-
|
|
5
|
-
## 功能特性
|
|
6
|
-
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
npm install
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
import '
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
1
|
+
# Vue Service Worker Updater
|
|
2
|
+
|
|
3
|
+
一个基于 Vue 2 和 Ant Design Vue 的项目更新通知与缓存清理方案。支持 Service Worker 自动更新和 HTTP 轮询检测更新双重机制,确保在 HTTPS 和 HTTP 环境下均能可靠运行。
|
|
4
|
+
|
|
5
|
+
## 功能特性
|
|
6
|
+
|
|
7
|
+
* **双模更新检测**:
|
|
8
|
+
* **Service Worker 模式**: 在 HTTPS 环境下,利用 SW 自动感知应用更新,支持离线缓存和增量更新。
|
|
9
|
+
* **HTTP 轮询模式**: 在 HTTP 环境或不支持 SW 的浏览器中,自动降级为 ETag / Last-Modified 轮询检测。
|
|
10
|
+
* **零侵入集成**: 核心逻辑封装在库内,无需手动编写复杂的 SW 或轮询代码。
|
|
11
|
+
* **多环境隔离**: 支持 `appName` 和 `env` 参数,生成独立的缓存空间,避免多应用或多环境缓存冲突。
|
|
12
|
+
* **全局提示**: 使用 Ant Design Vue Modal 全局弹窗提示用户,样式醒目(警告图标+加粗文字)。
|
|
13
|
+
* **强制刷新**: 用户点击“立即更新”后,自动清理当前应用缓存并重载页面,确保加载最新资源。
|
|
14
|
+
* **环境自适应**: 自动检测环境安全性(HTTPS/localhost),如果环境不安全且配置了轮询参数,自动切换至轮询模式;否则显示环境不支持提示。
|
|
15
|
+
|
|
16
|
+
## 安装
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install vue-sw-updater --save
|
|
20
|
+
# 确保项目中已安装 ant-design-vue 和 vue
|
|
21
|
+
npm install ant-design-vue vue --save
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## 快速使用
|
|
25
|
+
|
|
26
|
+
### 1. 引入插件
|
|
27
|
+
|
|
28
|
+
在你的 `main.js` 或入口文件中:
|
|
29
|
+
|
|
30
|
+
```javascript
|
|
31
|
+
import Vue from 'vue';
|
|
32
|
+
import VueSWUpdater from 'vue-sw-updater';
|
|
33
|
+
import 'ant-design-vue/dist/antd.css';
|
|
34
|
+
|
|
35
|
+
// 从你的配置或环境变量中获取信息
|
|
36
|
+
const version = process.env.VUE_APP_VERSION || '1.0.0';
|
|
37
|
+
const appName = process.env.VUE_APP_NAME || 'my-app';
|
|
38
|
+
const env = process.env.NODE_ENV || 'production';
|
|
39
|
+
|
|
40
|
+
Vue.use(VueSWUpdater, {
|
|
41
|
+
// [必填] 应用版本号 (当前运行的版本)
|
|
42
|
+
version: version,
|
|
43
|
+
|
|
44
|
+
// [必填] 应用名称(用于 SW 缓存隔离)
|
|
45
|
+
appName: appName,
|
|
46
|
+
|
|
47
|
+
// [必填] 部署环境(用于 SW 缓存隔离)
|
|
48
|
+
env: env,
|
|
49
|
+
|
|
50
|
+
// [可选] Service Worker 文件路径,默认为 /service-worker.js
|
|
51
|
+
swUrl: '/service-worker.js',
|
|
52
|
+
|
|
53
|
+
// [可选] 轮询检测间隔(分钟)。
|
|
54
|
+
// 如果设置为 > 0,当 SW 不可用(如 HTTP 环境)时,将启用轮询模式。
|
|
55
|
+
// 建议设置为 10 或 30。
|
|
56
|
+
pollingInterval: 10,
|
|
57
|
+
|
|
58
|
+
// [可选] 检测更新的目标 URL (仅轮询模式使用)
|
|
59
|
+
// 默认为空字符串 '',即检测当前页面 (index.html) 的 Header 变化
|
|
60
|
+
checkUrl: '',
|
|
61
|
+
|
|
62
|
+
// [可选] 调试模式,开启后控制台会输出更多日志
|
|
63
|
+
debug: false
|
|
64
|
+
});
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### 2. 构建配置 (针对不同模式)
|
|
68
|
+
|
|
69
|
+
#### A. 准备 Service Worker (HTTPS 模式推荐)
|
|
70
|
+
|
|
71
|
+
本库提供了一个通用的 `service-worker.js`,你需要将其包含在你的项目根目录(`public` 目录)中。
|
|
72
|
+
|
|
73
|
+
**Webpack / Vue CLI 配置:**
|
|
74
|
+
|
|
75
|
+
```javascript
|
|
76
|
+
// vue.config.js
|
|
77
|
+
const CopyWebpackPlugin = require('copy-webpack-plugin');
|
|
78
|
+
const path = require('path');
|
|
79
|
+
|
|
80
|
+
module.exports = {
|
|
81
|
+
configureWebpack: {
|
|
82
|
+
plugins: [
|
|
83
|
+
new CopyWebpackPlugin({
|
|
84
|
+
patterns: [
|
|
85
|
+
{
|
|
86
|
+
from: path.resolve(__dirname, 'node_modules/vue-sw-updater/dist/service-worker.js'),
|
|
87
|
+
to: 'service-worker.js' // 输出到 dist 根目录
|
|
88
|
+
}
|
|
89
|
+
]
|
|
90
|
+
})
|
|
91
|
+
]
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
#### B. HTTP 轮询模式配置
|
|
97
|
+
|
|
98
|
+
无需额外文件。插件会自动轮询当前页面(或指定的 `checkUrl`)的 HTTP 响应头:
|
|
99
|
+
* **ETag**: 资源唯一标识
|
|
100
|
+
* **Last-Modified**: 资源最后修改时间
|
|
101
|
+
|
|
102
|
+
**注意**: 请确保你的 Web 服务器(Nginx/Apache/IIS)已配置为返回这些 Header。对于 SPA 应用的入口文件 (`index.html`),通常默认都会返回。
|
|
103
|
+
|
|
104
|
+
## 运行机制
|
|
105
|
+
|
|
106
|
+
### 自动模式选择
|
|
107
|
+
|
|
108
|
+
插件初始化时会执行以下判断:
|
|
109
|
+
|
|
110
|
+
1. **检查 Service Worker 支持**:
|
|
111
|
+
* 如果是 HTTPS 或 localhost,且浏览器支持 SW -> **启用 SW 模式**。
|
|
112
|
+
* 如果是 HTTP (非 localhost) 或浏览器不支持 SW -> **进入降级判断**。
|
|
113
|
+
|
|
114
|
+
2. **降级判断**:
|
|
115
|
+
* 如果配置了 `pollingInterval > 0` -> **启用 HTTP 轮询模式**。
|
|
116
|
+
* 如果没有配置轮询 -> **显示“环境不支持”错误提示** (BrowserError Modal)。
|
|
117
|
+
|
|
118
|
+
### SW 模式工作流
|
|
119
|
+
|
|
120
|
+
1. 插件注册 `service-worker.js?v=1.0.0&app=my-app...`。
|
|
121
|
+
2. 浏览器发现 URL 变化,下载新 SW。
|
|
122
|
+
3. 新 SW 安装并等待激活。
|
|
123
|
+
4. 插件检测到 `waiting` 状态,弹出更新提示框。
|
|
124
|
+
5. 用户点击更新 -> 清理缓存 -> 强制刷新。
|
|
125
|
+
|
|
126
|
+
### HTTP 轮询模式工作流
|
|
127
|
+
|
|
128
|
+
1. 插件启动定时器 (例如每 10 分钟)。
|
|
129
|
+
2. 定时发送 `HEAD` 请求检测目标 URL (默认当前页面)。
|
|
130
|
+
3. 对比响应头中的 `ETag` 或 `Last-Modified` 与页面加载时是否一致。
|
|
131
|
+
4. 如果发现 Header 变更,视为有新版本发布,弹出更新提示框。
|
|
132
|
+
5. 用户点击更新 -> 强制刷新 (`window.location.reload(true)`).
|
|
133
|
+
|
|
134
|
+
## 浏览器支持
|
|
135
|
+
|
|
136
|
+
插件会自动处理兼容性,最低要求:
|
|
137
|
+
* **Edge**: 17+
|
|
138
|
+
* **Chrome**: 45+
|
|
139
|
+
* **Firefox**: 44+
|
|
140
|
+
* **Safari**: 11.1+
|
|
141
|
+
|
|
142
|
+
如果浏览器版本过低,无论哪种模式都会显示阻断性提示。
|
package/dist/service-worker.js
CHANGED
|
@@ -22,7 +22,7 @@ const BLACKLIST = [
|
|
|
22
22
|
console.log(`[VueSWUpdater] Service Worker 初始化. App: ${APP_NAME}, Env: ${ENV}, Version: ${VERSION}, Cache: ${CACHE_NAME}`);
|
|
23
23
|
|
|
24
24
|
self.addEventListener('install', (event) => {
|
|
25
|
-
console.log('[VueSWUpdater]
|
|
25
|
+
console.log('[VueSWUpdater] 正在安装...');
|
|
26
26
|
event.waitUntil(
|
|
27
27
|
caches.open(CACHE_NAME).then((cache) => {
|
|
28
28
|
// 仅缓存核心入口文件,其他资源依赖运行时缓存
|
|
@@ -30,7 +30,7 @@ self.addEventListener('install', (event) => {
|
|
|
30
30
|
'./',
|
|
31
31
|
'./index.html'
|
|
32
32
|
]).catch(err => {
|
|
33
|
-
console.warn('[VueSWUpdater]
|
|
33
|
+
console.warn('[VueSWUpdater] 预缓存失败:', err);
|
|
34
34
|
});
|
|
35
35
|
})
|
|
36
36
|
);
|
|
@@ -38,14 +38,14 @@ self.addEventListener('install', (event) => {
|
|
|
38
38
|
});
|
|
39
39
|
|
|
40
40
|
self.addEventListener('activate', (event) => {
|
|
41
|
-
console.log('[VueSWUpdater]
|
|
41
|
+
console.log('[VueSWUpdater] 正在激活...');
|
|
42
42
|
event.waitUntil(
|
|
43
43
|
caches.keys().then((keyList) => {
|
|
44
44
|
return Promise.all(keyList.map((key) => {
|
|
45
45
|
// 清理旧版本缓存 (只清理本插件前缀的缓存)
|
|
46
46
|
// 逻辑:如果是同一个 App 和 Env,但是版本不同,则清理
|
|
47
47
|
if (key.startsWith(CACHE_PREFIX) && key !== CACHE_NAME) {
|
|
48
|
-
console.log('[VueSWUpdater]
|
|
48
|
+
console.log('[VueSWUpdater] 移除旧缓存:', key);
|
|
49
49
|
return caches.delete(key);
|
|
50
50
|
}
|
|
51
51
|
}));
|
|
@@ -57,7 +57,7 @@ self.addEventListener('activate', (event) => {
|
|
|
57
57
|
// 监听 skipWaiting 消息(由 UI 组件触发)
|
|
58
58
|
self.addEventListener('message', (event) => {
|
|
59
59
|
if (event.data && event.data.type === 'SKIP_WAITING') {
|
|
60
|
-
console.log('[VueSWUpdater] Skip Waiting
|
|
60
|
+
console.log('[VueSWUpdater] 收到 Skip Waiting 指令');
|
|
61
61
|
|
|
62
62
|
// 强制清理所有缓存(不仅仅是旧版本,确保彻底)
|
|
63
63
|
// 注意:这里只清理当前 APP_NAME 和 ENV 下的缓存,防止误删其他应用缓存
|
|
@@ -65,7 +65,7 @@ self.addEventListener('message', (event) => {
|
|
|
65
65
|
caches.keys().then(keys => {
|
|
66
66
|
keys.forEach(key => {
|
|
67
67
|
if (key.startsWith(CACHE_PREFIX)) {
|
|
68
|
-
console.log('[VueSWUpdater]
|
|
68
|
+
console.log('[VueSWUpdater] 更新前强制清理缓存:', key);
|
|
69
69
|
caches.delete(key);
|
|
70
70
|
}
|
|
71
71
|
});
|
|
@@ -9842,6 +9842,7 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
9842
9842
|
// EXPORTS
|
|
9843
9843
|
__webpack_require__.d(__webpack_exports__, {
|
|
9844
9844
|
BrowserError: function() { return /* reexport */ BrowserError; },
|
|
9845
|
+
PollingUpdater: function() { return /* reexport */ PollingUpdater; },
|
|
9845
9846
|
ServiceWorkerUpdater: function() { return /* reexport */ ServiceWorkerUpdater; },
|
|
9846
9847
|
UpdateModal: function() { return /* reexport */ UpdateModal; },
|
|
9847
9848
|
checkBrowserSupport: function() { return /* reexport */ checkBrowserSupport; },
|
|
@@ -10038,7 +10039,7 @@ class ServiceWorkerUpdater {
|
|
|
10038
10039
|
updated: registration => {
|
|
10039
10040
|
console.log('新内容可用,请刷新。');
|
|
10040
10041
|
this.registration = registration;
|
|
10041
|
-
//
|
|
10042
|
+
// 触发更新回调 (显示 UI)
|
|
10042
10043
|
this.onUpdateFound(this);
|
|
10043
10044
|
},
|
|
10044
10045
|
offline: () => {
|
|
@@ -10050,7 +10051,7 @@ class ServiceWorkerUpdater {
|
|
|
10050
10051
|
}
|
|
10051
10052
|
});
|
|
10052
10053
|
|
|
10053
|
-
//
|
|
10054
|
+
// 处理 controller 变更 (刷新页面)
|
|
10054
10055
|
let refreshing = false;
|
|
10055
10056
|
if (navigator.serviceWorker) {
|
|
10056
10057
|
navigator.serviceWorker.addEventListener('controllerchange', () => {
|
|
@@ -10066,7 +10067,7 @@ class ServiceWorkerUpdater {
|
|
|
10066
10067
|
}
|
|
10067
10068
|
}
|
|
10068
10069
|
|
|
10069
|
-
//
|
|
10070
|
+
// 当用户接受更新时调用
|
|
10070
10071
|
applyUpdate() {
|
|
10071
10072
|
if (!this.registration || !this.registration.waiting) {
|
|
10072
10073
|
console.warn('未找到等待中的 Service Worker。');
|
|
@@ -10074,7 +10075,7 @@ class ServiceWorkerUpdater {
|
|
|
10074
10075
|
window.location.reload();
|
|
10075
10076
|
return;
|
|
10076
10077
|
}
|
|
10077
|
-
//
|
|
10078
|
+
// 发送消息给 SW 以跳过等待
|
|
10078
10079
|
// 这会触发 SW 中的 message 监听,执行 self.skipWaiting()
|
|
10079
10080
|
// 进而触发 controllerchange,最终执行上面的 window.location.reload()
|
|
10080
10081
|
this.registration.waiting.postMessage({
|
|
@@ -10082,6 +10083,90 @@ class ServiceWorkerUpdater {
|
|
|
10082
10083
|
});
|
|
10083
10084
|
}
|
|
10084
10085
|
}
|
|
10086
|
+
;// ./src/lib/utils/PollingUpdater.js
|
|
10087
|
+
class PollingUpdater {
|
|
10088
|
+
constructor(options = {}) {
|
|
10089
|
+
this.checkUrl = options.checkUrl || ''; // 默认为当前页面
|
|
10090
|
+
this.intervalMinutes = options.interval || 0; // 0 表示禁用
|
|
10091
|
+
this.onUpdateFound = options.onUpdateFound || (() => {});
|
|
10092
|
+
this.onError = options.onError || (() => {});
|
|
10093
|
+
this.timer = null;
|
|
10094
|
+
|
|
10095
|
+
// 记录 ETag/Last-Modified 状态
|
|
10096
|
+
this.lastEtag = null;
|
|
10097
|
+
this.lastModified = null;
|
|
10098
|
+
}
|
|
10099
|
+
init() {
|
|
10100
|
+
if (this.intervalMinutes <= 0) {
|
|
10101
|
+
console.log('[VueSWUpdater] 轮询时间未设置或为0,不启用轮询。');
|
|
10102
|
+
return;
|
|
10103
|
+
}
|
|
10104
|
+
console.log(`[VueSWUpdater] 启用 HTTP ETag/Last-Modified 检测,轮询间隔: ${this.intervalMinutes} 分钟`);
|
|
10105
|
+
|
|
10106
|
+
// 执行初始检测以建立基准
|
|
10107
|
+
this.checkUpdate(true);
|
|
10108
|
+
this.startPolling();
|
|
10109
|
+
}
|
|
10110
|
+
startPolling() {
|
|
10111
|
+
const ms = this.intervalMinutes * 60 * 1000;
|
|
10112
|
+
this.timer = setInterval(() => {
|
|
10113
|
+
this.checkUpdate(false);
|
|
10114
|
+
}, ms);
|
|
10115
|
+
}
|
|
10116
|
+
checkUpdate(isInit = false) {
|
|
10117
|
+
// 添加时间戳防止缓存
|
|
10118
|
+
// 处理 checkUrl 已包含查询参数的情况
|
|
10119
|
+
const separator = this.checkUrl.includes('?') ? '&' : '?';
|
|
10120
|
+
// 使用 'check_t' 避免与可能存在的 't' 参数冲突
|
|
10121
|
+
const url = `${this.checkUrl}${separator}check_t=${Date.now()}`;
|
|
10122
|
+
fetch(url, {
|
|
10123
|
+
method: 'HEAD',
|
|
10124
|
+
cache: 'no-cache'
|
|
10125
|
+
}).then(response => {
|
|
10126
|
+
if (!response.ok) {
|
|
10127
|
+
// 如果网络错误或 404,跳过此次检测
|
|
10128
|
+
return;
|
|
10129
|
+
}
|
|
10130
|
+
const etag = response.headers.get('ETag');
|
|
10131
|
+
const lastModified = response.headers.get('Last-Modified');
|
|
10132
|
+
if (isInit) {
|
|
10133
|
+
this.lastEtag = etag;
|
|
10134
|
+
this.lastModified = lastModified;
|
|
10135
|
+
// console.log(`[VueSWUpdater] 初始版本信息 - ETag: ${etag}, Last-Modified: ${lastModified}`);
|
|
10136
|
+
} else {
|
|
10137
|
+
let changed = false;
|
|
10138
|
+
|
|
10139
|
+
// 检查 ETag
|
|
10140
|
+
if (etag && this.lastEtag && etag !== this.lastEtag) {
|
|
10141
|
+
changed = true;
|
|
10142
|
+
console.log(`[VueSWUpdater] 检测到更新 (ETag变化): ${this.lastEtag} -> ${etag}`);
|
|
10143
|
+
}
|
|
10144
|
+
// 检查 Last-Modified (作为后备或补充)
|
|
10145
|
+
else if (lastModified && this.lastModified && lastModified !== this.lastModified) {
|
|
10146
|
+
changed = true;
|
|
10147
|
+
console.log(`[VueSWUpdater] 检测到更新 (Last-Modified变化): ${this.lastModified} -> ${lastModified}`);
|
|
10148
|
+
}
|
|
10149
|
+
if (changed) {
|
|
10150
|
+
this.stopPolling(); // 一旦发现更新,停止轮询
|
|
10151
|
+
this.onUpdateFound(this);
|
|
10152
|
+
}
|
|
10153
|
+
}
|
|
10154
|
+
}).catch(err => {
|
|
10155
|
+
console.error('[VueSWUpdater] 版本检测失败:', err);
|
|
10156
|
+
});
|
|
10157
|
+
}
|
|
10158
|
+
stopPolling() {
|
|
10159
|
+
if (this.timer) {
|
|
10160
|
+
clearInterval(this.timer);
|
|
10161
|
+
this.timer = null;
|
|
10162
|
+
}
|
|
10163
|
+
}
|
|
10164
|
+
applyUpdate() {
|
|
10165
|
+
console.log('[VueSWUpdater] 正在刷新页面以应用更新...');
|
|
10166
|
+
// 强制从服务器重新加载以获取新的 index.html 和资源
|
|
10167
|
+
window.location.reload(true);
|
|
10168
|
+
}
|
|
10169
|
+
}
|
|
10085
10170
|
// EXTERNAL MODULE: ./node_modules/ua-parser-js/src/ua-parser.js
|
|
10086
10171
|
var ua_parser = __webpack_require__(5135);
|
|
10087
10172
|
var ua_parser_default = /*#__PURE__*/__webpack_require__.n(ua_parser);
|
|
@@ -10089,7 +10174,7 @@ var ua_parser_default = /*#__PURE__*/__webpack_require__.n(ua_parser);
|
|
|
10089
10174
|
|
|
10090
10175
|
const MIN_VERSIONS = {
|
|
10091
10176
|
Chrome: 45,
|
|
10092
|
-
// Service Worker
|
|
10177
|
+
// Service Worker 自 40 起支持,但建议 45+
|
|
10093
10178
|
Firefox: 44,
|
|
10094
10179
|
Safari: 11,
|
|
10095
10180
|
// Safari 11.1+
|
|
@@ -10108,22 +10193,29 @@ function checkBrowserSupport() {
|
|
|
10108
10193
|
browserVersion: browser.version
|
|
10109
10194
|
};
|
|
10110
10195
|
|
|
10111
|
-
// 1.
|
|
10196
|
+
// 1. 检查 Service Worker API
|
|
10112
10197
|
if (!('serviceWorker' in navigator)) {
|
|
10113
10198
|
result.supported = false;
|
|
10114
|
-
|
|
10199
|
+
// 检查是否由于不安全上下文导致
|
|
10200
|
+
const isLocalhost = window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1';
|
|
10201
|
+
const isHttps = window.location.protocol === 'https:';
|
|
10202
|
+
if (!isLocalhost && !isHttps) {
|
|
10203
|
+
result.message = 'Service Worker 需要 HTTPS 环境或 localhost。请检查您的访问地址是否安全。';
|
|
10204
|
+
} else {
|
|
10205
|
+
result.message = '当前浏览器不支持 Service Worker API。请尝试升级浏览器或检查浏览器设置。';
|
|
10206
|
+
}
|
|
10115
10207
|
return result;
|
|
10116
10208
|
}
|
|
10117
10209
|
|
|
10118
|
-
// 2.
|
|
10210
|
+
// 2. 检查版本
|
|
10119
10211
|
if (MIN_VERSIONS[name]) {
|
|
10120
10212
|
if (version < MIN_VERSIONS[name]) {
|
|
10121
10213
|
result.supported = false;
|
|
10122
10214
|
result.message = `浏览器版本过低。${name} ${version} < 需要 ${MIN_VERSIONS[name]}+`;
|
|
10123
10215
|
}
|
|
10124
10216
|
} else {
|
|
10125
|
-
//
|
|
10126
|
-
//
|
|
10217
|
+
// 未知浏览器,但支持 SW (通过了步骤 1)
|
|
10218
|
+
// 我们假设它是可用的。如果存在 SW API,则通过。
|
|
10127
10219
|
console.warn('检测到未知浏览器,但存在 Service Worker API。');
|
|
10128
10220
|
}
|
|
10129
10221
|
return result;
|
|
@@ -15479,55 +15571,86 @@ var BrowserError_component = normalizeComponent(
|
|
|
15479
15571
|
|
|
15480
15572
|
|
|
15481
15573
|
|
|
15574
|
+
|
|
15482
15575
|
// 导出单独的组件和工具,以便高级用户按需使用
|
|
15483
15576
|
|
|
15484
15577
|
const Plugin = {
|
|
15485
15578
|
install(Vue, options = {}) {
|
|
15486
|
-
// 1.
|
|
15579
|
+
// 1. 浏览器环境检测
|
|
15487
15580
|
const checkResult = checkBrowserSupport();
|
|
15581
|
+
let usePolling = false;
|
|
15582
|
+
|
|
15583
|
+
// 检查是否需要降级到轮询模式
|
|
15584
|
+
// 降级条件:不支持 SW 且设置了轮询间隔
|
|
15488
15585
|
if (!checkResult.supported) {
|
|
15489
|
-
|
|
15490
|
-
|
|
15491
|
-
|
|
15492
|
-
|
|
15493
|
-
|
|
15494
|
-
|
|
15495
|
-
|
|
15496
|
-
|
|
15497
|
-
errorInstance
|
|
15498
|
-
|
|
15499
|
-
|
|
15586
|
+
if (options.pollingInterval && options.pollingInterval > 0) {
|
|
15587
|
+
usePolling = true;
|
|
15588
|
+
// 如果通过轮询处理,则不在控制台显示错误信息
|
|
15589
|
+
console.warn('[VueSWUpdater] Service Worker 不可用 (' + checkResult.message + '),切换到 HTTP 轮询模式。');
|
|
15590
|
+
} else {
|
|
15591
|
+
// 挂载 BrowserError 组件
|
|
15592
|
+
const ErrorConstructor = Vue.extend(BrowserError);
|
|
15593
|
+
const errorInstance = new ErrorConstructor();
|
|
15594
|
+
errorInstance.$mount();
|
|
15595
|
+
document.body.appendChild(errorInstance.$el);
|
|
15596
|
+
|
|
15597
|
+
// 稍作延迟以确保挂载完成
|
|
15598
|
+
Vue.nextTick(() => {
|
|
15599
|
+
errorInstance.show(checkResult);
|
|
15600
|
+
});
|
|
15601
|
+
return; // 停止初始化
|
|
15602
|
+
}
|
|
15500
15603
|
}
|
|
15501
15604
|
|
|
15502
|
-
// 2.
|
|
15605
|
+
// 2. 准备更新弹窗实例
|
|
15503
15606
|
let modalInstance = null;
|
|
15504
|
-
|
|
15505
|
-
|
|
15506
|
-
//
|
|
15507
|
-
|
|
15508
|
-
|
|
15509
|
-
|
|
15510
|
-
|
|
15511
|
-
|
|
15512
|
-
|
|
15513
|
-
|
|
15514
|
-
|
|
15515
|
-
|
|
15516
|
-
|
|
15517
|
-
|
|
15518
|
-
|
|
15519
|
-
|
|
15520
|
-
|
|
15607
|
+
let updater = null;
|
|
15608
|
+
|
|
15609
|
+
// 3. 初始化更新器
|
|
15610
|
+
if (usePolling) {
|
|
15611
|
+
updater = new PollingUpdater({
|
|
15612
|
+
version: options.version,
|
|
15613
|
+
checkUrl: options.checkUrl,
|
|
15614
|
+
interval: options.pollingInterval,
|
|
15615
|
+
onUpdateFound: () => {
|
|
15616
|
+
if (modalInstance) {
|
|
15617
|
+
modalInstance.show();
|
|
15618
|
+
}
|
|
15619
|
+
},
|
|
15620
|
+
onError: err => {
|
|
15621
|
+
if (options.debug) {
|
|
15622
|
+
console.error('[VueSWUpdater] 轮询错误:', err);
|
|
15623
|
+
}
|
|
15521
15624
|
}
|
|
15522
|
-
}
|
|
15523
|
-
|
|
15524
|
-
|
|
15525
|
-
|
|
15625
|
+
});
|
|
15626
|
+
} else {
|
|
15627
|
+
// Service Worker Updater
|
|
15628
|
+
// 构造带参数的 SW URL
|
|
15629
|
+
let swUrl = options.swUrl || '/service-worker.js';
|
|
15630
|
+
const params = new URLSearchParams();
|
|
15631
|
+
if (options.version) params.append('v', options.version);
|
|
15632
|
+
if (options.appName) params.append('app', options.appName);
|
|
15633
|
+
if (options.env) params.append('env', options.env);
|
|
15634
|
+
const queryString = params.toString();
|
|
15635
|
+
if (queryString) {
|
|
15636
|
+
swUrl += (swUrl.indexOf('?') === -1 ? '?' : '&') + queryString;
|
|
15637
|
+
}
|
|
15638
|
+
updater = new ServiceWorkerUpdater({
|
|
15639
|
+
swUrl: swUrl,
|
|
15640
|
+
onUpdateFound: () => {
|
|
15641
|
+
if (modalInstance) {
|
|
15642
|
+
modalInstance.show();
|
|
15643
|
+
}
|
|
15644
|
+
},
|
|
15645
|
+
onError: err => {
|
|
15646
|
+
if (options.debug) {
|
|
15647
|
+
console.error('[VueSWUpdater] SW 注册错误:', err);
|
|
15648
|
+
}
|
|
15526
15649
|
}
|
|
15527
|
-
}
|
|
15528
|
-
}
|
|
15650
|
+
});
|
|
15651
|
+
}
|
|
15529
15652
|
|
|
15530
|
-
// 4.
|
|
15653
|
+
// 4. 挂载更新弹窗组件
|
|
15531
15654
|
const ModalConstructor = Vue.extend(UpdateModal);
|
|
15532
15655
|
modalInstance = new ModalConstructor({
|
|
15533
15656
|
propsData: {
|
|
@@ -15537,13 +15660,13 @@ const Plugin = {
|
|
|
15537
15660
|
modalInstance.$mount();
|
|
15538
15661
|
document.body.appendChild(modalInstance.$el);
|
|
15539
15662
|
|
|
15540
|
-
// 5.
|
|
15541
|
-
//
|
|
15663
|
+
// 5. 启动更新器
|
|
15664
|
+
// 等待页面加载完成,避免影响首屏性能
|
|
15542
15665
|
window.addEventListener('load', () => {
|
|
15543
15666
|
updater.init();
|
|
15544
15667
|
});
|
|
15545
15668
|
|
|
15546
|
-
// 6.
|
|
15669
|
+
// 6. 挂载到 Vue 原型
|
|
15547
15670
|
Vue.prototype.$swUpdater = updater;
|
|
15548
15671
|
}
|
|
15549
15672
|
};
|