qiankun微前端库-沙箱(sandBox)原理

qiankun通过微前端技术将多个应用通过子应用集合在一块,为了避免js和css这两大维度的冲突,采取了沙箱机制来进行隔离,使应用之间的环境相互独立,互不干扰。

1. 沙箱的主要功能

  • 隔离全局变量: 防止子应用之间的全局变量冲突,eg:windownavigator
  • 样式隔离: 确保子应用的样式只作用于自身,不会影响其他子应用或主应用。

  • JS 执行环境隔离: 确保子应用的 JavaScript 代码不会影响其他子应用或主应用。
  • 模块作用域隔离:子应用的模块(如 VueReact)不污染全局命名空间。

2. ‌JS沙箱机制

2.1 快照沙箱(静态隔离)

 在子应用挂载时,生成当前全局(Windows)变量的快照;子应用运行时,将对变量的修改存入modifyPropsMap;在子应用卸载时,恢复这些全局变量到之前的状态。仅支持单子应用运行(同一时间仅运行一个子应用)‌ 。

2.2 ‌Proxy代理沙箱(动态隔离)

Proxy代理沙箱是主流方案

 使用 Proxy 对象来拦截对全局变量的读写操作,从而实现隔离。为每个子应用创建代理对象fakeWindow,通过Proxy拦截对window的读写操作。支持多子应用并行运行(多例模式)‌

3.css隔离机制  

CSS是 根据选择器去全局匹配元素的,所以微前端应用之间很容易造成css样式冲突,这个时候就要实现css隔离

1 qiankun自带的隔离机制

1.1 experimentalStyleIsolation

自动为子应用的根 DOM 元素添加 data-qiankun-<app-id> 属性,使样式仅作用于当前子应用容器内部‌。动态为子应用的样式选择器添加唯一前缀(类似 Vue 的 scoped 属性),例如将 .button 转换为 [data-qiankun-app-id] .button

轻量级隔离,兼容性好,但无法隔离全局样式(如 body 标签)‌

若子应用样式权重不足(如使用 :where 选择器),可能被主应用覆盖‌

start({sandbox: {
  enable: true,
  experimentalStyleIsolation: true, // 加强样式隔离
}})

 1.2 strictStyleIsolation(未来版本的qiankun可以会移除这个属性

通过浏览器原生的 Shadow DOM 将子应用包裹在独立的 DOM 树中,外部样式无法渗透,且内部样式默认不溢出到主应用。子应用无需额外配置,子应用的样式和 DOM 自动被隔离在 Shadow DOM 内。

隔离彻底,但可能破坏依赖全局 DOM 的组件(如弹窗)‌

部分第三方库(如 Ant Design)在 Shadow DOM 中可能失效‌

start({sandbox: {
  enable: true,
  strictStyleIsolation: true,       // 启用 Shadow DOM
}})

2 其它常用的css隔离机制

2.1 CSS-in-JS

将样式写在 JavaScript 中,通过动态生成唯一的类名实现作用域隔离。

  • 实现方式:使用库如 styled-components 或 emotion
import styled from 'styled-components';

const StyledButton = styled.button`
  background-color: tomato;
  padding: 10px;
`;

function Button() {
  return <StyledButton>Click Me</StyledButton>;
}
2.2  类名作用域(Scoped CSS)

为子应用的类名添加唯一前缀或哈希值,确保样式仅作用于自身组件。

  • 实现方式:通过构建工具(如 Webpack 的 css-loader)或框架特性(如 Vue 的 <style scoped>)生成唯一类名。
  • Vue 的 <style scoped>通过 PostCSS 为组件内所有 DOM 元素添加唯一属性(如 data-v-xxxx),并修改 CSS 选择器为属性匹配模式‌。
2.3 CSS Module​

 它是一种将css利用模块化的思维来管理样式的一种 理念,每个文件都是一个独立的模块 ,这样可以产生局部作用域,通常使用Webpack等构建工具来实现。每个CSS文件中的选择器都会被添加一个唯一的前缀,即使两个文件中定义了相同的名字,也不会发生冲突。具体来说,Webpack会为每个CSS文件中的类名生成一个唯一的前缀,例如[path][name]__[local]--[hash:base64:5]

配置示例(Webpack)

// webpack.config.js
module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              modules: {
                auto: true, // 或 auto: /.*\.module\.css$/, 仅处理特定文件
                localIdentName: '[hash:base64:5]', // 哈希格式
              },
            },
          },
        ],
      },
    ],
  },
};

使用示例(React 或 Vue)

// 组件文件(Button.module.css)
/* Button.module.css */
.button {
  background-color: skyblue;
}

// 组件文件
import styles from './Button.module.css';

