在 Umi 4 中,默认按页拆包进行优化,实现每个页面只需加载最少的 js 资源,这会产生很多异步 js 分包。通常我们会开启 hash: true 构建,将 js / css 等资源做长期缓存,而 html 不缓存。
然而,在版本发布时,如果有用户在旧的应用 html 上加载新的页面,会导致旧资源 xxx.[hash].js 加载不到。比如在整个替换 oss 存储内容的情况下,因为新版本已经发布,旧的 hash 文件不存在了。
下面为大家介绍两种解决方案:
一、加载失败自动重试方案
可以考虑 patch React.lazy 加载方法,遇到加载失败后自动 reload 页面重试。以下是自动重试的示例代码,若阻塞超过 10s,则弹出报错弹窗需用户手动刷新加载:
javascript
// src/global.tsx
import { Modal, Result } from 'antd';
import React from 'react';
const originLazy = React.lazy;
React.lazy = (factory) => {
return originLazy(() => factory().catch(loadError));
};
let hasError = false;
function loadError(): { default: React.ComponentType<unknown> } {
const time = Number(window.location.hash.match(/#s(\d+)/)?.[1]);
const now = Date.now();
const isReloading =!time || time + 10000 < now;
if (isReloading) {
window.location.hash = `#s${now}`;
window.location.reload();
}
const module = {
default: () => {
if (hasError) {
return null;
}
hasError = true;
return (
<Modal
open
cancelButtonProps={{
style: {
display: 'none',
},
}}
onOk={() => {
window.location.reload();
}}
okText="Reload"
>
<Result title="Generic error message" status="error">
{`Oops, something went wrong.`}
</Result>
</Modal>
);
},
};
return module;
}
参考来源:https://twitter.com/cpojer/status/1730141247900459166。
另外,也可参考 Vite 的 preload error 实现,自行在 loadError 内 dispatch 事件,之后在需要监听的地方处理即可。
二、自动检测新版本是否发布方案
还可参考https://github.com/umijs/umi/issues/10171 中的方案,在后台轮询 html 内容,在 html 中或其他位置维护新的版本标识,发版后将自动提示用户刷新页面。
注:添加版本标识的方案多样,如在 api.onBuildHtmlComplete 构建完成后手动修改 html,或 headScripts 手动添加等:
javascript
//.umirc.ts
headScripts: [
{
content: `// version: 1.2.3`
}
],
此外,"version-rocket"这个库也可以参考。单独开了一个工作线程,后台轮询比对版本标识,逻辑相同。