目标是最完善的微前端解决方案 - qiankun 2.0

原文地址
距 qiankun 开源已过去了 11 个月,距上次官方 发声 已过去 8 个月。

Announcing qiankun@2.0html

2019 年 6 月,微前端框架 qiankun 正式发布了 1.0 版本,在这一年不到的时间内,咱们收获了 4k+ star,收获了来自 single-spa 官方团队的问候,支撑了阿里 200+ 线上应用,也成为社区不少团队选用的微前端解决方案。前端

在今天,qiankun 将正式发布 2.0 版本。react

qiankun@2.0 带来了一些新能力的同时,只作了很小的 API 调整,1.x 的用户能够很轻松的迁移到 2.x 版本,详细信息见下方 升级指南 小节。webpack

qiankun 简介

可能有的朋友还不太了解 微前端 和 qiankun 是什么。git

微前端是最近一年国内前端领域被频繁说起的关键字,虽然它并非一个全新的领域/技术,但很显然在当今愈来愈多的前端应用即将步入第 3 个、第 5 个甚至更久的年头的背景下,如何给 巨石应用/遗产应用 注入新鲜的技术血液已经成为咱们不得不正视的问题,而微前端正是解决这类问题的一个很是合适的解决方案。github

qiankun 是一个生产可用的微前端框架,它基于 single-spa,具有 js 沙箱、样式隔离、HTML Loader、预加载 等微前端系统所需的能力。qiankun 能够用于任意 js 框架,微应用接入像嵌入一个 iframe 系统同样简单。web

更多信息能够查阅咱们的 官方站点api

定位变化

qiankun 2.0 带来的最大变化即是 qiankun 的定位将由 微前端框架 转变为 微应用加载器浏览器

此前 qiankun 的典型应用场景是 route-based 的控制台应用,作为一个微应用的聚合框架而被使用。前端框架

image-20200415232001094

如上图所示,在这种场景下,一个负责聚合与切换的主应用 与 多个相互独自的微应用 一块儿构成了整个大的微前端应用,通常来讲页面上活跃着的也每每只有一个微应用。

而这是微前端的场景之一,在另一些场景下,你应该能够在同一个页面中,加载多个不一样的微应用,每一个微应用都是主应用的组成部分 或者是 提供一些加强能力,这种场景能够说是微应用粒度的前端组件化。

所以,qiankun@2.0 将跳出 route-based 的微前端场景,提供更加通用的微应用加载能力,让用户能够更加自由的组合微应用来搭建产品。

本次升级带来了什么?

新功能

  • 支持多应用并行及多实例沙箱
  • 支持手动 加载/卸载 微应用
  • 支持 IE11 沙箱兼容
  • 官方的极简微应用通讯方案
  • 支持基于 Shadow DOM 的样式隔离

此外咱们还作了

  • 升级 single-spa 到 5.x 版本
  • 更灵活的 prefetch 的定制策略
  • 配套的 webpack 插件
  • 更友好的部署场景支持,如自动为微应用注入运行时 publicPath 等
  • 更简单易懂的 API,重构了许多代码,使其更清晰和更具扩展性
  • 修复了一些 bug

另外咱们还升级了相应的 umi qiankun plugin,在 umi 场景下你能够这样去加载一个微应用:

import { MicroApp } from 'umi';

function MyPage() {
  return (
      <div>
      <MicroApp name="qiankun"/>
    </div>
  );
}

发布日志

多应用支持

在 qiankun@1.x 中,咱们的沙箱、样式隔离等机制只能对单一微应用场景生效,多个微应用共存的支持能力尚不完备。

而在 2.0 版本中,咱们终于完善了这一功能,如今,你能够同时激活多个微应用,而微应用之间能够保持互不干扰。

在多应用场景下,每一个微应用的沙箱都是相互隔离的,也就是说每一个微应用对全局的影响都会局限在微应用本身的做用域内。好比 A 应用在 window 上新增了个属性 test,这个属性只能在 A 应用本身的做用域经过 window.test 获取到,主应用或者其余微应用都没法拿到这个变量。

可是注意,页面上不能同时显示多个依赖于路由的微应用,由于浏览器只有一个 url,若是有多个依赖路由的微应用同时被激活,那么大几率会致使其中一个 404。

为了更方便的同时装载多个微应用,咱们提供了一个全新的 API loadMicroApp ,用于手动控制微应用:

import { loadMicroApp } from 'qiankun';

/** 手动加载一个微应用 */
const microApp = loadMicroApp(
  {
    name: "microApp",
    entry: "https://localhost:7001/micro-app.html",
    container: "#microApp"
  }
)

// 手动卸载
microApp.mountPromise.then(() => microApp.unmount());

这也是 qiankun 做为一个应用加载器的使用方式。

基于这个 api,你能够很容易的封装一个本身的微应用容器组件,好比:

class MicroApp extends React.Component {
  
  microAppRef = null;
  
  componentDidMount() {
    const { name, entry } = this.props;
    this.microAppRef = loadMicroApp({ name, entry, container: '#container' });
  }
  
  componentWillUnmount() {
    this.microAppRef.mountPromise.then(() => this.microAppRef.unmount());
  }
  
  render() {
    return <div id="container"/>;
  }
}

兼容 IE11 的沙箱能力

在 qiankun issue 区域呼声最高的就是 IE 的兼容,有很多小伙伴都期待 qiankun 可以在 IE 下使用。

