背景
在做工作台应用中,整个页面是由远程组件组成的(如下图),远程组件本质上是打包了vue组件,然后通过 requirejs
动态加载组件并且注册到页面上。 所以本身会带来跟微前端很类似的问题。
- Js没隔离。 组件可以修改window对象、组件内部也可以重写内置对象方法... ❌
- css没隔离。 组件内的样式不加scoped的话,样式各种污染...
下面这种图很清晰的展示了组件A、组件B都有一个.container
class命名,并且没有scoped的话。一个设置了背景色为red
,一个设置了blue
。这样的话就会产生污染。而且跟随网络加载状态
,后注册的远程组件的样式将优先级更高。
思考
微前端的CSS隔离主要有两种:
- 样式增加不同前缀
- ShadawDom
这里毫无疑问增加不同前缀
是最简单、最快的方式了。
解决(配置)
分解
要想把组件内的css都加个前缀的话,要自己在编译时做处理那就是很麻烦的事情(投入产出比不是很高),所以第一反应还是去找编译Vue时候有没有入口。我们是Vite+Vue3
项目,项目中配置如下
json
// ...其他配置
plugins: [
vue({
template: {
compilerOptions: {
isCustomElement: (tag) => tag === 'micro-app'
}
},
include: [/\.vue$/, /\.md$/], // <--
}),
// ...其他配置
],
查看@vitejs/plugin-vue
配置
可以很敏感的发现有一个自定义的template配置。那必然还有一个style配置了。果然是有(如下图)。然后查看SFCStyleCompileOptions
。当看到postcssPlugins
时候,就可以直接飞起了。postcss插件。css界的Babel.
ts
export declare interface SFCStyleCompileOptions {
source: string;
filename: string;
id: string;
scoped?: boolean;
trim?: boolean;
isProd?: boolean;
inMap?: RawSourceMap;
preprocessLang?: PreprocessLang;
preprocessOptions?: any;
preprocessCustomRequire?: (id: string) => any;
postcssOptions?: any;
postcssPlugins?: any[];
/**
* @deprecated use `inMap` instead.
*/
map?: RawSourceMap;
}
寻找插件并配置。
那现在的问题就是寻找postcss插件,postcss生态还是很好的。 postcss-prefix-selector
。然后进行配置。组件内的css都加了属性选择器作为前缀 [data-hurricane-code="${组件ID}"]
。
json
plugins: [
extractElComponentsPlugin(),
vue({
template: {
compilerOptions: {
isCustomElement: (tag) => tag === 'micro-app'
}
},
include: [/\.vue$/, /\.md$/], // <--
style: {
postcssPlugins: [
postcssPrefixSelector({
prefix: `[data-hurricane-code="${COMPONENT}"]`, // 添加前缀选择器
exclude: [/\.portal-ignore/],
includeFiles: [`${resolve(__dirname, '../packages')}`]
})
]
}
}),
],
查看编译后结果
css
.table{
width: 100%;
}
.swwwwww {
background-color: red;
height: 100px;
}
=>
[data-hurricane-code=TestSww] .table[data-v-394bd0c5] {
width: 100%;
}
[data-hurricane-code=TestSww] .swwwwww[data-v-394bd0c5] {
background-color: red; height: 100px;
}
现在变成这样隔离了。
但是有些需要忽略的,可以根据规则自定义传入忽略的className
。可以自行配置。