项目背景
一个后台管理系统,因为里面的内容由多个不同的项目组负责开发,于是领导要求将该系统重构成微前端。因该系统正在外部使用中,所以对微前端先进行用户试点,即登录使用的还是旧项目,在登录的时候识别该用户是否是微前端试点用户,是的话设置cookie的canary为‘always’,nginx会通过这个值去切换静态资源,而前端需要在登录成功后跳转到别的页面时使用location.reload()刷新去拿到最新的静态资源。
nginx配置
旧项目下登录后跳转的路由是/index/roomAudit。由于微前端划分了不同的子应用,菜单路由是后台配置的,所以路径先由前端写死了index替换,即微前端的路由会变成/operating/roomAudit(即这个路由对于旧项目来说是不存在的)。
问题
灰度方案(即使用canary去切换资源):会刷新login跳转后的页面(前提是项目跳不准确的路由不会报404)
微前端:会跳回login页面刷新
跳转后刷新
原因
使用灰度方案时,由于路由会由前端进行index替换成对应的子应用名字,所以router跳转的页面是不存在的;而单使用微前端时,路由名字已经是改成了对应的名字,对于项目来说router跳转的页面是存在的。那么问题可以转化为:
- router.push('不存在的页面A') -> location.reload()的页面效果为:跳转A,再刷新A页面
- router.push('存在的页面B') -> location.reload()的页面效果为:跳转B,再刷新login页面(原页面)
function resolveAsyncComponents (matched) {
return function (to, from, next) {
var hasAsync = false;
var pending = 0;
var error = null;
flatMapComponents(matched, function (def, _, match, key) {
// if it's a function and doesn't have cid attached,
// assume it's an async component resolve function.
// we are not using Vue's default async resolving mechanism because
// we want to halt the navigation until the incoming component has been
// resolved.
if (typeof def === 'function' && def.cid === undefined) {
hasAsync = true;
pending++;
var resolve = once(function (resolvedDef) {
if (isESModule(resolvedDef)) {
resolvedDef = resolvedDef.default;
}
// save resolved on async factory in case it's used elsewhere
def.resolved = typeof resolvedDef === 'function'
? resolvedDef
: _Vue.extend(resolvedDef);
match.components[key] = resolvedDef;
pending--;
if (pending <= 0) {
next();
}
});
var reject = once(function (reason) {
var msg = "Failed to resolve async component " + key + ": " + reason;
"development" !== 'production' && warn(false, msg);
if (!error) {
error = isError(reason)
? reason
: new Error(msg);
next(error);
}
});
var res;
try {
res = def(resolve, reject);
} catch (e) {
reject(e);
}
if (res) {
if (typeof res.then === 'function') {
res.then(resolve, reject);
} else {
// new syntax in Vue 2.3
var comp = res.component;
if (comp && typeof comp.then === 'function') {
comp.then(resolve, reject);
}
}
}
}
});
if (!hasAsync) { next(); }
}
}
通过查看vue-router源码发现,当要跳转的路由是不存在的情况下,hasAsync为false,直接next(),相当于是同步的,因此是先跳转路由再刷新页面,刷的就是跳转后的路由。当要跳转的路由是存在的情况下,会进入到resolve函数里,是异步的,就会先刷新而此时路由跳转过程还没有完成,就会导致刷新的是跳转前的页面。
网友评论