使用
<BrowserRouter>
<Route path="/app" render={() => <MicroApp target="http://localhost:8080" />} />
<Route path="/app2" render={() => <MicroApp target="http://localhost:8090" />} />
</BrowserRouter>
没错,就是一个组件而已。
实现
function MicroApp({ target }) {
const div = useRef(null)
useEffect(() => {
const container = div.current
const oldWindowKeys = Object.keys(window)
// 获取子应用入口html
fetch(target)
.then(res => res.text())
.then(htmlString => {
const html = document.createElement('html')
html.innerHTML = htmlString
// 插入样式
const links = Array.from(html.getElementsByTagName('link'))
links.forEach(it => {
const link = document.createElement('link')
link.rel = 'stylesheet'
link.href = it.href
container.appendChild(link)
})
// 插入脚本
const scripts = Array.from(html.getElementsByTagName('script'))
scripts.forEach(it => {
const script = document.createElement('script')
script.defer = true
script.src = it.src
container.appendChild(script)
})
})
// 重置容器和window
return () => {
container.innerHTML = '<div id="root"></div>'
const newWindowKeys = Object.keys(window)
const appWindowKeys = newWindowKeys.filter(key => !oldWindowKeys.includes(key))
appWindowKeys.forEach(key => {
window[key] = undefined
})
}
}, [])
return (
<div ref={div}>
<div id="root"></div>
</div>
)
}
优点
- 框架,技术栈无关
- 子应用是作为一个干干净净的 div 放到主应用里,自动享用主应用的一切,如公共样式等
- js 沙箱
- css 沙箱
- 子应用的接入成本为 0,子应用就按普通的 app 写就可以,出现子应用嵌套的时候的,挂载点可以约定一下
对比乾坤
- 乾坤把所有的 src 改成手动 fetch,这样资源是没有缓存的,这样每次进应用都会白屏,体验较差。这也是写这个东西的初衷。
- 嵌入后的代码更为干净。
- 子应用不需要做任何事情,不需要额外的代码和打包配置
- 不依赖 webpack
- 乾坤比较重
未实现
主子项目暂无通信机制,这个可以通过 sessionStorge 之类的解决