跳到主要内容

方案-样式隔离

CSS BEM

CSS 的命名问题向来是个很重要的问题, 不好的命名规则往往会导致混乱的样式代码, 最终腐朽成一堆不可维护的垃圾.

目前来说, CSS BEN 是比较主流的一种命名规范, 有 Yandex 团队提出, 旨在更好的创建 CSS/Sass 模块.

官方网站在这里: https://en.bem.info/

B__E--M

BEM, 即Block__Element--Modifier, 翻译为中文即.块__元素--修饰符{}

其中:

  • block 代表了更高级别的抽象或者组件
  • block__element 代表 block 的后代, 用于形成一个完整的 block 整体
  • block--modifier 代表 block 的不同状态或者不同版本.

由于 css 选择器的性能问题, 我们通常要避免选择器的嵌套超过三层, 因此用一个具体的类名去替代嵌套的选择器组, 是一个比较明智的选择. BEM 就是这样的情境下提出的一种命名规则.

SASS

BEM配合SASS可以避免在我们的代码中写出冗长的样式名称, 并且可以借助mixin等工具更灵活的编写 css 代码. 比如, 使用@at-root内联选择器, 可以帮助我们编写出无嵌套的 CSS(这个很重要):

//这个例子来自于: https://www.jianshu.com/p/54b000099217
.person {
@at-root #{&}__hand {
color: red;
@at-root #{&}--left {
color: yellow;
}
}
@at-root #{&}--female {
color: blue;
@at-root #{&}__hand {
color: green;
}
}
}
/*生成的css*/
.person__hand {
color: red;
}
.person__hand--left {
color: yellow;
}
.person--female {
color: blue;
}
.person--female__hand {
color: green;
}

我们还可以创建一些辅助函数, 来帮助我们编写 bem 代码. 下面是elementui中一些bem的辅助函数:

//首先定义连接符常量和命名空间, 防止css污染
$namespace: 'el';
$element-separator: '__';
$modifier-separator: '--';
$state-prefix: 'is-';

//定义一些工具函数

//选择器裁剪, 除去'.'和
@function selectorToString($selector) {
$selector: inspect($selector);
$selector: str-slice($selector, 2, -2);
@return $selector;
}

//判断是否是modifier
@function containsModifier($selector) {
$selector: selectorToString($selector);

@if str-index($selector, $modifier-separator) {
@return true;
} @else {
@return false;
}
}

//判断是否是`is-`开头的状态
@function containWhenFlag($selector) {
$selector: selectorToString($selector);

@if str-index($selector, '.' + $state-prefix) {
@return true;
} @else {
@return false;
}
}

//判断是否含有伪类
@function containPseudoClass($selector) {
$selector: selectorToString($selector);

@if str-index($selector, ':') {
@return true;
} @else {
@return false;
}
}

//判断所有的特殊类名
@function hitAllSpecialNestRule($selector) {
@return containsModifier($selector) or containWhenFlag($selector) or containPseudoClass($selector);
}

//block 这个简单的函数接受一个字符串生成我们的block域名
@mixin b($block) {
$B: $namespace + '-' + $block !global;

.#{$B} {
@content;
}
}

//element
@mixin e($element) {
$E: $element !global;
$selector: &;
$currentSelector: '';
@each $unit in $element {
$currentSelector: #{$currentSelector + '.' + $B + $element-separator + $unit + ','};
}

@if hitAllSpecialNestRule($selector) {
@at-root {
#{$selector} {
#{$currentSelector} {
@content;
}
}
}
} @else {
@at-root {
#{$currentSelector} {
@content;
}
}
}
}

//modifier 生成modifier类名
@mixin m($modifier) {
$selector: &;
$currentSelector: '';
@each $unit in $modifier {
$currentSelector: #{$currentSelector + & + $modifier-separator + $unit + ','};
}

@at-root {
#{$currentSelector} {
@content;
}
}
}

CSS Module

CSS Module 指的是我们向import js一样去引入我们的CSS代码, 代码中的每一个类型都是引入对象的一个属性, 通过这种方式制定所引用的CSS样式.

并且CSS Modules在打包的时候会自动将类名转换成hash值, 完全杜绝CSS类型冲突的问题

使用

  1. 定义CSS文件
.className {
color: green;
}
/* 编写全局样式 */
:global(.className) {
color: red;
}

/* 样式复用 */
.otherClassName {
composes: className;
color: yellow;
}

.otherClassName {
composes: className from "./style.css";
}
  1. 在js中导入css文件
import styles from "./style.css";

element.innerHTML = '<div class="' + styles.className + '">';
  1. 配置css-loader: css module 需要配置loader对css文件进行处理, 打包为一个对象.
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use:{
loader: 'css-loader',
options: {
modules: {
// 自定义 hash 名称
localIdentName: '[path][name]__[local]--[hash:base64:5]',
}
}
}
]
}
};
  1. 最终打包得到的css类型就是一串hash值
._2DHwuiHWMnKTOYG45T0x34 {
color: red;
}

._10B-buq6_BEOTOl9urIjf8 {
background-color: blue;
}

CSS in JS

这种方案意思就是用js语言去写css, 完全不需要独立的css文件, 所有的css代码放在组件内部, 以实现css的模块化.

CSS in JS是一种编写思想, 目前有超过40中的实现方案, 最出名的大概是styled-components.

使用大致如下:

import React from "react";
import styled from "styled-components";

// 创建一个带样式的 h1 标签
const Title = styled.h1`
font-size: 1.5em;
text-align: center;
color: palevioletred;
`;

// 创建一个带样式的 section 标签
const Wrapper = styled.section`
padding: 4em;
background: papayawhip;
`;

// 通过属性动态定义样式
const Button = styled.button`
background: ${props => (props.primary ? "palevioletred" : "white")};
color: ${props => (props.primary ? "white" : "palevioletred")};

font-size: 1em;
margin: 1em;
padding: 0.25em 1em;
border: 2px solid palevioletred;
border-radius: 3px;
`;

// 样式复用
const TomatoButton = styled(Button)`
color: tomato;
border-color: tomato;
`;

<Wrapper>
<Title>Hello World, this is my first styled component!</Title>
<Button primary>Primary</Button>
</Wrapper>;

参考链接