Fizzy Zhang

本文目录

  1. 方案原理及使用前提
  2. 方案使用
  3. 附:git hash 注入
  4. 注意

前端实现新版本发布提示

系统在发布后,会有希望用户刷新的业务场景。本文仅为 前端发布,纯前端实现提示的场景提供一种思路/方案。如果后端发布也要触发提醒刷新,那一般只能通过接口交互方案来触发前端提示。

方案原理及使用前提

方案的核心原理是轮询 index.html,对比 html 内容是否一致,若不一致,则提示刷新。故使用该方案的前提是:让 index.html 每次发版前后内容产生不一致。

一般来说,现代化前端项目每次发版,所引用 JS 的文件名 chunkhash 值都会发生变化,故 index.html 中的 script src 属性 一定会发生变化。

如果因技术架构导致无法通过 chunkhash 来判断,那就要考虑通过其他方式让 html 产生变化。

比如:构建时,在 index.html 中,

  1. 注入当前 git-分支的 HEAD hash
  2. 注入当前打包时间
  3. …(任何你能想到的变化标识值)

注入的方式有很多种:

  1. 修改 webpack 配置,加入 process.env 变量(如:增加 process.env.APP_VERSION,在 index.html 中使用该变量)
  2. 在打包机上识别 html 文件并写入内容(如:匹配 </body> 标签,在前面插入 <script> window.buildTime = new Date().valueOf(); </script>
  3. …(任何在打包环节可以变动 html 的钩子)

方案使用

方案实现如下:

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
// version-check.js
import { sendError } from '埋点上报';

function getWebsiteHTML(url) {
// no-cache: 不使用缓存,强制从服务器获取
return fetch(url, { cache: 'no-cache' }).then(res => {
try {
if (res && [200, 304].includes(res.status) && res.text) {
return res.text();
}
return Promise.reject(res);
} catch (err) {
return Promise.reject(err);
}
});
}

/**
* 定时轮询检查是否有新版本发布
* @param {Object} args
* @param {Number} args.intervalTime 轮询间隔时间,单位ms
* @param {Function} args.cb 检测到有新版本发布的回调函数
*/
export default function checkVersionChange(args) {
const {
intervalTime,
cb,
} = args;
let timer = null;
let initHtml = '';
function loop() {
const resolve = html => {
// 第一次获取到的html
if (!initHtml) {
initHtml = html;
loop();
return;
}
// 如果html发生变化,说明有新版本发布
if (initHtml !== html) {
cb();
clearTimeout(timer);
timer = null;
return;
}
// html没有发生变化,继续轮询
loop();
}
const reject = err => {
console.error(err);
sendError(err);
}
timer = setTimeout(() => {
getWebsiteHTML(window.location.origin).then(resolve).catch(reject);
}, intervalTime);
}
loop();
}

在项目入口(页面渲染后),增加 checkVersionChange 函数调用,即开始轮询判断是否有新版本。

1
2
3
4
5
6
7
8
9
10
11
// entry.js
import checkVersionChange from 'lib/version-check';

checkVersionChange({
intervalTime: 1000 * 10,
cb: () => {
// 替换成需要执行的回调函数
alert('有新版本发布,请您刷新!');
window.location.reload();
},
});

附:git hash 注入

打包时,注入当前 git-分支的 HEAD hash。需要注意,打包所处的目录一定是包含 .git 的项目目录,且具备 git 命令执行环境,如果不满足条件,需要考虑其他方案改变 html。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// webpack.config.js
const childProcess = require('child_process');
let lastCommitHash = ''; // 当前打包目录 git HEAD commit hash
try {
lastCommitHash = childProcess
.execSync('git rev-parse --short HEAD')
.toString()
.trim();
} catch (e) {
console.error(e);
}

// 写入 process.env.APP_VERSION
defineInProcessEnv: {
APP_VERSION: JSON.stringify(lastCommitHash),
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// index.ejs
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>demo</title>
</head>
<body>
<script type="text/javascript">
window.APP_VERSION = <%= process.env.APP_VERSION %>;
</script>
</body>
</html>

注意

轮询方法会导致前端服务器QPS大幅上涨,在项目中应用,应考虑事先通知干系人该影响,避免造成疑惑或更大的影响。