css样式隔离
qiankun现成方案
动态样式(默认)
单实例时通过动态添加/移除子应用的根元素(包含css style),避免各子应用之间的样式冲突. 但主子应用的样式无法避免.
多实例时子应用无法避免
TIP
script标签被移除时,已被浏览器解析的代码并不会受影响
<html lang="en">
<body>
<!-- 移除掉第一个script -->
<script>
var a = 1
let = document.getElementsByTagName('script')[0]
script0.parentElement.removeChild(script0) // 移除第一个script标签
setTimeout(() => {
console.log(2) // 1000ms后依旧执行,证明script标签被移除代码仍会被执行
}, 1000)
</script>
<script>
console.log(a) // 1 a变量依然可以输出
</script>
</body>
</html>
Scoped CSS 作用域样式隔离
通过为子应用css选择器添加**前缀: div[data-qiankun="应用名"]**的方式,实现css样式相互不影响
// 实现原理
let style0 = document.getElementsByTagName('style')[0]
// console.log(style.sheet.cssRules) // 伪数组,每条选择器及其css样式
let arr = []
let prefix = 'div[data-qiankun="test"] ' // 前缀
let css = ''
style0.sheet.cssRules.forEach(({ cssText }, index) => {
cssText = cssText.replace(/(.+)[^{]?/, `${prefix}$1\n`)
css += cssText
})
console.log(css)
style0.textContent = css // 【赋值后样式生效】
cssRules
缺点
由于给子应用所有的css选择器都添加了特定前缀,一些插入到body的DOM就无法应用到这些样式,例如:ElementUI的弹窗Dialog
scopedCss后,v-modal样式丢失
scopedCss后,v-modal样式选择器被加前缀,此时得append到子应用自身DOM中遮罩层css才生效
解决
将dialog的遮罩层放到子应用自身当中, 即:modal-append-to-body="false"【子应用需改造dialog属性,侵入性太大,不可取】
单独加载Element css样式,使用
excludeAssetFilter
配置不被乾坤处理该样式【待实验】
Shadow DOM 样式隔离
通过shadow DOM隔离
const { innerHTML } = appElement
appElement.innerHTML = ''
let shadow = appElement.attachShadow({ mode: 'open' }) // 【主要调用DOM原生的attachShadow API创建shadow DOM环境】
shadow.innerHTML = innerHTML
缺点
由于给子应用应用了shadowDOM特性,css被强隔离,内部样式无法影响外部,一些插入到body的DOM就无法应用到这些样式,例如:ElementUI的弹窗Dialo
各应用避免css冲突流行方案
1、BEM命名约定
每个项目的css选择器添加特定前缀
2、Css Module
模块化CSS. 给每一个dom都给了一个独立的Hash, 可作为scoped CSS的替代方案
TIP
Css Module
需要在vue的template(jsx语法)中进行 className 的动态绑定.
需要先编写样式而不是先编写元素结构和定义
在 className 写法上由于需要使用获取对象属性的写法,会导致一些使用连字符的样式类名需要用中括号才行【这点可配置css-loader解决】
// vue.config.js
// vue-cli天然支持css module;以下配置不写也可以
// css module与scoped不支持同时使用
css: {
requireModuleExtension: false, // 不用指定module后缀
loaderOptions: {
css: {
modules: {
localIdentName: '[name]-[hash]' // 编译后的真正的类名
},
localsConvention: 'camelCaseOnly' // 只转为驼峰(camelCase: 保留原选择器的基础上还增加驼峰选择器)
}
}
},
// 将css打包成js对象,在template中使用
// xxx.vue
<template>
// class的值会被编译成:test-31b137f69a6fb76865813bdcf69edf32
<div :class="$style.test" >
123 红色
</div>
</template>
<style module>
.test {
color: red
}
// $style为css modules对象
{ "test": "test-31b137f69a6fb76865813bdcf69edf32"}
</style>
3、css in js
react中的流行方案;用js写css
// styled-components:react的css in js流行库
import styled from 'styled-components'
const Button = styled.button`
width: 100px;
height: 100px;
color: red;
`
function App() {
// react对HTML的封装是 JSX 语言
return (
<div className="App">
<Button>123</Button>
</div>
);
}
export default App
4、scoped css
vue中的流行方案。通过为子应用css选择器添加前缀的方式,实现css样式相互不影响