BEM(Block Element Modifier)
- Block:表示一个独立的、具有明确功能的实体。例如,.header 或 .nav。
- Element:表示一个 Block 内的元素。它始终与其父 Block 相关联,并使用双下划线分隔。例如,.button__icon 或 .form__input
- Modifier:表示 Block 或 Element 的状态、变体或特性。它使用双连字符分隔。例如,.button–primary 或 .form__input–invalid。
/* 基本 Block */
.button {
background-color: #4CAF50;
border: none;
color: white;
padding: 15px 32px;
text-align: center;
display: inline-block;
font-size: 16px;
margin: 4px 2px;
}
/* Element */
.button__icon {
/* 添加 icon 的样式 */
}
/* Modifier */
.button--primary {
background-color: #008CBA;
}
.button--danger {
background-color: #f44336;
}
CSS Modules
- 每个组件都有独立的 CSS 文件,并在构建过程中为每个类生成唯一的哈希值,以确保样式隔离
// 通过 css-loader 转换 hash 类名
const path = require('path');
const { getLocalIdent } = require('./css-modules');
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js',
},
module: {
rules: [
{
test: /\.css$/i,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
module: true,
getLocalIdent: (loaderContext, localIdentName, localName, options) => {
return getLocalIdent(loaderContext, localIdentName, localName, options);
},
},
},
],
},
],
},
};
// 将类名转换成唯一 hash
const fs = require('fs');
const path = require('path');
const crypto = require('crypto');
function generateHashedClassName(filename, className) {
const content = fs.readFileSync(filename, 'utf-8');
const hash = crypto
.createHash('md5')
.update(content)
.digest('base64')
.substr(0, 5);
return `${className}--${hash}`;
}
function getLocalIdent(loaderContext, localIdentName, localName, options) {
const fileName = path.basename(loaderContext.resourcePath);
return generateHashedClassName(loaderContext.resourcePath, localName);
}
module.exports = {
getLocalIdent
};
Shadow DOM
- 使用 Shadow DOM 技术,组件的 CSS 样式会被限制在组件的局部范围内,从而实现样式隔离。
CSS-in-JS
- 动态生成样式:在 CSS-in-JS 中,组件的样式可以通过 inline JavaScript 对象或模板字符串直接在组件中赋值。这意味着可以根据组件的属性和状态动态调整样式。
- 局部作用范围:由于 CSS-in-JS 将样式直接与特定组件关联,每个组件的样式都具有局部作用范围。通过局部变量和组件生命周期钩子,可以在组件卸载时自动取消应用其样式,避免影响其他组件的样式。
- 唯一的类名:当使用 CSS-in-JS 库(例如 styled-components)时,它们会在运行时为每个组件生成唯一的类名。这使得每个组件的样式完全分离,不会与其他组件发生命名冲突。
- 优化渲染:CSS-in-JS 库会在组件挂载时自动将样式插入到 DOM 中。这样可以确保只有必要的样式被渲染,以提高性能和避免渲染不必要的全局样式。
核心是将唯一样式和元素关联起来
<!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>CSS-in-JS</title>
</head>
<body>
<button id="myButton">Click me!</button>
<script src="css-in-js.js"></script>
</body>
</html>
const myButton = document.getElementById('myButton');
// 生成唯一哈希值
function generateHash() {
return Math.random().toString(36).substr(2, 5);
}
// 创建样式标签
function createStyleTag(css) {
const style = document.createElement('style');
style.type = 'text/css';
style.appendChild(document.createTextNode(css));
document.head.appendChild(style);
}
// 将样式与元素关联
function cssInJs(element, styles) {
const hashedClassName = 'css_in_js_' + generateHash();
element.classList.add(hashedClassName);
const css = `
.${hashedClassName} {
background-color: lightblue;
color: white;
padding: 0.5rem 1rem;
border: none;
font-size: 1.2rem;
cursor: pointer;
}
`;
createStyleTag(css);
}
// 应用样式
cssInJs(myButton);
Scoped CSS
- 使用 Scoped CSS 技术来实现 CSS 隔离,每个组件的样式都会被包裹在一个特定的属性选择器中,避免样式冲突和影响。
const myButton = document.getElementById('myButton');
const styleElement = document.getElementById('css');
// 生成唯一哈希值
function generateHash() {
return Math.random().toString(36).substr(2, 5);
}
// 添加唯一属性
function addScopedAttribute(element, attributeName) {
element.setAttribute(attributeName, '');
}
// 创建 Scoped CSS 的样式
function createScopedCSS(attributeName, css) {
// 将应用到组件的原始选择器类名替换成属性选择器
// .myButton {...} 类样式将变为 .myButton[data-scoped-abcdef] {...}
const scopedCSS = css.replace(/(\.\w+)/g, `$1[${attributeName}]`);
styleElement.textContent += scopedCSS;
}
// 实现 Scoped CSS
function scopedCSS(element, styles) {
const uniqueAttribute = `data-scoped-${generateHash()}`;
// 将唯一属性设置到元素上,从而能通过属性选择器匹配
addScopedAttribute(element, uniqueAttribute);
createScopedCSS(uniqueAttribute, styles);
}
// 定义样式
const buttonStyles = `
.myButton {
background-color: lightblue;
color: white;
padding: 0.5rem 1rem;
border: none;
font-size: 1.2rem;
cursor: pointer;
}
`;
// 应用样式
scopedCSS(myButton, buttonStyles);