React 纯前端版本更新提示 Hooks

前言

通过 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
效果截图

总结

实现起来还是比较容易,就是找到那个能判断版本的文件,然后根据一些特殊的标识来判断是否版本有更新。

有一点需要特别注意,我们是弹框提醒用户,如果我们轮询的周期很短,可能会出现多个弹框。为了更友好的交互体验,需要通过一些变量控制提示框只弹一个。

参考链接



原文始发于微信公众号(前端学习总结):React 纯前端版本更新提示 Hooks

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/83040.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!