子应用之间的样式隔离方案
- Dynamic Stylesheet 动态样式表,档应用切换时移除老应用样式,添加新应用样式
1 主应用和子应用之间的样式隔离
1.1 BEM(Block Element Modifer) 约定项目前缀
-
BEM 本质上是css的一种命名方案,是最流行的命名规则之一
- Block: 块, 一般是元素的父级模块作为前缀,命名规范
.block
- Element: 元素, 命名规范,元素名称可以包含拉丁字母,数字,破折号和下划线
.block__element
- Modifer: 修饰符,命名规范,修饰符名称可以包含拉丁字母,数字,破折号和下划线,
.block--modifier
- Block: 块, 一般是元素的父级模块作为前缀,命名规范
-
BEM 特点:
- 复杂甚至稍显冗长的类名极大减少了类名重复的可能性
- 每个块理的一类元素的样式对应一个类名。如此,一个元素对应一个类名,减少了子选择器或者后代选择器的使用,提升了css的性能
- css 类名的命名更加语义化,更容易读懂
- 可复用性高
.block {}
.block__element {}
.block--modifier {}
1.2 CSS-Modules 打包时生成不冲突的选择器名
- css modules 允许我们想import一个js模块一样的去引用css模块。每个css文件都是一个独立的模块,每个类名都是该模块所导出对象的一个属性。通过这种方式,便可以在使用时明确的指定所引用的css样式。
- css modules 在打包时会自动将id和class混淆成全局唯一的hash值,从而避免了发生命名冲突的问题
- css modules 的特点:
作用域
: 模块中的名称默认都属于本地作用域,定义在:local
中的名称属于本地作用域,定义在:global
中的名称属于全局作用域,全局名称不会被变异成哈希字符串命名
:对于本地类名称,css modules 建议使用camelCase方式来命名,这样会使js文件更干净,即style.className
.组合
:使用compose属性来继承另一个选择器的样式,这与sass的@extend
规则类似变量
: 使用@value
来定义变量,不过需要安装Postcss 和postcss-modules-values
插件来支持
/* 全局样式 */
// index.css
:global(.title) {}
.box {...}
/* 引用方式 */
// App.js
import React from 'react'
import styles from './index.css'
export default function App() {
return (
<div className={styles.box}>
<h3 className="title">use css modules<h3>
</div>
)
}
/* webpack 打包之后的编译结果 */
<div className="style__box-ht21N">
<h3 className="title">use css modules</h3>
</div>
- 怎么在项目中启用css modules? (配置webpack 中的 css-loader)
// webpack.config.js
module.exports = {
...
{
test: /\.(c|sa|sc)ss$/i,
exclude: /node_modules/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 2,
// 开启 css modules
modules: true,
localIdentName; '[path][name]__[local]--[hash:base64:5]'
}
},
'postcss-loader',
'sass-loader',
]
}
...
}
1.3 Shadow DOM 真正意义上的隔离
-
通常情况下,DOM tree 分为两类: Light tree(一般的DOM树, 由HTML元素组成); Shadow tree(一个隐藏的DOM子树,不在HTML 显示)
- 如果一个元素同时拥有两种DOM tree, 则浏览器只渲染Shadow tree
-
通常在自定义元素中使用Shadow tree, 目的是为了隐藏组件内部结构和有效的样式
-
Shadow DOM
允许将隐藏的DOM树附加到常规的DOM树中,它以shadow root 节点为起始根节点,在这个根节点的下方,可以是任意元素,和普通的dom元素一样 -
通过调用element.attachShadow({mode “open”/“close”}) 来创建一个Shadow root
- 每个元素中只能有一个shadow root
- element 原属必须是自定义元素,或者是以下元素中其中一个:
article
、aside
、blockquote
、body
、div
、footer
、h1~h6
、header
、main
、nav
、p
、section
、span
。其他元素不能容纳shadow tree - mode 选择的值为"open"和"close",
open
时, shadow root 可以通过element.shadowRoot访问;close
时,element.shadowRoot 永远为null
-
shadow dom 是元素对于light dom 中的querySelector 不可见。实际上,shadow DOM 中的元素可能与light dom 中的某些元素的id冲突。这些元素必须在shadow tree 中独一无二
-
shadow dom 有自己的样式,外部样式规则不会在shadow中生效
-
获取shadow dom 内部元素,我们可以从树内部查询
-
拥有自己的id空间
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Shadow DOM use</title>
</head>
<script>
customElements.define('shadow-use', class extends HTMLElement {
connectedCallback() {
const shadow = this.attachShadow({mode: "open"})
shadow.innerHTML = "shadow dom 元素"
const div = document.createElement("div")
div.innerHTML = "light dom 元素"
this.append(div)
}
})
</script>
<body>
<shadow-use></shadow-use>
</body>
</html
1.4 css-in-js (js 写css)
- react 的出现,使
关注点分离
的开发原则不再使用,react 是组件结构,强制要求开发者将HTML、CSS、Javascript 写在一起,React 在js里面实现了对HTML和CSS 的封装,react对HTML的封装是JSX语言;对CSS的封装沿用了DOM的style属性对象,此外出现了一些用来加强react的css操作,这些第三方库统称为css-in-js,各种css-in-js 库将近有47种: https://github.com/MicheleBertoli/css-in-jspolished.js
aphrodite
babel-plugin-css-in-js
babel-plugin-pre-style
bloody-react-styled
csjs
- …