接上回
- props的实现,路由的实现?
这次的问题非常明确,首先还是老套路,找方法然后看进去~
路由
文档中路由相关的方法navigateToUrl(...),首先从文档中看,主要就是一个路由跳转方法,但是如何操作对应的应用切换的。我们跟着看进去~
/src/navigation/navigation-events.js
export function navigateToUrl(obj) {
let url;
// 获取url的值
if (typeof obj === "string") {
url = obj;
} else if (this && this.href) {
url = this.href;
} else if (
obj &&
obj.currentTarget &&
obj.currentTarget.href &&
obj.preventDefault
) {
url = obj.currentTarget.href;
obj.preventDefault();
} else {
... // 错误处理
}
// 分别创建对应的a标签的DOM对象
const current = parseUri(window.location.href);
const destination = parseUri(url);
// hash的情况
if (url.indexOf("#") === 0) {
window.location.hash = destination.hash;
} else if (current.host !== destination.host && destination.host) {
if (process.env.BABEL_ENV === "test") {
return { wouldHaveReloadedThePage: true };
} else {
window.location.href = url;
}
} else if (
destination.pathname === current.pathname &&
destination.search === current.search
) {
window.location.hash = destination.hash;
} else {
// different path, host, or query params
/*
* 上面提到说不同path,host 或者是 search的情况, 不同host的话指的应该也是没有 host 的情况所以这边用了,pushState这个方法
*/
window.history.pushState(null, null, url);
}
}
// 以传入地址为href创建一个a标签对象,这里应该是想借DOM对象对于URI的解析,来进行格式化取值
function parseUri(str) {
const anchor = document.createElement("a");
anchor.href = str;
return anchor;
}
那么首先看完这里以后,发现在路由的这个方法中有两种切换路由的方式相当于,一种是直接修改hash 一种是 通过pushState这个方法进行的路由变换,有一种很熟悉的感觉有没有。我感觉就很像VueRouter的原理,当然可能大部分现在spa的路由都是通过这个原理去实现的。然后我们看到下面这一段
/src/navigation/navigation-events.js
// 发布中心, 收集事件数组
const capturedEventListeners = {
hashchange: [],
popstate: [],
};
// 路由的监听事件,对这两个的进行额外的收集处理
export const routingEventsListeningTo = ["hashchange", "popstate"];
if (isInBrowser) {
window.addEventListener("hashchange", urlReroute);
window.addEventListener("popstate", urlReroute);
// Monkeypatch addEventListener so that we can ensure correct timing
const originalAddEventListener = window.addEventListener;
const originalRemoveEventListener = window.removeEventListener;
// 重写事件监听,针对hashchange,popstate进行收集,防止挂载的路由事件发生问题,其他事件按原本的进行挂载
window.addEventListener = function (eventName, fn) {
if (typeof fn === "function") {
if (
routingEventsListeningTo.indexOf(eventName) >= 0 &&
!find(capturedEventListeners[eventName], (listener) => listener === fn)
) {
capturedEventListeners[eventName].push(fn);
return;
}
}
return originalAddEventListener.apply(this, arguments);
};
// 原理同上
window.removeEventListener = function (eventName, listenerFn) {
if (typeof listenerFn === "function") {
if (routingEventsListeningTo.indexOf(eventName) >= 0) {
capturedEventListeners[eventName] = capturedEventListeners[
eventName
].filter((fn) => fn !== listenerFn);
return;
}
}
return originalRemoveEventListener.apply(this, arguments);
};
// 重写pushState方法,使其兼容IE11
window.history.pushState = patchedUpdateState(
window.history.pushState,
"pushState"
);
window.history.replaceState = patchedUpdateState(
window.history.replaceState,
"replaceState"
);
// 单例模式报错,全局只注册一个singleSpaNavigate
if (window.singleSpaNavigate) {
... // 错误处理
} else {
/* For convenience in `onclick` attributes, we expose a global function for navigating to
* whatever an <a> tag's href is.
*/
window.singleSpaNavigate = navigateToUrl;
}
}
// 重写对应state的方法
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) {
urlReroute(createPopStateEvent(window.history.state, methodName));
}
return result;
};
}
// 为收集的 popstate 回调函数列表创建事件对象,并做区分singleSpa = true
function createPopStateEvent(state, originalMethodName) {
let evt;
try {
evt = new PopStateEvent("popstate", { state });
} catch (err) {
// IE 11 compatibility https://github.com/single-spa/single-spa/issues/299
// https://docs.microsoft.com/en-us/openspecs/ie_standards/ms-html5e/bd560f47-b349-4d2c-baa8-f1560fb489dd
evt = document.createEvent("PopStateEvent");
evt.initPopStateEvent("popstate", false, false, state);
}
evt.singleSpa = true;
evt.singleSpaTrigger = originalMethodName;
return evt;
}
// 重新渲染页面
function urlReroute() {
// 文章(2)就提到了的重新渲染页面的方法,这里就不重新介绍了
reroute([], arguments);
}
这里主要做了监听,依赖收集,重写方法,这么几个事情再结合之前的页面渲染方法就完成了,对应的路由导航了~,
props
关于props的问题,目前看下来,只有两个地方会涉及到props一个是reasonableTime(...) 一个是 Parcel中注册的update生命周期,也就是说Props针对Application是不会发生动态变化的情况。那么换而言之,我们针对single-spa在开发的过程中,还要去考虑应用间的状态通信的开发,当然也可以用一些现有的开源框架解决。
总结
到这里我们基于这一个小系列就要结束了,这个系列简单的带大家走读了single-spa框架中的一些简单实现,实战方面后续会出一个系列专门讲到如应用singles-spa搭建自己的微应用,后续还会推出一个系列是关于qiankun的简单学习介绍,有兴趣的同学,千万不要错过了,我们回见~