解读微前端的css沙箱

qiankun 的 css 沙箱的原理是重写 HTMLHeadElement.prototype.appendChild 事件,记录子项目运行时新增的 style/link 标签,卸载子项目时移除这些标签。

在子系统卸载的时候,将子系统引入css使用的、标签移除掉。移除的办法是重写标签的appendChild方法,办法类似定时器的重写。 子系统加载时,会将所需要的js/css文件插入到标签,而重写的appendChild方法会记录所插入的标签,然后子系统卸载的时候,会移除这些标签。

注意:无法解决多个子项目同时运行时的 css 污染,以及子项目对主项目的 css 污染

我们可以借助换肤的思路来解决css污染,首先css-scoped解决95%的样式污染,然后就是全局样式可能会造成污染,我们只需要将全局样式用一个id/class包裹着就可以了,这样这些全局样式仅在这个id/class范围内生效。

具体做法就是:在子系统加载时(mount)给加一个特殊的id/class,然后在子系统卸载时(unmount)删掉这个id/class。而子系统的全局样式都仅在这个id/class范围内生效,如果子系统独立运行,只需要在子系统的入口文件index.html里面给手动加上这个id/class即可。 代码如下:

javascript 复制代码
async function mount(props){
  //给body加class,以解决全局样式污染
  document.body.classList.add('app-vue-history')
}
async function unmount(props){
  //去掉body的class
  document.body.classList.remove('app-vue-history')
}

当然了,你写的全局样式也在这个class下面:

less 复制代码
.app-vue-history{
    h1{
        color: red
    }
}

样式的隔离有很多种处理方式,如:BEM、CSS Module、css前缀、动态加载/卸载样式表、Web Components自带隔离机制等。

约定式编程 这里我们可以采用一定的编程约束:

  • 尽量不要使用可能冲突全局的 class 或者直接为标签定义样式;
  • 定义唯一的 class 前缀,现在的项目都是用诸如 antd 这样的组件库,这类组件库都支持自定义组件 class 前缀;
  • 主应用一定要有自定义的 class 前缀;

此次采用添加 css 前缀来隔离样式,比如 postcss 插件:postcss-plugin-namespace。但是这个插件并不满足需求,我们的应用分布在 src/下,并以 name.app 的方式命名,需要给不同的应用添加不同的前缀。因此使用自己定制的插件:

javascript 复制代码
postcss.plugin('postcss-plugin-namespace', function() {
	return function(css) {
		css.walkRules(rule => {
			if (rule.parent && rule.parent.type === 'atrule' && rule.parent.name !== 'media') return
			const filePath = rule.source && rule.source.input.file
			const appName = /src\/(\S*?)\//.exec(filePath)[1] || ''
			const namespace = appName.split('.')[0] || ''

			rule.selectors = rule.selectors.map(s => `#${namespace} ${s === 'body' ? '' : s}`)
		})
	}
})
相关推荐
mCell33 分钟前
AI 时代 SVG 画图的潜力
前端·agent·svg
我命由我123456 小时前
CesiumJS 笔记 - 获取容器中心点、Cartesian3 clone 方法、修改 Cartesian3 对象的高度
前端·javascript·css·前端框架·html·html5·js
Hopebearer_7 小时前
页面突然只剩 DOM?一次静态资源版本错配排查
前端·部署
东风破_8 小时前
ESLint 是什么?为什么你的项目需要它?
前端·后端·代码规范
java1234_小锋9 小时前
Vue3专题 - 组件基础
vue.js·vite
圣殿骑士-Khtangc9 小时前
Go字符串高效拼接性能对比与底层原理分析
服务器·前端·golang
BigTopOne10 小时前
【ijkplayer】 硬解码流程
前端
kyriewen10 小时前
DeepSeek Harness开源第一天我就上手了——和Claude Code的差距比想象中大
前端·ai编程·deepseek
捡田螺的小男孩11 小时前
什么是 Skill?手把手带你写一个简单有用的 Skill!
前端·后端·程序员
IT_陈寒11 小时前
Redis集群这个坑,差点让我通宵
前端·人工智能·后端