yshaojun / blog

1 stars 1 forks source link

从 alicloud-console-os 看微前端 #13

Open yshaojun opened 4 years ago

yshaojun commented 4 years ago

微前端(或者叫前端微服务)以技术无关、独立部署的特点,成为前端重点关注的技术方向之一。虽然业内对微前端基本要点(比如服务加载、隔离、通信、路由等)的认识趋于一致,但是仍然没有一套完整且公认的解决方案。

本文是读阿里云 alicloud-console-os 的感受,看它是分别选择怎样的方案解决上述问题的。

入口

直接的想法是以 js 为入口,就像个人网站接百度推广那样,但问题是不好处理 css 和 thunk js;以 html 为入口,又需要对 html 进行解析,做起来也比较麻烦。

console-os 使用 json 文件作为入口,这个文件里包含了 css、js 等资源 url 列表,配合 jsonp 的引入方式(下文介绍),显得简单可靠。

加载

之前想到的加载办法是 fetch 到 js 代码,然后用 eval 或者 new Function 执行;fetch 到 css,用 innerHtml 插入到 DOM 里,问题是如果 js 加载其他 chunk js 时,在加载方式又比较混乱。

console-os 使用 jsonp 的方式,在 head 里插入 scriptlink 标签,配合子应用打包时最外层包裹的函数:

window.__CONSOLE_OS_GLOBAL_HOOK__(
  id,
  function(require, module, exports, { window, location, history, document }){ 
    // 子应用代码
  }
)

应用就加载即执行了。

隔离

在上述的 __CONSOLE_OS_GLOBAL_HOOK__ 函数中,传入了用 Proxy 劫持的 {window,location,history,document},以此实现了子应用 js 隔离。这个地方有个有趣的实现,相比 qiankun 的 “快照”,console-os 是为每个子应用创建一个隐藏的 iframe,然后把这个 iframe 上的全局变量比如 history 传给它!因此子应用的全局操作,一部分就直接落到各自的 iframe 上。

官方 example 里没有实现 css 隔离,不过从示例看,子应用都被限制在各自以 id 为名称的自定义 html 标签里,加上子应用使用同一个 Document 对象,因此很容易套用常规的组件局部样式方案,比如选择器都加最外层 class、自定义属性 data-[hash] 等。

通信

console-os 的父子应用并没有完完全全的隔离开,比如它们仍然共用相同的 WindowDocument(当然对子应用,以 Proxy 对部分属性做了拦截),所以它们的通信没有天然的障碍。官方在 events 中,直接使用了 EventEmitter

路由

console-os 中对子应用的路由进行了隔离,但是目前并未实现完整的路由方案。不过在 console-os 中实现应该不难,宿主获取路由信息,通过事件向子应用广播,子应用以 id 为频道订阅自己的,然后做对应的路由动作;子应用切换路由时,以同样的方式通知宿主做路由变更,应该就行了。

微组件

其他微前端框架中,都是以类似配置或 api 的形式引入子应用,在 console-os 中,提供了组件形式:

function App() {
  return (
    <div className="App">
      {/* 渲染应用 */}
      <div className="react">
        <Application
          id="os-example"
          manifest="http://localhost:8081/os-example.manifest.json"
        />
      </div>
    </div>
  )
}

这种方式利于在已有项目中灵活的引入微服务。