1、前言
在 Vue 前端项目打包更新后,我们部署了新的代码,而用户浏览器中的缓存却没有及时更新
没有刷新页面或长时间停留该页面中,还在访问老旧的版本。为了确保用户能够及时获得最新的功能和修复的 bug,我们需要通知用户刷新页面获取最新的代码
2、解决方案
1、前后端使用websocket
配合通知更新
2、纯前端实现,编译打包项目时动态生成一个记录版本号的version.json
文件,通过轮询,将静态文件version.json
中的版本号与缓存中的版本号对比是否一致
本文案例中采用第二种纯前端实现方案,无需依托后端再起一个服务,并且通俗易懂
流程如下:
3、实现步骤
1、封装webpack插件
封装一个webpack插件,当每次执行编译或者打包时
webpack构建流程中都会插入动态生成一个带最新版本标识的文件version.json
apply
方法是所有Webpack插件必须实现的方法,它接收一个compiler
对象作为参数,通过它可以访问和修改Webpack编译过程。在这里,它监听了compiler.hooks.emit
钩子,这是一个异步的emit阶段钩子,在此阶段Webpack将生成并输出所有资产(assets)
js
// build/version.js
class VersionPlugin {
constructor(version) {
this.version = version; // 存储当前版本号
}
apply(compiler) {
// Webpack编译过程 compiler.hooks.emit异步钩子
compiler.hooks.emit.tapAsync("versionPlugin", (compilation, callback) => {
let fileContent = JSON.stringify({
version: this.version
})
// 将此列表作为新的文件资产插入到webpack生成中:
compilation.assets["version.json"] = {
source: function () {
return fileContent
},
size: function () {
return fileContent.length;
},
};
callback();
});
}
}
module.exports = VersionPlugin;
2、引入插件 && 生成唯一hash值版本号
生成唯一hash值 版本号,这里用时间戳代替,如果要严谨一点可以参考如下两种方案
1)获取 Git 仓库当前所指向的最新提交(commit)的哈希值,既能保证唯一性,又能避免每次编译都会被当作更新,以git的提交记录为更新基准减少更新的频繁
js
const cp = require("child_process");
let commitHash = '0'
// 执行一个shell命令,使用git rev-parse --short HEAD来获取当前git仓库的commit哈希值
commitHash = cp.execSync("git rev-parse --short HEAD").toString().trim();
2) 引入uuid库,生成UUID(通用唯一标识符)
js
import { v4 as uuidv4 } from 'uuid';
console.log(uuidv4()); // 生成 UUID 并打印
在vue-cli脚手架配置文件vue.config.js
中引入插件
1、将生成的版本号传入插件类
2、将生成的版本号添加至全局环境变量
js
// vue.config.js
const VersionPlugin = require("./build/version.js");
const version = Date.now() // 生成唯一值,可以改用其他hash值代替,这里用时间戳
module.exports = {
// 部署应用包时的基本 URL
publicPath: "./",
// 是否需要生产环境的 source map(源映射文件,帮助开发者在浏览器的开发者工具中,将错误和日志定位到原始代码的具体位置)
productionSourceMap: false,
// 是否使用包含运行时编译器的 Vue 构建版本
runtimeCompiler: true,
// 是否在开发下通过eslint-loader在每次保存时lint代码, 需要安装@vue/cli-plugin-eslint
lintOnSave: true,
// webpack额外配置
configureWebpack: {
plugins: [
new VersionPlugin(version) // 传入hash版本号
],
},
chainWebpack: config => {
config.plugin('define')
.tap(args => {
// 添加全局环境变量
args[0]['process.env'] = {
...args[0]['process.env'],
version, // 将版本号添加至全局环境变量
};
return args;
});
},
}
3、缓存全局变量
在main.js
入口文件中,将全局环境变量中的版本号缓存至vue原型链上
js
// main.js
import Vue from 'vue'
import App from './App.vue'
const { version } = process.env;
Vue.prototype.$version = version // 将版本号写入vue原型
Vue.config.productionTip = false
new Vue({
render: h => h(App)
}).$mount('#app')
4、轮询监听版本号获取最新版本
在主页面app.vue
中进行请求监听,这里为方便测试轮询间隔为5s
,正式环境中可以延长此时间
打包后的version.js
被一起部署到线上的静态资源服务中,可以直接通过 访问域名+/version.js
即可直接访问,这里引用axios库进行默认的get请求访问
请求成功后将文件中最新的版本号 与当前运行网页中缓存的版本号比较,如果不一致则提示用户刷新网页
html
<!-- app.vue -->
<template>
<div id="app">
</div>
</template>
<script>
import axios from "axios";
export default {
data() {
return {
intervalTime: null, // 定时器
}
},
methods: {
// 检查版本更新
checkVersion(time) {
this.intervalTime = setInterval(() => {
axios({
url: `${window.location.origin}/version.json`, // 当前页面地址+json文件
method: "get",
timeout: 60000,
})
.then(({ status, data }) => {
if (status === 200) {
console.log(data.version, this.$version, "data");
let version = data.version;
let hash = this.$version;
if (hash != version) {
this.$confirm("系统已经更新,请刷新页面?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
}).then(() => {
window.location.reload();
});
}
}
})
.catch(() => {});
}, time);
},
// 监听浏览器页面是否更新
checkVersionHandler() {
const time = 5000; // 刷新时间间隔5s
this.checkVersion(time);
this.$on("hook:beforeDestroy", () => {
clearInterval(this.intervalTime);
this.intervalTime = null;
});
},
},
created() {
// 添加监听浏览器页面是否更新函数
this.checkVersionHandler();
},
}
</script>