import { registerMicroApps, start } from 'qiankun';
以上是我们主应用引入的乾坤2个api,registerMicroApps 注册子应用,start 启动主应用基座 我们重点看下乾坤在这两个api中主要做了哪些工作 先列出重要的几个步骤:
1、监视路由变化
2、匹配子应用
3、加载子应用
4、渲染子应用
然后再详细讲一下这几个步骤的基本实现原理,用到了哪些api
1、监视路由变化(hash路由&history路由)
-
hash路由监视方法为window.onhashchagnge
window.addEventListener(‘hashchange’,function(e){ console.log(‘hashchange’,e) }) -
history路由监视方法就相比而言比较复杂分为A、B、C两个部分,
A部分是我们点击浏览器前进和后退按钮触发的路由变化;
B部分就是我们向 history 中塞入一条历史记录例如 :
this.$router.push(/orderList?orderNo=76876);C部分replaceState,替换history栈中最后一个记录例如:
this.$router.push('/index')
A、history.go、history.back、history.forward 使用popstate事件:window.onpopstate
window.addEventListener('popstate',()=>{
console.log('popstate');nextRoute=window.location.pathname
}
)
B和C、pushState、replaceState 需要通过函数重写的方式进行劫持
const rawPushState=window.history.pushState
window.history.pushState=(...args)=>{
rawPushState.apply(window.history,args)
nextRoute=window.location.pathname
}
const rawReplaceState=window.history.replaceState
window.history.replaceState=(...args)=>{
rawReplaceState.apply(window.history,args)
nextRoute=window.location.pathname
}
2、匹配子应用
首先在步骤1中监视路由变化的时候我们记录了一个nextRoute(window.location.pathname),也就是要跳转的路由。
其次,在调用registerMicroApps的时候我们传入了一个注册子应用的数组apps 。
const apps=[
{
name: 'app1',
entry: 'http://localhost:1111',
container: '#appContainer',
activeRule: '/app1',
props: { data : { store, router } }
},
{
name: 'app2',
entry: 'http://localhost:2222',
container: '#appContainer',
activeRule: '/app2',
props: { data : store }
},
]
registerMicroApps(apps);
我们根据nextRoute去 apps 中匹配activeRule即可得到路由变化要匹配的子应用,代码例子:
const app = apps.find(item => getNextRoute().startsWith(item.activeRule))
if (!app) return;
3、加载子应用
加载子应用我们需要那些条件?
1)容器——加载到哪个节点
在步骤2中我们匹配到了子应用app,而app中定义的container:‘#appContaine’就是我们的节点容器如下:
const container = document.querySelector(app.container)
2)要加载的html
我们首先创建一个div,然后使用fetch或者axios也可以,去请求子应用(app.entry)的html放到div中。就得到了要加载的html。
const template = document.createElement('div')
const url=app.entry
const html = fetch(url).then(res=>res.text())
template.innerHTML = html
container.appendChild(template)
3)加载 script标 签
首先获取所有的script标签的代码放到一个数组 [代码,代码]
因为querySelectorAll得到的是nodeList的伪数组,所以我们用es6的Array.from()转一下。
const scripts=Array.from(template.querySelectorAll('script')).map(script => {
const src = script.getAttribute('src')
if (!src) {
return Promise.resolve(script.innerHTML)
} else {
return fetch(src.startsWith('http') ? src :`${url}${src}`).then(res=>res.text())
}
})
这样scripts就是我们要获取的子应用的script标签集合了,然后加载使用eval()函数来加载执行字符串的script。
scripts.forEach(code => {
eval(code)
})
4、渲染子应应用
1)怎么拿到子应用的生命周期钩子(bootstrap、mount、unmount)
子应用打包成了一个umd格式的库
Umd是什么格式?兼容不同的模块规范(commonjs,AMD)
最后导出的就是window[子应用]=factory()
所以我们使用全局变量window[app1]就能拿到子应用的导出结果
然后我们使app.bootstrap=window[app1].bootstrap
执行即可app.bootstrap && app.bootstrap()
以上就是本次微前端学习的一些原理主线总结。