Geass's Studio.

浅谈微前端

字数统计: 1.2k阅读时长: 4 min
2020/03/20 Share

微前端与微服务

在这两三年里,移动应用出现了一种趋势,用户不想装那么多应用了。而往往一家大的商业公司,会提供一系列的应用。这些应用也从某种程度上,反应了这家公司的组织架构。然而,在用户的眼里他们就是一家公司,他们就只应该有一个产品。相似的,这种趋势也在桌面 Web 出现。聚合成为了一个技术趋势,体现在前端的聚合就是微服务化架构。
对于后端服务而言,微服务提供了数据业务解耦的能力,降低了彼此依赖。而在前端微服务化上,则是恰恰与之相反的,人们更想要的结果是聚合,尤其是那些 To B(to Bussiness)的应用。

微服务是运行在各自系统上,控制各自对应的数据库,通过网络进行交互的后端服务。

不同点:微前端不直接访问数据库,存在与浏览器环境,通过浏览器内存进行组件间的交互。

相同点:独立开发独立部署;前端的DOM类比后端的数据库服务,只通过暴露出的服务进行修改。除了拥有者不允许别的服务直接进行访问修改。

价值

  • 解决痛点:解决遗留系统,才是人们采用微前端方案最重要的原因
  • 微服务架构来解耦服务间依赖。微前端架构来聚合多种服务产品。
  • 单页应用做大后加载慢,维护,回归成本升高,构建缓慢
  • 中后台有三方接入需求

特点

  • 子应用支持多框架
  • 子应用独立开发,发布Pre,迁移成本低
  • 应用体验接近 SPA
  • 独立部署与配置自动化

问题

  • 依赖管理问题,由谁来加载依赖,公共依赖的管理和版本迭代。统一这些依赖的版本,引入新的依赖时都需要一一加入。
  • 规范应用的组件及路由。避免不同的应用之间,因为这些组件名称发生冲突。
  • 共享通用代码。这显然是一个要经常面对的问题。
  • 制定代码规范,状态管理方案等规范化。

微前端架构

https://s3-us-west-2.amazonaws.com/secure.notion-static.com/396f339a-cf83-4b30-95ac-1b3b4c4900e8/Untitled.png

框架应用职责明确

框架应用只做两件事情:

  1. 系统整体 Layout 的设计
  2. 所有子应用的配置与注册

框架应用尽量避免包含具体页面的 UI 代码,如果框架应用做了过多的事情会带来以下问题:(反例:XSpace)

  • 框架应用样式代码太多,会增加子应用和框架应用样式冲突概率
  • 框架应用为子应用提供其他能力比如一些全局 API,会破坏子应用的独立性,增加相互的耦合
  • 框架应用本质是一个中心化的部分,变更后原则上需要回归所有子应用,因此需要保证职责的简单,越简单的东西越稳定
  • 框架的迭代对子应用产生过大影响

子应用的路由通过前缀约定管理

避免产生路由冲突,便于框架路由管理注册。

子应用路由管理,父子路由交互的设定方案。

应用间通信

  • 状态共享:通过全局 Store 进行数据状态的管理
  • 事件监听:通过事件总线机制

子应用生命周期

  • bootstrap
  • mount
  • update
  • unmount

样式脚本隔离

样式隔离

  • CSS Module
  • 自定义组件 prefix
  • 子应用避免全局样式
  • shadow DOM (问题点:挂载根下的组件样式问题,如 dialog 等)

脚本隔离

沙箱方案
  • iframe
    • 优点
      • 简单,浏览器自带隔离沙箱
      • 线/进程隔离,安全性高
    • 缺点
      • 交互复杂,成本高。postMessage 信息交互 0.1ms 一个来回,1s 仅允许~1000次交互
      • 加载成本高(线/进程)
  • 代理 JS 沙箱

    • 技术点:

      • 通过 Proxy 代理 windows 对象,截获数据访问
      • 通过 with(sandbox) {${script}} 构建执行环境,通过 with 动态改变全局 this 访问(不用 eval,容易越界访问)
      • 通过 new Function 构建沙箱函数进行执行

      注意:Symbol.unscopables:可以越界访问,代理时需注意

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      function compileCode(code) {
      code = 'with (sandbox) {' + code + '}';
      const fn = new Function('sandbox', code);
      return (sandbox) => {
      const proxy = new Proxy(sandbox, {
      has(target, key) {
      return true; // 欺骗,告知属性存在
      }
      get(target, key, receiver) {
      // 加固,防止逃逸
      if (key === Symbol.unscopables) {
      return undefined;
      }
      Reflect.get(target, key, receiver);
      }
      });
      return fn(proxy);
      }
      }
  • web worker

https://intranetproxy.alipay.com/skylark/lark/0/2020/jpeg/1687/1583390359268-d0c3a937-dd6b-413e-9f7e-a4258d6a9610.jpeg

https://s3-us-west-2.amazonaws.com/secure.notion-static.com/40d77deb-c9e9-4e7d-baa2-548db51db17f/B49A5E47-14F3-4903-B31A-2D4BC8B9106B.png

微前端流程

https://s3-us-west-2.amazonaws.com/secure.notion-static.com/2efd26a4-01b2-48be-8c2c-ad256e605a00/Untitled.png

参考文献

这可能是你见过最完善的微前端解决方案!-InfoQ

Thinking in Microfrontend (微前端的那些事儿)

CATALOG
  1. 1. 微前端与微服务
  2. 2. 价值
  3. 3. 特点
  4. 4. 问题
  • 微前端架构
  • 框架应用职责明确
    1. 子应用的路由通过前缀约定管理
    2. 应用间通信
    3. 子应用生命周期
    4. 样式脚本隔离
      1. 1. 样式隔离
      2. 2. 脚本隔离
        1. 2.1. 沙箱方案
    5. 微前端流程
    6. 参考文献