前言
通过 sentry
查看错误,发现一些解决了近半个月的问题还有用户在上报。观察收集到的一些用户信息,发现很多用户都是使用的 mac
系统。猜想他们的操作习惯应该是打开了浏览器后,一直没有关闭或者刷新浏览器。也就是一直都使用的老版本的前端代码。
解决方案
在前端重新发版后,提示用户刷新浏览器。办法有许多种,比如:
-
token
过期时间设置短一点,过期后提示重新登录,重新登录时reload
一次页面 -
有新功能上线时,基本后端也会上线,后端上线后,发一个 WS
给前端,里面包含最新版本的信息 -
定时请求最新的版本,和当前的版本对比,如果有更新,弹消息提醒用户 -
页面请求数据时,在统一的 request
处请求最新的版本信息(原理同第二个)
token
过期时间和 WS
需要后端参与,这里讨论一下纯前端的实现方案。
最初我想通过类似 百度统计
和 sentry
的方式,利用一个一像素的 gif
图片,每次打包时,更新一下 gif
图片的 hash
值,并且在 hash
值后面加上打包的版本信息。但是在请求的过程中,发现 gif
图片地址在没有刷新浏览器(没有获取到最新的 bundle
内容)前,一直都是之前的 CDN
地址,就算我们给图片后面再随机加一个参数,让图片不缓存,也无法获取到最新的版本信息。
正好看到 前端重新部署如何通知用户刷新网页? 有做类似的介绍,于是实现了一个 hooks
。
它的实现原理是,打包后的 index.html
文件不缓存。我们在打包时,配置 hash
方式为 contenthash
,每次内容变化后,打包的 hash
值就会变化,部分前端框架(像 umi
)默认就是 contenthash
。我们写个定时器,去读取这个 html
文件,获取到内容后再通过正则将 hash
值解析出来,和系统当前的 version
进行对比,如果不一样,就提示用户。
build
后的 index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
</head>
<body>
<div id="root"></div>
<script src="/umi.7623d592.js"></script>
</body>
</html>
Hooks 代码
import { useCallback, useEffect, useRef } from 'react';
import { Button, notification } from 'antd';
interface IVersionUpdate {
// 间隔时间,时间戳
interval?: number;
// 是否重新刷新
reload?: boolean;
}
const oneDay = 24 * 60 * 60 * 1000;
/** 版本更新提醒 Hooks */
const useVersionUpdateRemind = (props?: IVersionUpdate) => {
const { interval = oneDay, reload } = props || {};
const oldHashRef = useRef<string>('');
const notificationIsOpen = useRef<boolean>(false);
const getHtml = useCallback(() => {
// 如果当前已经有弹框了,先不执行后面的请求
if (notificationIsOpen.current) return;
//读取index html 中关于打包文件最新的 hash 值
fetch('/').then(async (res) => {
const newRes = await res.text();
// 默认的打包 bundle 或者 chunk 后的打包文件
const currentHash = newRes.match(/(?<=umi.)w+(?=.js)/g)?.[0];
if (oldHashRef.current && oldHashRef.current !== currentHash) {
notificationIsOpen.current = true;
notification.warning({
message: '提示',
description: (
<>
<p>客户端版本过低,请刷新浏览器</p>
{reload && (
<div style={{ textAlign: 'right', marginTop: 20 }}>
<Button type="primary" onClick={() => window.location.reload()}>
立即刷新
</Button>
</div>
)}
</>
),
duration: 0,
onClose() {
notificationIsOpen.current = false;
}
});
} else {
oldHashRef.current = currentHash!;
}
});
}, [reload]);
useEffect(() => {
// 先执行一次,初始化 oldHashRef 值
getHtml();
const timer = setInterval(() => {
getHtml();
}, interval);
return () => {
clearInterval(timer);
};
}, [getHtml, interval]);
};
export default useVersionUpdateRemind;
总结
实现起来还是比较容易,就是找到那个能判断版本的文件,然后根据一些特殊的标识来判断是否版本有更新。
有一点需要特别注意,我们是弹框提醒用户,如果我们轮询的周期很短,可能会出现多个弹框。为了更友好的交互体验,需要通过一些变量控制提示框只弹一个。
参考链接
原文始发于微信公众号(前端学习总结):React 纯前端版本更新提示 Hooks
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/83040.html