系统在发布后,会有希望用户刷新的业务场景。本文仅为 前端发布,纯前端实现提示
的场景提供一种思路/方案。如果后端发布也要触发提醒刷新,那一般只能通过接口交互方案来触发前端提示。
方案原理及使用前提
方案的核心原理是轮询 index.html
,对比 html
内容是否一致,若不一致,则提示刷新。故使用该方案的前提是:让 index.html
每次发版前后内容产生不一致。
一般来说,现代化前端项目每次发版,所引用 JS 的文件名 chunkhash
值都会发生变化,故 index.html
中的 script src 属性
一定会发生变化。
如果因技术架构导致无法通过 chunkhash
来判断,那就要考虑通过其他方式让 html
产生变化。
比如:构建时,在 index.html
中,
- 注入当前 git-分支的 HEAD hash
- 注入当前打包时间
- …(任何你能想到的变化标识值)
注入的方式有很多种:
- 修改 webpack 配置,加入 process.env 变量(如:增加
process.env.APP_VERSION
,在 index.html
中使用该变量)
- 在打包机上识别 html 文件并写入内容(如:匹配
</body>
标签,在前面插入 <script> window.buildTime = new Date().valueOf(); </script>
)
- …(任何在打包环节可以变动 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
| import { sendError } from '埋点上报';
function getWebsiteHTML(url) { 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); } }); }
export default function checkVersionChange(args) { const { intervalTime, cb, } = args; let timer = null; let initHtml = ''; function loop() { const resolve = html => { if (!initHtml) { initHtml = html; loop(); return; } if (initHtml !== html) { cb(); clearTimeout(timer); timer = null; return; } 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
| 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
| const childProcess = require('child_process'); let lastCommitHash = ''; try { lastCommitHash = childProcess .execSync('git rev-parse --short HEAD') .toString() .trim(); } catch (e) { console.error(e); }
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大幅上涨,在项目中应用,应考虑事先通知干系人该影响,避免造成疑惑或更大的影响。