vue-sw-updater 0.0.3 → 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 +40 -23
- package/dist/vue-sw-updater.umd.js +4822 -4699
- 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 +2 -3
- package/dist/vue-sw-updater.common.js +0 -15543
- package/dist/vue-sw-updater.common.js.map +0 -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
|
});
|
|
@@ -84,7 +84,8 @@ self.addEventListener('fetch', (event) => {
|
|
|
84
84
|
if (BLACKLIST.some(path => url.pathname.startsWith(path))) return;
|
|
85
85
|
|
|
86
86
|
// HTML 页面:网络优先,失败回退到缓存 (Network First)
|
|
87
|
-
|
|
87
|
+
const accept = event.request.headers.get('accept') || '';
|
|
88
|
+
if (event.request.mode === 'navigate' || accept.includes('text/html')) {
|
|
88
89
|
event.respondWith(
|
|
89
90
|
fetch(event.request)
|
|
90
91
|
.then(response => {
|
|
@@ -103,25 +104,41 @@ self.addEventListener('fetch', (event) => {
|
|
|
103
104
|
return;
|
|
104
105
|
}
|
|
105
106
|
|
|
106
|
-
|
|
107
|
-
|
|
107
|
+
const destination = event.request.destination;
|
|
108
|
+
const pathname = url.pathname || '';
|
|
109
|
+
const isScriptOrStyle =
|
|
110
|
+
destination === 'script' ||
|
|
111
|
+
destination === 'style' ||
|
|
112
|
+
pathname.endsWith('.js') ||
|
|
113
|
+
pathname.endsWith('.css');
|
|
114
|
+
|
|
115
|
+
if (isScriptOrStyle) {
|
|
116
|
+
event.respondWith(
|
|
117
|
+
fetch(event.request)
|
|
118
|
+
.catch(() => caches.match(event.request))
|
|
119
|
+
);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
|
|
108
123
|
event.respondWith(
|
|
109
|
-
caches.match(event.request).then((
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
124
|
+
caches.match(event.request).then((cachedResponse) => {
|
|
125
|
+
const fetchAndUpdate = fetch(event.request)
|
|
126
|
+
.then(networkResponse => {
|
|
127
|
+
if (networkResponse && networkResponse.status === 200 && networkResponse.type === 'basic') {
|
|
128
|
+
const responseToCache = networkResponse.clone();
|
|
129
|
+
caches.open(CACHE_NAME).then(cache => {
|
|
130
|
+
cache.put(event.request, responseToCache);
|
|
131
|
+
});
|
|
132
|
+
}
|
|
116
133
|
return networkResponse;
|
|
117
|
-
}
|
|
118
|
-
const responseToCache = networkResponse.clone();
|
|
119
|
-
caches.open(CACHE_NAME).then(cache => {
|
|
120
|
-
// 避免缓存过大文件或不必要文件,这里可以加过滤逻辑
|
|
121
|
-
cache.put(event.request, responseToCache);
|
|
122
134
|
});
|
|
123
|
-
|
|
124
|
-
|
|
135
|
+
|
|
136
|
+
if (cachedResponse) {
|
|
137
|
+
event.waitUntil(fetchAndUpdate.catch(() => {}));
|
|
138
|
+
return cachedResponse;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return fetchAndUpdate;
|
|
125
142
|
})
|
|
126
143
|
);
|
|
127
144
|
});
|