1.方案一:需要后端配合的方案
如果后端可以配合我们的话,可以使用websocket与后端进行实时通讯,前端部署完成后,后端发出一个通知。前端检测到通知消息后,进行提示。还可以再进一步优化下,使用EventSource,EventSource是HTML5中的一种新的API,用来实现服务器端向客户端推送事件。相比于常规的轮询方式,EventSource可以实现更加高效、低延迟的数据传输。
2.方案二:纯前端实现,json文件
在项目根目录放一个json文件,写入一个固定的key值然后打包的时候变一下,然后在代码中轮询去判断看有没有变化,有就提示。
具体做法是:public里放一个json文件,然后每次打包的时候 改变这个json,第一次拿到了json的数据存起来,然后轮询请求,直到json 数据上这个时间变化了,提示用户。
具体解决方案:
①.在在public文件夹下加入manifest.json文件,记录版本信息
②.前端打包的时候向manifest.json写入当前时间戳信息
③.在入口JS引入检查更新的逻辑,有更新则提示更新
这个做法有个问题就是:需要手动配置json文件,还需要打包的时候修改
3.方案三:纯前端实现,根据script src的hash值去判断
今天要详细说的就是这第三个方案。在工作当中都遇到了这么一个需求,我们的用户在站点上停留的时间可能比较长,而我们的系统每天都要进行很多次的更新。这就造成了一个问题,当我们的系统更新了之后,用户还停留在老的页面。我们则更希望用户能够收到一个提示,提示他应该刷新页面了。

实现的原理:
1.每隔一小段时间去请求一次服务器的首页,把它解析为纯文本
2.前端工程化会自动的给这个首页js和css文件,带上一个文件指纹,只要这些文件有变动,该文件指纹是一个哈希值,它也会跟着变,每隔一小段时间去分析页面中该hash值是否变化
3.只要发现JS有变动,说明这个系统的功能有所改动,那么我们就在页面上给他弹一个框,告诉他系统有更新。
适用场景:单页面应用且更新频率较高的场景,涉及到轮询(通过websocket实现也可以)
本地demo代码
1 2 3 4 5 6 7 8 9 10 11 12
| yarn create vite@4.0.0 create-vite
cd vite-project-react yarn
yarn dev
yarn build
yarn preview
|
vite-project-react/dist/index.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <link rel="icon" type="image/svg+xml" href="/vite.svg" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Vite + React + TS</title> <script type="module" crossorigin src="/assets/index-fbe6c06b.js"></script> <link rel="stylesheet" href="/assets/index-3fce1f81.css"> </head> <body> <div id="root"></div>
</body> </html>
|
vite-project-react/src/auto-update.js
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
| let prevSrcs;
const scriptReg = /\<script.*src=["'](?<src>[^"']+)/gm;
async function extractNewScript() { const html = await fetch('/?_timestamp=' + Date.now()).then((resp) => resp.text(), ); let result = []; let match; while ((match = scriptReg.exec(html))) { result.push(match.groups.src); } return result; }
async function needUpdate() { const nextScrs = await extractNewScript(); if (!prevSrcs) { prevSrcs = nextScrs; return false; } let result = false; if (prevSrcs.length !== nextScrs.length) { result = true; } for (let i = 0; i < prevSrcs.length; i++) { if (prevSrcs[i] !== nextScrs[i]) { result = true; break; } } prevSrcs = nextScrs; return result; } const DURATION = 60 * 1000; const PRODUCTION = process.env.NODE_ENV === 'production'; function autoRefresh() { setTimeout(async () => { const willUpdate = await needUpdate(); if (willUpdate) { const result = confirm('页面有更新,点击确定刷新页面'); if (result) { location.reload(); } } autoRefresh(); }, DURATION); } PRODUCTION && autoRefresh();
|
在main.ts文件中引入auto-update.js
1 2 3 4 5 6 7 8 9 10
| import React from 'react' import ReactDOM from 'react-dom/client' import App from './App' import './index.css' import './auto-update' ReactDOM.render(<App />, document.getElementById('root') as HTMLElement).render( <React.StrictMode> <App /> </React.StrictMode>, )
|
