1

背景

尽管 SPA(单页应用)通常为用户提供了流畅的操作体验。

但我们也经常收到用户反馈页面问题,最终发现只是资源缓存或者过时资源,需要强制刷新

为应对这一问题,我们开发了一个升级提示组件,用来通知用户页面有更新,需及时刷新。

实现原理

要实现版本提示功能,基本逻辑包含三部分:

  1. 获取当前版本号
  2. 定时检查版本号
  3. 比较版本号并提示用户

基于此,我们提供了三种预设方案。

Html 检查⽅案 <HtmlUpgradeNotification />

如何判断页面需要提示用户更新?

从 HTML 渲染的角度看,加载 HTML 文档后,解析并将 DOM 渲染到页面,如果 HTML 内容一致,理论上渲染结果也应一致。因此,当 HTML 内容发生变化时,可能会导致渲染结果不同。基于此逻辑,我们通过定时检测 HTML 的关键特征,若内容发生变化,则提示用户更新。

那么这个⽅案有什么缺点吗? 此方案的缺点在于其检测粒度较粗,可能误报。例如,页面中的一些非内容更新(如 CSRF token 变化)也会触发提示。

主要代码

export const defaultRequestManifest: UpgradeNotificationProps['requestManifest'] = async ({ fetchUrl, params, ...options } = { fetchUrl: '/', params: {} }) => {
  const url = new URL(fetchUrl, location.href);
  if (params && typeof params === 'object') {
    Object.entries(params).forEach(([key, value]) => {
      url.searchParams.set(key, value);
    });
  }
  const response = await fetch(url.href, { ...options });
  const newVersion = await response.text();
  return newVersion;
};

Hash 检查⽅案 <HashUpgradeNotification />

由于 HTML 作为关键特征的检测方式过于粗糙,我们优化了逻辑,提取页面中 <script> 标签的 src 属性,忽略其他内容。

这样可以避免不必要的刷新提示,专注于页面核心逻辑的变化。

主要代码

<HtmlUpgradeNotification
  requestManifest={async ({ fetchUrl = '', ...props }) => {
    const response = await defaultRequestManifest({ fetchUrl, ...props });
    const scriptStr = response
      .match(/\<script( defer(="defer")?)?( src="[^"]+")><\/script>/g)
      ?.map((v) => v.trim())
      ?.filter((v) => v)
      ?.join('\n');
    return scriptStr || '';
  }}
  {...props}
/>

version 检查⽅案 `<VersionUpgradeNotification params={{

moduleType: 'Main', moduleName: 'mains' }} />`

主要代码

const VersionUpgradeNotification = ({ ...props }: UpgradeNotificationProps) => (
  <HtmlUpgradeNotification
    fetchUrl="/api/current-version"
    requestManifest={async ({ fetchUrl = '', ...props }) => {
      const response = await defaultRequestManifest({ fetchUrl, ...props });
      return response;
    }}
    {...props}
/>
);

版本号检查⽅案

通过 currentVersion 参数获取当前版本号。如果没有传递 currentVersion,则使用首次 fetch 的结果作为版本号。

使用方法

SPA 项目、 MPA 项目

<UpgradeNotification />

默认情况下,直接在项目的入口文件(如 main.ts)中渲染 <UpgradeNotification /> 组件,即可支持全局提示功能。如果只需要在特定页面显示提示,可以将组件嵌入该页面,这样会根据组件的挂载/卸载动态监听更新。

微前端

// 主应⽤、框架中写
<VersionUpgradeNotification params={{ moduleType: 'Main', moduleName: 'mains'
}} />

// ⼦应⽤中写
<VersionUpgradeNotification params={{ moduleType: 'SubModule', moduleName:
'subs' }} />

在微前端架构中,需要为主应用和子应用分别传递标识,通过不同的接口来获取各自的版本信息。


linong
29.2k 声望9.5k 粉丝

Read-Search-Ask