前端更新部署后如何通知用户刷新

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代码

yarn create vite@4.0.0
create-vite
# ···
# 选择 react,typescript 即可
cd vite-project-react
yarn
# 本地启动
yarn dev
# 本地产出
yarn build
# 预览生产环境
yarn preview

vite-project-react/dist/index.html


<!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

// 根据script src的hash值 实现前端更新后,单页面的地址部署刷新
let prevSrcs; // 上一次获取到的script链接地址
// 匹配过滤script标签
const scriptReg = /\<script.*src=["'](?<src>[^"']+)/gm;
/**
 * 获取最新页面中的script链接地址
 */
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);
    }
    // 记录这一次请求首页当中,所有的JS的地址
    return result;
}
/**
 * 检查是否需要更新
 * @returns boolean
 */
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

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>,
)

根据script src的hash值去判断页面是否更新