微前端应用隔离

微前端中 CSS 和 JavaScript 的隔离方案

问题

微前端架构中,如何实现主应用与微应用、微应用与微应用之间的隔离?

解答

应用隔离主要包括 CSS 样式隔离和 JavaScript 执行环境隔离两个方面。

CSS 隔离

主应用与微应用之间

当主应用和微应用同屏渲染时,样式可能相互污染。可以采用以下方案:

  1. CSS Module:模块化的 CSS 方案
  2. 命名空间:给每个微应用添加特定前缀

使用 webpack 的 postcss 插件可以在打包时自动添加前缀:

// postcss.config.js
module.exports = {
  plugins: [
    require('postcss-prefix-selector')({
      prefix: '.micro-app-prefix',
      exclude: ['.ignore-prefix']
    })
  ]
}

微应用与微应用之间

在应用加载时标记所有的 linkstyle 标签,卸载时同步移除:

// 加载时标记
function loadApp(appName) {
  const styles = document.querySelectorAll('link, style');
  styles.forEach(style => {
    style.setAttribute('data-app', appName);
  });
}

// 卸载时清理
function unloadApp(appName) {
  const styles = document.querySelectorAll(`[data-app="${appName}"]`);
  styles.forEach(style => style.remove());
}

JavaScript 隔离

微应用的 JavaScript 运行时会修改全局对象 window,例如 jQuery 会挂载 window.$,React、Vue 也会有类似行为。

沙箱机制(SandBox)

沙箱让局部 JavaScript 对外部对象的访问和修改处在可控范围内,确保内部运行不影响外部对象。

浏览器端实现需要结合 with 关键字和 Proxy 对象:

class SandBox {
  constructor() {
    this.proxy = null;
    this.running = false;
  }

  active() {
    this.running = true;
    const fakeWindow = Object.create(null);
    
    this.proxy = new Proxy(fakeWindow, {
      get(target, prop) {
        return prop in target ? target[prop] : window[prop];
      },
      set(target, prop, value) {
        target[prop] = value;
        return true;
      }
    });
  }

  inactive() {
    this.running = false;
  }
}

// 使用
const sandbox = new SandBox();
sandbox.active();
// 在 sandbox.proxy 环境中执行微应用代码
sandbox.inactive();

关键点

  • CSS 隔离可通过命名空间或 CSS Module 实现,打包时自动添加前缀
  • 微应用卸载时需同步清理其注入的样式标签
  • JavaScript 隔离采用沙箱机制,使用 Proxy 拦截对全局对象的访问
  • 沙箱确保微应用的全局变量修改不会影响主应用和其他微应用