背景
尽管 SPA(单页应用)通常为用户提供了流畅的操作体验。
但我们也经常收到用户反馈页面问题,最终发现只是资源缓存或者过时资源,需要强制刷新
为应对这一问题,我们开发了一个升级提示组件,用来通知用户页面有更新,需及时刷新。
实现原理
要实现版本提示功能,基本逻辑包含三部分:
- 获取当前版本号
- 定时检查版本号
- 比较版本号并提示用户
基于此,我们提供了三种预设方案。
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' }} />
在微前端架构中,需要为主应用和子应用分别传递标识,通过不同的接口来获取各自的版本信息。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。