一、问题描述
1.项目相关配置: umi + qiankun + ahooks, 在项目开发过程中碰到这样一个场景:条件改变列表进行筛选并使用useUrlState改变url参数,结果发现操作一次页面被渲染2次
2.复现方式: umi + qiankun(2.4.0以下版本), 注册qiankun:{master:{}},然后更新history就会触发
二、问题排查
排查思路: 组件—页面, 业务逻辑— 相关api—依赖 顺序来排除
1.组件逻辑排查
2.页面逻辑排查
3.相关api及配置 排查
1.api排查
经过查看ahooks源码,发现useUrlState内部调用update来强制更新(更新会和history更新合并),为了排查下是否是由于update的更新引起的,项目内手动写入useUrlState函数,去掉update来测试猜想是否正确,经过测试发现,2次渲染与update并无关系,只要histroy改变就会触发
....
const setState = (s: React.SetStateAction<state>) => {
const newQuery = typeof s === 'function' ? (s as Function)(targetQuery) : s;
// 1. 如果 setState 后,search 没变化,就需要 update 来触发一次更新。比如 demo1 直接点击 clear,就需要 update 来触发更新。
// 2. update 和 history 的更新会合并,不会造成多次更新
update((v) => !v);
history[navigateMode]({
hash: location.hash,
search: stringify({ ...queryFromUrl, ...newQuery }, parseConfig) || '?',
});
};
....
2.qiankun相关配置排查
plugin-qiankun 插件配置相当简单, mater和slave两种, 经过测试发现 只要 qiankun 注册 mater然后更新history就会触发
经过初步测试结论为: qiankun注册master:{} 就会触发2次渲染的bug
3.qiankun 逻辑排查
排查qiankun逻辑需要先熟悉了解qiankun源码,qiankun源码篇幅较大,后一篇文章单独描述
排查一遍qiankun源码并未发现触发2次渲染可能会出现的问题,只能继续往下排查single-spa框架
4.single-spa 框架排查
经过翻看single-spa源码及各版本更新记录比对发现,5.8.1以前版本popstate事件会执行2次,5.8.2之后修复加了判断start之后再执行
5.8.1源码
function patchedUpdateState(updateState, methodName) {
return function () {
const urlBefore = window.location.href;
const result = updateState.apply(this, arguments);
const urlAfter = window.location.href;
if (!urlRerouteOnly || urlBefore !== urlAfter) {
window.dispatchEvent(
createPopStateEvent(window.history.state, methodName)
);
}
return result;
};
}
5.8.2源码 isStarted()就是用来判断start是否执行
function patchedUpdateState(updateState, methodName) {
return function () {
const urlBefore = window.location.href;
const result = updateState.apply(this, arguments);
const urlAfter = window.location.href;
if (!urlRerouteOnly || urlBefore !== urlAfter) {
if (isStarted()) {
// fire an artificial popstate event once single-spa is started,
// so that single-spa applications know about routing that
// occurs in a different application
window.dispatchEvent(
createPopStateEvent(window.history.state, methodName)
);
} else {
// do not fire an artificial popstate event before single-spa is started,
// since no single-spa applications need to know about routing events
// outside of their own router.
reroute([]);
}
}
return result;
};
}
三、影响范围及处理方式
1.影响范围
qiankun 2.3.1-2.4.0
由于qiankun2.3.1为了处理loadMicroApp手动加载页面不跳转的问题写死single-spa版本为2.3.1 一直到2.4.0版本,所以这些版本都存在这个问题
2.处理方式
此bug以通过lssues提交给qiankun官方,官方不会对老版本做升级,不符合版本规范,所以业务侧通过升级qiankun版本(跳过这些问题版本)来解决此bug
结束语: 写到这里,这次qiankun问题排查已告一段落,记录文档是方便后面如果有人遇到此类问题方便定位,如果写的不当之处还望多多指教