最简洁的微前端方案

226 阅读1分钟

使用

<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 之类的解决