qiankun简介

419 阅读3分钟

qiankun的隔离

qiankun的JS隔离:沙箱模式(三种:Proxy沙箱,Legacy沙箱,快照沙箱)

Proxy沙箱:利用Proxy代理window对象,创建一个独立的fakeWindow对象,代理其get、set操作,优先返回虚拟window的值,否则返回原始window的值。子应用对全局的变量的操作会被劫持然后定到虚拟window上,避免污染其他子应用和主应用;支持多个子应用,每个子应用拥有独立的虚拟window; **Legacy沙箱:**同样使用proxy,但是是修改主应用window,记录变更,卸载时恢复原来,仅支持单例,多例会污染全局;(使用很少) 快照沙箱:在子应用加载前保存全局对象,子应用激活时遍历window生成快照,失活时遍历window,将修改的属性记录下来,然后还原到原来的状态;

CSS隔离:

1.Shadow Dom:将子应用的DOM和样式封装在独立的Shadow Dom中(但要支持Shadow Dom的浏览器),shadow Dom中定义的样式不会影响页面的其他部分,也不会被影响,避免样式冲突。Shadow Dom就是创建了一个独立的DOM树,这个树被附加在某个元素上,主DOM树的脚本和样式没有办法访问Shadow Dom上的内容。(但qiankun中要注意:(1)弹窗组件样式失效,弹窗组件挂载在document.body上,要将其挂在子应用容器内;(2)动态样式加载丢失,需要保证加载的样式要放到Shadow Dom内;(3)浏览器兼容性) 实现方式:通过配置 sandbox: { strictStyleIsolation: true },qiankun 将子应用容器包裹在 Shadow DOM 内; 对Shadow Dom做一些工程化处理:

1. 修改类名前缀
  • 场景:主应用和子应用使用同名 UI 库(如 Element UI 与 Element Plus)时,通过修改类名前缀避免冲突。

  • 实现:使用 postcss-change-css-prefix 和 change-prefix-loader 在构建时替换类名前缀。 // postcss.config.js module.exports = { plugins: [require("postcss-change-css-prefix")({ prefix: "el-", replace: "parent-" })] };

2. 自定义 Loader 处理第三方库样式
  • 场景:解决 Shadow DOM 中 :root 选择器失效问题(如 Element Plus 的全局变量)。
  • 实现:编写 Webpack Loader,将 :root 替换为 :host
3. ### CSS Modules
  • 实现:在子应用的构建工具(如 Webpack)中启用 CSS Modules。以 Webpack 为例,需要在 webpack.config.js 中进行配置

image.png

事件隔离

qiankun的事件隔离是,子应用的事件仅在子应用的DOM树中传播,不会冒泡到主应用。qiankun提供的事件总线机制,允许子应用和主应用,子应用和其他子应用的的通信,通过总线处理,不会影响到其他的部分的DOM和事件处理。

qiankun子应用和主应用的通信 (1)props和监听事件 通过props传递,简易数据传递方式 (2)initGlobalState

// 主应用
import { initGlobalState } from 'qiankun';

const initialState = { user: 'admin' };
const actions = initGlobalState(initialState);

// 监听状态变化
actions.onGlobalStateChange((state, prevState) => {
  console.log('全局状态变更:', state);
});

// 更新状态
actions.setGlobalState({ user: 'newAdmin' });
// 子应用
export async function mount(props) {
  // 监听状态变化
  props.onGlobalStateChange((state) => {
    console.log('全局状态变更:', state);
  });

  // 修改状态
  props.setGlobalState({ theme: 'dark' });
}

使用CustomEvent

  • 原理:通过浏览器原生 CustomEvent 或第三方库(如 mitt)实现发布-订阅模式。
// 主应用
window.dispatchEvent(new CustomEvent('main-event', { 
  detail: { data: '来自主应用的消息' } 
}));
// 子应用
window.addEventListener('main-event', (e) => {
  console.log('收到主应用消息:', e.detail.data);
});

子应用与子应用的通信 子应用->主应用->目标子应用 (1)使用CustomEvent,A发布,主应用监听,主应用发布新事件,B监听获取; (2)localStorage,子应用使用localStorage.setItem()方法写数据,另一个子应用使用事件监听"storage",确认e.key是发送消息的对象,做后续处理,但是这两个应用需要同源; (3)initGlobalState 主应用创建GlobalState,子应用A 使用setGlobalState,子应用Bs使用onGlobalStateChange,多个子应用同步数据