qiankun 1.x 在 IE 使用的主要阻碍就是 qiankun 的沙箱使用了 ES6 的 Proxy,而这没法经过 ployfill 等方式弥补。这致使 IE 下的 qiankun 用户没法开启 qiankun 的沙箱功能,致使 js 隔离、样式隔离这些能力都没法启用。

为此,咱们实现了一个 IE 特供的快照沙箱,用于这些不支持 Proxy 的浏览器;这不须要用户手动开启,在代理沙箱不支持的环境中,咱们会自动降级到快照沙箱。

注意,因为快照沙箱不能作到互相之间的彻底独立,因此 IE 等环境下咱们不支持多应用场景, singlur 会被强制设为 true。

基于 shadow DOM 的样式隔离

样式隔离也是微前端面临的一个重要问题,在 qiankun@1.x 中,咱们支持了微应用之间的样式隔离(仅沙箱开启时生效),这尚存一些问题:

  1. 主子应用之间的样式隔离依赖手动配置插件处理
  2. 多应用场景下微应用之间的样式隔离亟待处理

为此,咱们引入了一个新的选项, sandbox: { strictStyleIsolation?: boolean }

在该选项开启的状况下,咱们会以 Shadow DOM 的形式嵌入微应用,以此来作到应用样式的真正隔离:

import { loadMicroApp } from 'qiankun'

loadMicroApp({xxx}, { sandbox: { strictStyleIsolation: true } });

Shadow DOM 能够作到样式之间的真正隔离(而不是依赖分配前缀等约定式隔离),其形式以下:

image.png

图片来自 MDN

在开启 strictStyleIsolation 时,咱们会将微应用插入到 qiankun 建立好的 Shadow Tree 中,微应用的样式(包括动态插入的样式)都会被挂载到这个 Shadow Host 节点下,所以微应用的样式只会做用在 Shadow Tree 内部,这样就作到了样式隔离。

可是开启 Shadow DOM 也会引起一些别的问题:

一个典型的问题是,一些组件可能会越过 Shadow Boundary 到外部 Document Tree 插入节点,而这部分节点的样式就会丢失;好比 antd 的 Modal 就会渲染节点至 ducument.body ,引起样式丢失;针对刚才的 antd 场景你能够经过他们提供的 ConfigProvider.getPopupContainer API 来指定在 Shadow Tree 内部的节点为挂载节点,但另一些其余的组件库,或者你的一些代码也会遇到一样的问题,须要你额外留心。

此外 Shadow DOM 场景下还会有一些额外的事件处理、边界处理等问题,后续咱们会逐步更新官方文档指导用户更顺利的开启 Shadow DOM。

因此请根据实际状况来选择是否开启基于 shadow DOM 的样式隔离,并作好相应的检查和处理。

官方的极简通讯方案

微前端场景下,咱们认为最合理的通讯方案是经过 URL 及 CustomEvent 来处理。但在一些简单场景下,基于 props 的方案会更直接便捷,所以咱们为 qiankun 用户提供这样一组 API 来完成应用间的通讯:

主应用建立共享状态:

import { initGloabalState } from 'qiankun';

initGloabalState({ user: 'kuitos' });

微应用经过 props 获取共享状态并监听:

export function mount(props) {
  props.onGlobalStateChange((state, prevState) => {
    console.log(state, prevState);
  });
};

更详细的 API 介绍能够查看官方文档

咱们会继续为你们带来什么

除了基本的平常维护、bugfix 以外,咱们还会尝试走的更远:

  1. 官方支持的 qiankun webpack 插件,解决一些因为配置不当出现的问题
  2. 自定义的沙箱规则
  3. 微应用嵌套支持
  4. 更友好的调试体验
  5. 与 Webpack5 Module Federation 的结合,提供官方的使用指导或插件
  6. 更多的实验性(experimental)尝试,如基于原生 Portal 标签的微应用渲染,基于运行时的更轻量的样式隔离方案。

升级指南

2.0 版本 调整了至关多的内部 API 名字,但你们使用的外部 API 变化并不大(基本彻底兼容 1.x),你能够在十分钟内完成升级。

render 更改成 container

import { registerMicroApps } from 'qiankun'

registerMicroApps(
  [
    {
      name: 'react16',
      entry: '//localhost:7100',
-     activeRule: location => location.pathname.startsWith('/react'),
+     activeRule: '/react',
-     render: renderFn,
+     container: '#subapp-viewport',
    },
  ]
)

如今你能够简单的指定一个挂载节点便可,而不用本身手写对应的 render 函数了。简单场景下 activeRule 配置也不须要再手写函数了(固然仍是支持自定义函数),只须要给出一个前缀规则字符串便可,同时支持 react-router 类的动态规则,如 /react/:appId/name (来自 single-spa 5.x 的支持)。

同时,微应用收到的 props 中会新增一个 container 属性,这就是你的挂载节点的 DOM,这对处理动态添加的容器以及开启了 Shadow DOM 场景下很是有用。

注意,旧的 render 配置依然可使用,咱们作了兼容处理方便不想升级的用户;但 render 存在时,container 就不会生效。

start 的配置变化

由于咱们引入了一些新的能力,由于 start 的配置也发生了一些变化:

import { start } from 'qiankun'

start({
-  jsSandbox: true,
+  sandbox: {
+    strictStyleIsolation: true
+  }
})

新的 API loadMicroApp

这个 API 用于手动挂载一个微应用

/** 用于加载一个微应用 */
loadMicroApp(app: LoadableApp, configuration?: FrameworkConfiguration)

使用详情可见上面 多应用支持 小节。