reroute 方法,没有调用start时举行加载app,调用loadApps方法,loadApps中准备加载应用,准备完毕后注册自界说事故callAllEventListeners
function reroute() { if (isStarted()) { appChangeUnderway = true; appsThatChanged = appsToUnload.concat( appsToLoad, appsToUnmount, appsToMount ); return performAppChanges(); } else { appsThatChanged = appsToLoad; return loadApps(); } function loadApps() { return Promise.resolve().then(function () { var loadPromises = appsToLoad.map(toLoadPromise); return ( Promise.all(loadPromises) .then(callAllEventListeners) .then(function () { return []; }) .catch(function (err) { callAllEventListeners(); throw err; }) ); }); }}callAllEventListeners方法,遍历每个应用,给每个应用都添加callCapturedEventListeners事故监听,这边是操纵的备份对象不会对欣赏器中的事故举行重写。
function callAllEventListeners() { pendingPromises.forEach(function (pendingPromise) { callCapturedEventListeners(pendingPromise.eventArguments); }); callCapturedEventListeners(eventArguments); }callCapturedEventListeners方法,遍历快照中重写的方法,之后监听到"hashchange", "popstate"时就会走我们自己界说的方法逻辑。
var routingEventsListeningTo = ["hashchange", "popstate"];function callCapturedEventListeners(eventArguments) { var _this = this; if (eventArguments) { var eventType = eventArguments[0].type; if (routingEventsListeningTo.indexOf(eventType) >= 0) { capturedEventListeners[eventType].forEach(function (listener) { try { // The error thrown by application event listener should not break single-spa down. // Just like https://github.com/single-spa/single-spa/blob/85f5042dff960e40936f3a5069d56fc9477fac04/src/navigation/reroute.js#L140-L146 did listener.apply(_this, eventArguments); } catch (e) { setTimeout(function () { throw e; }); } }); } }}这个流程完毕之后,主应用调用了start方法后,就开始走performAppChanges相干逻辑。上篇文章有讲到过,重要是通过控制状态来到达激活和切换子应用的效果。
路由切换的时间怎么举行微应用加载的?
实现原理:通过监听和重写欣赏器的hashchange、popstate、pushState和replaceState的方法
下面这段代码是默认实行的,阐明一开始就对欣赏器中的方法举行了注册和重写,hash模式的代码会重写hashchange方法,当使用history模式时,pushState和 replaceState事故被触发时不会触发 popstate事故,只有back、forward、go才可以触发。
pushState:只会向汗青堆栈内里添加一个状态,不会更新页面,传了url差异源时会报错,不传则将其设置为当前文档的url。
replaceState:修改当前汗青记载实体 ,同上,传了url差异源时会报错,不传则将其设置为当前文档的url。
//判断是否在欣赏器环境if (isInBrowser) { window.addEventListener("hashchange", urlReroute); window.addEventListener("popstate", urlReroute); // 不要直接操纵原有方法,在copy的方法上操纵 var originalAddEventListener = window.addEventListener; var originalRemoveEventListener = window.removeEventListener;//重写pushState方法 window.history.pushState = patchedUpdateState( window.history.pushState, "pushState" ); //重写replaceState方法 window.history.replaceState = patchedUpdateState( window.history.replaceState, "replaceState" );}patchedUpdateState,记载跳转前和跳转后的url,假如urlBefore和urlAfter差异的时间才会重写路由,否则就只要举行pushState和replaceState操纵就会调用。
只有子应用启动了之后才手动触发popState事故,由于在没有启动子应用之前就可以使用默认的路由事故了。
dispatchEvent即为派发事故,现实就是我们在开始时监听了popstate事故,回调函数即reroute,因此pushstate和replacestate实行reroute的本质上是通过popstate来触发route的
function patchedUpdateState(updateState, methodName) { return function () { //性能优化,只有当当前页面和要跳转的页面不一样时才创建createPopStateEvent //获取跳转前的url var urlBefore = window.location.href; var result = updateState.apply(this, arguments); //获取跳转后的url var urlAfter = window.location.href; if (!urlRerouteOnly || urlBefore !== urlAfter) { if (isStarted()) { window.dispatchEvent( createPopStateEvent(window.history.state, methodName) ); } else { reroute([]); } } return result; };}createPopStateEvent方法,将重写后的popstate返回
function createPopStateEvent(state, originalMethodName) { var evt; try { evt = new PopStateEvent("popstate", { state: state, }); } catch (err) { evt = document.createEvent("opStateEvent"); evt.initPopStateEvent("popstate", false, false, state); } evt.singleSpa = true; evt.singleSpaTrigger = originalMethodName; return evt;}为什么要重写pushState和 replaceState方法?
这篇文章对这个标题的表明我以为是最公道的
除了在微前端框架中须要监听对应的导航事故外,在微前端框架外部我们也可以通过 addEventListener 的方式来注册 hashchange 和 popstate 事故,那么如许一来导航事故就会有多个,为了在实现对导航事故的控制,到达路由变革时对应的子应用可以或许精确的 卸载 和 挂载,须要对 addEventListener 注册的 hashchange 和 popstate 举行拦截,并将对应的事故给存储起来,便于后续在特定的时间可以或许实现手动触发。
发现在挂载和卸载阶段都调用了callAllEventListeners方法,而callAllEventListeners又调用了callCapturedEventListeners方法,callCapturedEventListeners方法中是对"hashchange", "popstate"挨个举行手动调用。
这证实白加载子应用时通过reroute和路由控制不停地在加载和切换子应用。
callCapturedEventListeners方法源码参考上面 |