function Button() {
  return <button className={styles.button}>Click Me</button>;
}
<!-- App.vue(子应用) -->
<template>
  <div :class="$style.container">Content</div>
</template>

<style module>
.container { padding: 20px; }
</style>
 2.4 BEM

 (Block__Element--Modifier)是一种组件化命名规范,通过明确的语法结构避免 CSS 类名冲突。

Block(块):独立的、可复用的功能模块(如按钮、卡片、表单),命名以描述性词汇为基础(如 .header 或 .search-form)‌。

Element(元素):块内组成部分(如按钮的文字、输入框的输入区域),块名__元素名‌。

Modifier(修饰符):块或元素的变体状态(如按钮的“禁用”状态(.button--disabled)、卡片的“主题颜色”)命名格式为 块名--修饰符 或 块名__元素名--修饰符

/* CSS 样式 */
/* Block(基础样式) */
.button {
  padding: 8px 16px;
  background: #fff;
  border: 1px solid #ccc;
}

/* 块的修饰符(主色按钮) */
.button--primary {
  background: royalblue;
  color: white;
}

/* 元素(文字部分) */
.button__text {
  font-weight: bold;
}

/* 元素的修饰符(文字大写) */
.button__text--uppercase {
  text-transform: uppercase;
}

/* 元素(图标) */
.button__icon {
  margin-left: 8px;
}

/* 元素的修饰符(特定图标类型) */
.button__icon--arrow {
  content: "→"; /* 具体图标代码 */
}
 2.5 Shadow DOM

浏览器原生提供的 DOM 隔离机制,允许将一段 DOM 树(称为 Shadow Tree)附加到主文档中,形成一个独立的作用域 ,内部的样式默认不会溢出到外部,外部样式也不会影响内部元素。外部无法直接访问 Shadow DOM 的内部结构(除非显式开放)

  • 两种模式
    • Open Mode:允许外部 JavaScript 访问 Shadow DOM 内容({ mode: 'open' })。
    • Closed Mode:完全隔离,外部无法访问({ mode: 'closed' },默认)。

// 获取宿主元素(如 div)
const hostElement = document.getElementById('my-component');

// 创建 Shadow Root(Open 模式)
const shadowRoot = hostElement.attachShadow({ mode: 'open' });

// 在 Shadow Root 中添加内容
shadowRoot.innerHTML = `
  <style>
    .button { background: red; }
  </style>
  <button class="button">Click Me</button>
`;

在 qiankun中的实现

<!-- 主应用入口 -->
<template>
  <div id="subapp-container"></div>
</template>

<script>
export default {
  mounted() {
    const container = document.getElementById('subapp-container');
    const shadowRoot = container.attachShadow({ mode: 'closed' });

    // 动态注入子应用内容(如通过 Qiankun)
    shadowRoot.innerHTML = `
      <style>
        body { margin: 0; } /* 仅作用于 Shadow DOM 内 */
      </style>
      <div id="real-subapp-container"></div>
    `;
    
    // 挂载子应用到 real-subapp-container
    // (此处调用 Qiankun 的子应用加载逻辑)
  },
};
</script>
import { registerMicroApps } from 'qiankun';

registerMicroApps([
  {
    name: 'vue-subapp',
    entry: '//localhost:8080',
    container: '#subapp',
    activeRule: '/vue',

    // 创建 Shadow DOM 容器
    loader: () => {
      const container = document.getElementById('subapp');
      container.attachShadow({ mode: 'closed' }); // 创建 Shadow DOM
    },
    // 卸载时清理
    unload: () => {
      const container = document.getElementById('subapp');
      container.shadowRoot?.innerHTML = ''; // 清除内部内容
    },
  },
]);
<template>
  <div>
    <h1 style="color: red">子应用内容</h1>
    <button class="global-button">全局样式?不了,我用 Shadow DOM!</button>
  </div>
</template>

<script>
// 子应用样式完全隔离在 Shadow DOM 内,此处的 .global-button 不会污染主页面
</script>

4. qiankuncss样式隔离组合方案实践(推荐)

为最大程度降低冲突,建议组合使用以下技术

  1. 核心隔离方式
    • 主应用:为子应用容器添加 experimentalStyleIsolation‌。
    • 子应用:在组件中使用 CSS Modules 或 CSS-in-JS
  2. 全局样式
    • 如果需定义全局样式,通过 BEM 规范命名(如 .subapp-card)。
  3. 第三方库处理
    • 对于依赖全局样式库(如 Bootstrap),在子应用中使用 <style scoped> 或单独打包 CSS。

 5 路由隔离

registerMicroApps 的 activeRule 配置精准控制路由的跳转

资源隔离

沙箱的 globals 和生命周期钩子 mount/unmount,隔离第三方库和全局状态。 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值