背景

上篇总结了如何实现网站更新的功能,现在还有另外的一种做法pwa技术实现;

创建项目

vite+ts+vue创建一个空项目

1
pnpm create vite vite-pwa-demo --template vue-ts

安装插件

安装完成之后,需要再次安装pwa插件

1
pnpm add -D vite-plugin-pwa

配置插件

完成安装之后,需要改一下vite.config.ts文件,添加pwa插件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import { VitePWA } from "vite-plugin-pwa";

export default defineConfig({
plugins: [
vue(),
VitePWA({
// prompt 弹框模式。 autoupdate 自动更新模式
registerType: "prompt",
devOptions: {
enabled: true,
},
workbox: {
cleanupOutdatedCaches: true,
},
manifest: {},
}),
],
server: {
port: 5177,
},
});

接着需要在vite-env.d.ts中添加对应的服务:

1
2
3
4
5
6
7
8
9
10
11
12
declare module "virtual:pwa-register/vue" {
import type { Ref } from "vue";
import type { RegisterSWOptions } from "vite-plugin-pwa/types";

export type { RegisterSWOptions };

export function useRegisterSW(options?: RegisterSWOptions): {
needRefresh: Ref<boolean>;
offlineReady: Ref<boolean>;
updateServiceWorker: (reloadPage?: boolean) => Promise<void>;
};
}

接着在tsconfig.json中配置

1
2
3
4
5
6
{
"compilerOptions": {
// 添加这一行
"types": ["vite-plugin-pwa/client", "vite-plugin-pwa/vue", "vite/client"]
}
}

更新检测

1
2
3
4
5
6
7
8
9
10
11
12
// 核心代码
import { useRegisterSW } from "virtual:pwa-register/vue";
const { offlineReady, needRefresh, updateServiceWorker } = useRegisterSW({
// 每隔3s检查一次是否有新版本
onRegistered(r) {
if (r) {
setInterval(() => {
r.update();
}, 1000 * 3);
}
},
});

编写更新弹框

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
<script setup lang="ts">
import { useRegisterSW } from "virtual:pwa-register/vue";
import { ref, watch } from "vue";
import { ElMessage } from "element-plus";

const { offlineReady, needRefresh, updateServiceWorker } = useRegisterSW({
onRegistered(r) {
if (r && !check.value) {
setInterval(() => {
r.update();
}, 1000 * 3);
}
},
});

// 如果是强制更新,只需要needRefresh.value 即可;

// 用另外的变量控制弹框的显示
const check = ref(needRefresh.value);

const loading = ref(false);
const btnText = ref("立即更新");
// 更新
const updataPage = async () => {
loading.value = true;
btnText.value = "正在更新中...";
await updateServiceWorker();
};

// 关闭弹框
const close = () => {
check.value = false;
loading.value = false;
btnText.value = "立即更新";
};
// 更新的时候禁止关闭
const beforeClose = (done: any) => {
if (!loading.value) {
return done();
}
ElMessage({
message: "正在进行更新, 请稍等...",
type: "warning",
});
};
// 没3s会更新needRefresh.value的状态,所以这里监听一下即可;
watch(
() => needRefresh.value,
val => {
check.value = val;
}
);
</script>

<template>
<div>======11111213+++---</div>
<el-dialog
v-model="check"
append-to-body
class="w-update"
draggable
width="410px"
:before-close="beforeClose"
>
<template #header></template>
<div class="w-update-icon"></div>
<h3>新版本来袭:</h3>
<p>更新时间:最近更新</p>

<p>更新内容:更新的内容呢还请详看上线邮件~</p>
<p style="color: #ff0000; font-size: 12px">
如果您暂时不想更新(如:您正在操作内容),点击关闭按钮可关闭,后续想更新只需要刷新页面进行更新即可~如果想使用新功能,请点击更新按钮即可
</p>
<template #footer>
<el-button type="primary" @click="updataPage" :loading="loading">
{{ btnText }}
</el-button>
<el-button type="default" :loading="loading" @click="close">
暂时不更新
</el-button>
</template>
</el-dialog>
</template>

<style scoped>
.w-update {
position: relative;
}
.w-update-icon {
position: absolute;
top: -50px;
left: 50%;
width: 100px;
height: 100px;
line-height: 100px;
text-align: center;
background: linear-gradient(
50deg,
var(--el-color-primary),
var(--el-color-primary-light-7)
);
border-radius: 50%;
transform: translateX(-50%);
}
.w-update-icon i {
font-size: 50px;
color: var(--el-color-white);
}

.w-update-cup {
position: absolute;
right: 20px;
bottom: 70px;
font-size: 80px;
-webkit-text-fill-color: transparent;
background-image: linear-gradient(
var(--el-color-primary-light-7),
var(--el-color-primary-light-9)
);
background-clip: text;
}
</style>

<style>
.w-update.el-dialog {
margin-top: 30vh !important;
border-radius: 15px;
}

.w-update .el-dialog__body {
margin: 0 40px 0 40px;
}

.w-update .el-dialog__footer {
text-align: center !important;
}
.w-update .el-button {
margin-bottom: 20px;
border-radius: 20px;
}
</style>

测试

测试的话,可以上传服务器或者本地nginx测试;

项目执行pnpm run build将生成dist文件夹,将dist文件夹上传到服务器或者nginx的html文件夹中即可;

然后等待页面弹框即可;