微前端 
css 隔离方案
| 方法 | 隔离级别 | 开发成本 | 兼容性 | 适用场景 | 
|---|---|---|---|---|
| Shadow DOM | 完全隔离 | 低 | 不支持 IE | 高隔离需求,现代浏览器 | 
| 动态样式表 | 作用域隔离 | 中 | 全兼容 | 通用方案,灵活可控 | 
| CSS Modules | 组件级隔离 | 中 | 全兼容 | 子应用内部组件 | 
| Iframe | 完全隔离 | 高 | 全兼容 | 第三方应用嵌入 | 
| CSS-in-JS | 组件级隔离 | 高 | 全兼容 | 现代框架项目 | 
| 命名约定 | 无隔离 | 低 | 全兼容 | 辅助手段 | 
js 隔离方案
| 方法 | 隔离级别 | 性能 | 兼容性 | 适用场景 | 
|---|---|---|---|---|
| Proxy 沙盒 | 高 | 高 | 不支持 IE | 现代浏览器,多应用并行 | 
| 快照沙盒 | 中 | 中 | 支持 IE | 低版本浏览器降级方案 | 
| Iframe | 最高 | 低 | 全支持 | 第三方应用,安全要求高 | 
| 事件监听劫持 | 中 | 高 | 全支持 | 避免事件残留 | 
| 存储前缀隔离 | 中 | 高 | 全支持 | 简单键名隔离 | 
| SystemJS 模块加载 | 高 | 中 | 现代浏览器 | 复杂模块化应用 | 
微组件构想 
类似一个 react server components
iframe 方案或其他
子组件 ssr 渲染过来 发 html js 再水合
要技术栈无关
组件数据流通 父可以给子发数据

康威定律 
🤓☝️你说得对但是康威定律是一个架构定律。
设计系统的架构受制于产生这些设计的组织的沟通结构。 — M.Conway
我的理解是:
- 如果所有组件由一个小组织开发(目测小于 10 人)则架构应为一般耦合 spa 应用
- 如果所有组件由一个大组织开发(目测约为 50 人)则架构应为微前端应用
- 小组织可以直接治理老项目混杂问题,拆分成本高于治理成本
- 大组织可能一个小组分一个小服务,新的需求源源不断,想支持动态插拔,想小组内不需要什么难的沟通就完成需求
- 既然沟通是大问题,那么就不要沟通就好了
本质上,微前端 (微服务架构) 关注的是如何解决组织和团队间协作带来的工程问题,而不是单纯的某个技术问题。
微前端的现状 
| 方案类型 | single-spa | 类 WebComponent | WebComponent + iframe | 
|---|---|---|---|
| 框架 | qiankun | micro-app | wujie-micro | 
| 支持 IE | Yes | No | Yes,自动切换成 iframe | 
| 数据通信机制 | props | addDataListener | props、window、eventBus | 
| js 沙箱 | Yes | Yes | Yes,iframe 来实现 js 沙箱 | 
| 样式隔离 | Yes | Yes | Yes,webcomponent 来实现页面的样式元素隔离 | 
| 元素隔离 | No | Yes | Yes | 
| 静态资源地址补全 | No | Yes | No | 
| 预加载 | Yes | Yes | Yes | 
| keep-alive | No | Yes | Yes | 
| 应用共享同一个资源 | Yes | Yes | Yes | 
| 应用嵌套 | Yes | Yes | Yes | 
| 插件系统 | No | Yes | Yes | 
| 子应用不改造接入 | No | Yes | Yes,满足跨域可以不改 | 
| 内置降级兼容处理 | No | No | Yes,通过 babel 来添加 polyfill | 
iframe 方案似乎会让 css 定位不准
qiankun 原理 
这边因为我只看了 qiankun 所以我只讲这个☝️
核心功能是 一个父应用在某个路由下可以拉起子应用(通过网络引入、类似 nginx),这样会让不同技术栈的应用同时存活在一个父框架上。这样维护就可以分组维护,某一些功能是一个子应用项目由某个小组维护。目前广泛应用于 toB 的控制台项目,例如 aws、cf 控制台这些
样式隔离:单应用会约定样式前缀来隔离(或者直接 scoped);微前端框架在子应用卸载后卸载 css
js 运行环境隔离:单应用同样是约定前缀等隔离方式;微前端框架使用 JS 沙箱。
最简单的 js 沙箱为
js
const windowProxy = new Proxy(window, traps);
with(windowProxy) {
// 提前将一些全局变量通过 赋值/取值 从 proxy 里缓存下来
  const undefined = windowProxy.undefined; 
  const Array = windowProxy.Array;
  const Promise = windowProxy.Promise;
// 应用代码,通过 with 确保所有的全局变量的操作实际都是在操作 qiankun 提供的代理对象
  ${appCode}
}1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
使用方法 
在主应用中注册
js
import { registerMicroApps, start } from 'qiankun';
registerMicroApps([
  {
    name: 'react app', // app name registered
    entry: '//localhost:7100',
    container: '#yourContainer',
    activeRule: '/yourActiveRule',
  },
  {
    name: 'vue app',
    entry: { scripts: ['//localhost:7100/main.js'] },
    container: '#yourContainer2',
    activeRule: '/yourActiveRule2',
  },
]);
start();1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
在微应用中适配
微应用需要在自己的入口 js (通常就是你配置的 webpack 的 entry js) 导出  bootstrap 、 mount 、 unmount  三个生命周期钩子,以供主应用在适当的时机调用。(你自己写仨函数)
js
/**
 * bootstrap 只会在微应用初始化的时候调用一次,下次微应用重新进入时会直接调用 mount 钩子,不会再重复触发 bootstrap。
 * 通常我们可以在这里做一些全局变量的初始化,比如不会在 unmount 阶段被销毁的应用级别的缓存等。
 */
export async function bootstrap() {
  console.log('react app bootstraped');
}
/**
 * 应用每次进入都会调用 mount 方法,通常我们在这里触发应用的渲染方法
 */
export async function mount(props) {
  ReactDOM.render(<App />, props.container ? props.container.querySelector('#root') : document.getElementById('root'));
}
/** 
 * 应用每次 切出/卸载 会调用的方法,通常在这里我们会卸载微应用的应用实例
 */
export async function unmount(props) {
  ReactDOM.unmountComponentAtNode(
    props.container ? props.container.querySelector('#root') : document.getElementById('root'),
  );
}
/**
 * 可选生命周期钩子,仅使用 loadMicroApp 方式加载微应用时生效
 */
export async function update(props) {
  console.log('update props', props);
}
// 以下是一些其他适配
if (window.__POWERED_BY_QIANKUN__) {
  __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
<BrowserRouter basename={window.__POWERED_BY_QIANKUN__ ? '/app-react' : '/'}>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
配置 webpack
这玩意不支持 vite 因为 vite 是 esm 他兼容沙箱做不了,但是有一些社区 plugin 可以让你用就是没有沙箱
js
const packageName = require('./package.json').name;
module.exports = {
  output: {
    library: `${packageName}-[name]`,
    libraryTarget: 'umd',
    chunkLoadingGlobal: `webpackJsonp_${packageName}`,
  },
};1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9