本人vue3+vite项目
背景
- 前端项目版本投产后如果用户没有及时的拿到最新投产的资源包,可能会存在以下问题:
- 缺少新功能或修复:如果最新的资源包含新功能或修复了现有的问题,但用户没有及时获取到它们,那么用户将无法及时的体验到这些新功能或修复的好处。
- 安全问题:新版本的资源可能包含了安全性修复,修复了已知的漏洞或安全风险。如果用户没有及时的获取到这些修复,那么网站可能存在潜在的安全风险,容易受到攻击。
- 程序报错:如果最新投产的版本包含了一些配合后端接口数据改造,比如后端接口数据结构或者字段发生了改变,但是前端静态资源JS没有获取到最新的,可能会导致程序报错,会严重影响到用户体验。
解决方案
- 轮询检测版本更新
- 使用服务端推送技术(Server-Sent Events,SSE)
- 使用WebSocket实时通信
这三种实现方案都需要后端配合,下面介绍不需要后端配合,纯前端实现
实现原理
使用nodejs脚本生成版本信息json文件 + 监听页面显示和隐藏会触发的
visibilitychange事件,纯前端实现版本投产自动刷新浏览器更新版本内容,以下具体细节:
- 使用nodejs编写脚本,获取git版本相关信息(必须包含git commitId,用于版本对比),并保存为json文件,存放在构建打包的目录下(比如,public目录)。
- 使用页面显示和隐藏会触发的
visibilitychange事件,监听页面的显示和隐藏操作,如果页面显示,则请求打包放在dist根目录下的版本信息json文件,对比当前打包版本的commitId与历史版本信息json文件中commitId是否一致,如果不一致,则触发浏览器刷新。 - vite打包项目使用
.env文件 +import.meta.env保存当前打包变量(webpack打包项目可以使用definePlugin插件 +process.env保存变量)
以下代码实现
使用nodejs编写获取git版本信息的脚本
/* useNodeGetGitInfo.js */
/** 定义模块和变量* */
// const exec = require('child_process').exec //异步子进程
const moment = require('moment');
const { execSync } = require('child_process'); // 同步子进程
const fs = require('fs'); // 文件读取模块
const path = require('path');
// 文件路径处理模块
const gitInfoPath = 'gitInfo.json'; // gitInfo路径
const publicPath = 'public'; // 不能放到dist目录(该目录打包文件会被清空),要放到public目录,
const autoPush = false; // 写入版本信息之后是否自动提交git上
const isVite = true; // 是否是vite构建打包
const commitId = execSync('git show -s --format=%H').toString().trim(); // 当前提交的版本号
// 不借用chalk库,原生Node打印颜色
// console.log('\x1b[32m%s\x1b[0m', '这是绿色文本') // 绿色
// console.log('\x1b[33m%s\x1b[0m', '这是黄色文本') // 黄色
// console.log('\x1b[31m%s\x1b[0m', '这是红色文本') // 红色
/** 程序开始* */
let gitInfoObj = { };// 保存git版本信息
// 如果gitInfoPath存在,将先读取里边的版本信息
if (fs.existsSync(gitInfoPath)) {
gitInfoObj = JSON.parse(fs.readFileSync(gitInfoPath).toString());
}
// 判断当前版本是否已经存在,存在则不再次生成
if (gitInfoObj.commitId === commitId) {
console.warn('\x1B[33m%s\x1b[0m', 'warning: 当前的git版本数据已经存在了!\n');
} else {
const currentGitBranch = execSync('git rev-parse --abbrev-ref HEAD')
.toString()
.trim(); // 当前git分支
const name = execSync('git show -s --format=%cn').toString().trim(); // 姓名
const email = execSync('git show -s --format=%ce').toString().trim(); // 邮箱
const date = new Date(execSync('git show -s --format=%cd').toString()); // 日期
const message = execSync('git show -s --format=%s').toString().trim(); // 说明
gitInfoObj = {
currentGitBranch,
// name,
// email,
date: moment(date).format('yyyy-mm-dd hh:mm:ss'),
commitId,
// message,
};
const saveInfoStr = JSON.stringify(gitInfoObj, null, 2);
fs.writeFileSync(gitInfoPath, saveInfoStr);
// 写入版本信息之后,自动将版本信息提交到当前分支的git上
if (autoPush) {
execSync('git add .');
execSync(`git commit ${gitInfoPath} -m 自动提交版本信息`);
execSync(
`git pull origin ${execSync('git rev-parse --abbrev-ref HEAD')
.toString()
.trim()}`,
);
execSync(
`git push origin ${execSync('git rev-parse --abbrev-ref HEAD')
.toString()
.trim()}`,
);
}
// 程序执行结束
console.log(
'\x1b[32m%s\x1b[0m',
`execute success: file address is ${process.cwd()}/${gitInfoPath}\n`,
);
}
// 将gitInfo文件移植到public文件中,以便构建工具能够正常打包到项目根目录
if (fs.existsSync(publicPath)) {
fs.writeFileSync(
`${process.cwd()}/${publicPath}/${gitInfoPath}`,
fs.readFileSync(gitInfoPath),
);
}
// 如果是vite构建打包,把git信息追加写入.env文件中
if (isVite) {
const dotenv = require('dotenv');
const envPath = `${process.cwd()}/.env`;
// 读取 .env 文件内容
const envContent = fs.readFileSync(envPath, {
encoding: 'utf-8',
});
// 解析内容为键值对对象
const envVariables = dotenv.parse(envContent);
const gitInfoStr = JSON.stringify(gitInfoObj);
// 修改特定的环境变量
envVariables.VITE_GIT_INFO = gitInfoStr;
// 将修改后的键值对转换为字符串
const updatedEnvContent = Object.entries(envVariables)
.map(([key, value]) => `${key}=${value}`)
.join('\n');
// 将修改后的内容写入 .env 文件
console.log(updatedEnvContent);
fs.writeFileSync(envPath, updatedEnvContent, { encoding: 'utf-8' });
console.log('\x1b[32m%s\x1b[0m', '.env 文件已更新');
}
配置执行获取git版本信息脚本命令
// package.json
"scripts": {
"build": "npm run get-git-info && vite build",
"preview": "vite preview",
"get-git-info": "node scripts/git/useNodeGetGitInfo.js",
},
项目入口JS文件,监听visibilitychange事件
// app.vue
...
import { useDocumentVisibility } from '@vueuse/core';
const visibility = useDocumentVisibility();
watch(visibility, (current, previous) => {
// 获取当前版本git信息
const gitInfo = import.meta.env.VITE_GIT_INFO;
const gitInfoObj = gitInfo && JSON.parse(gitInfo);
if (current === 'visible' && previous === 'hidden' && import.meta.env.MODE !== 'development') {
fetch(`/${import.meta.env.MODE}/gitInfo.json`)
.then((res) => res.json())
.then((data) => {
if (data.commitId !== gitInfoObj.commitId) {
ElMessageBox({
title: '更新提示',
message: '有新的版本发布,请及时更新',
confirmButtonText: '确认',
callback: () => {
window.location.reload();
},
});
}
});
}
});
完成以上步骤,可以在自己项目中使用
npm run build && npm run preview进行测试并查看预览效果。