likes
comments
collection
share

CSS模块化--解决命名冲突等问题

作者站长头像
站长
· 阅读数 17

CSS模块化方案

原因:CSS作用域是全局的,项目越来越大,人越来越多,命名慢慢成为了问题,于是CSS 社区也诞生了相应的模块化解决方案:BEM、Atomic CSS、OOCSS、SMACSS、ITCSS,以及 CSS Modules 和 CSS-in-JS 等。

根据这些 CSS 模块化方案的特点,简单的分为三大类:

  1. CSS 命名方法论:通过人工的方式来约定命名规则。
  2. CSS Modules:一个 CSS 文件就是一个独立的模块。
  3. CSS-in-JS:在 JS 中写 CSS。

CSS模块化--解决命名冲突等问题

CSS 命名方法论

BEM

BEM即为块级元素修饰字符(Block Element Modifier),以 .block__element--modifier 形式命名,即 .模块名__元素名--修饰符名 三个部分,用双下划线 __ 来明确区分模块名和元素名,用双横线 -- 来明确区分元素名和修饰符名。

BEM 的命名规范如下:

/* 块即是通常所说的 Web 应用开发中的组件或模块。每个块在逻辑上和功能上都是相互独立的。 */
.block {}
.card {}

/* 元素是块中的组成部分。元素不能离开块来使用。BEM 不推荐在元素中嵌套其他元素。 */
.block__element {}
.card__head {}
.card__menu {}
.card__menu-item {}

/* 修饰符用来定义块或元素的外观和行为。同样的块在应用不同的修饰符之后,会有不同的外观 */
.block--modifier {}
.card__menu-item--active {}
.card__menu-item--disable {}

通过 BEM 的命名方式严格控制CSS命名去解决命名冲突的问题主要还是人为去遵守命名约束,那么就会有出错的时候。

Atomic CSS

Atomic CSS是原子化的CSS,就是将单一的属性封装一个Class,保证没有重复的样式,这样便于属性的复用,同时也会导致样式复杂的元素Class非常臃肿,所以具体的使用还是需要根据开发场景判断。

例如:

<div class="mt-10 w-100 h-15"></div> 
// mt-10 > margin-top: 10px; 
// w-100 > width: 100px; 
// h-15 > height: 15px;

Tailwind CSS是使用Atomic CSS的一种代表框架,感兴趣大家可以自己实践下。 www.tailwindcss.cn/

CSS Modules

CSS Modules 是用于模块化和组合 CSS 的系统,通过加入局部作用域、依赖管理来避免全局污染和样式冲突,并且 CSS Modules 在打包的时候会自动将类名转换成 hash 值,完全杜绝 css 类名冲突的问题。

特性:

  • 作用域:模块中的名称默认都属于本地作用域,定义在 :local 中的名称也属于本地作用域,定义在 :global 中的名称属于全局作用域,全局名称不会被编译成哈希字符串。
  • 命名:对于本地类名称,CSS Modules 建议使用 camelCase 方式来命名,这样会使 JS 文件更干净,即 styles.className。 但是你仍然可以固执己见地使用 styles['class-name'],允许但不提倡。
  • 组合:使用 composes 属性来继承另一个选择器的样式,这与 Sass@extend 规则类似。
  • 变量:使用 @value 来定义变量,不过需要安装 PostCSSpostcss-modules-values 插件。

启用 CSS Modules

CSS Modules 很容易学。webpack 自带的 css-loader 组件,自带了 CSS Modules,通过简单的配置即可使用。

// webpack.config.js
module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              modules: true,
            }
          }
        ],
      /* 或者这么写
        use:[
           loader: "style-loader!css-loader?modules"
        ]
      */  
      },
    ]
  }
};

自定义生成的类名

css-loader默认的哈希算法是[hash:base64],但是我们可能有需求去自定义想要的类名格式,css-loader 为我们提供了localIdentName 参数指定生成的名字格式。

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              modules: true,
              localIdentName: '[name]__[local]-[hash:base64:5]',
            }
          }
        ],
      /* 或者这么写
        use:[
           loader: "style-loader!css-loader?modules&localIdentName=[path][name]---[local]---[hash:base64:5]"
        ]
      */  
      },
    ]
  }
};

作用域

CSS Modules 通过默认的局部作用域 :local 来实现样式的局部化,使用全局作用域 :global 来实现样式的全局化,同时全局的类名打包不会被编译成哈希字符串。

// 局部样式(可以省略:local)
.normal {
  color: green;
}

:local(.normal) {
  color: green; 
}


// 全局样式
:global(.btn) {
  color: red;
}

然后我们在js中使用就先导入css文件,然后通过导入对象的属性名使用样式


// 导入css
import styles from './Button.css';

console.log(styles);

// 使用normal样式
buttonElem.outerHTML = `<button class=${styles.normal}>Submit</button>`

Compose 组合样式

为了避免代码的冗余,我们需要去把一些样式去复用,而 CSS Modules 也提供了相应的方法 composes 去让我们处理。

/* components/Button.css */
.common { /* 所有通用的样式 */ }

.normal {
  composes: common;
  /* normal 其它样式 */
}

.disabled {
  composes: common;
  
  // 导入别的css文件
  composes: shadow from './author.css';
  /* disabled 其它样式 */
}

import styles from './Button.css';

buttonElem.outerHTML = `<button class=${styles.normal}>Submit</button>`

变量

@value blue: #0c77f8;
@value red: #ff0000;
@value green: #aaf200;

.title{ 
    color:red;
    background-color:blue;
}

CSS in JS

正如我们字面看到的意思,CSS in JS就是在JavaScript中写css,而不是单独的css文件去管理。在JS中使用了,那么 css 中使用一些属于 JS 的如模块声明、变量定义、函数调用和条件判断等语言特性来提供灵活的可扩展的样式定义。

CSS in JS 在react社区的热度是最高的,因为 react 本身不会管用户怎么去为组件定义样式问题,而vue有属于框架自己的一套定义样式的方案。

CSS in JS 目前已经有超过 40 多种方案的实现,最出名的是 styled-components。

组件内写法

// ======== Basic.jsx文件 ==========
import React, { Component } from 'react';
// 导入样式组件
import styled from 'styled-components';

class Basic extends Component {
    render() {
        return (
            <div>
                <Ha>内部文件写法</Ha>
            </div>
        );
    }
}
const Ha = styled.div`
    font-size: 50px;
    color: red;
    background: pink;
    width: 100%;
    height: 100vh;
`

export default Basic;

导入写法

// ======== Card.jsx文件 ==========
// 导入样式组件
import { CardText } from './style';

function Card() {
  return (
    <div className="card">
      Card卡片
      <CardText>外部Card卡片写法</CardText>
    </div>
  );
}

export default Card;

// ======== style.js文件 ==========
// ======== 外部写法(导入样式组件) ========
import styled from 'styled-components';

// const 标签名(首字母大写)= styled.HTML标签名`css样式`
// 导出
export const CardText = styled.div`
    font-size: 50px;
    font-family: 华文行楷;
    color: orange;
    background-color: blue;
    width: 40vw;
`

样式继承

在styled-components中也可以使用样式的继承,有继承和重载两种情况

// ======== style1.js文件 ==========
// ======== 样式继承 ========
import styled from 'styled-components';

// const 标签名(首字母大写)= styled.HTML标签名`css样式`
// 导出
const Fu = styled.div`
    font-size: 50px;
    font-family:'Courier New',Courier,monospace,kai;
    color: green;
    width: 30vw;
`
// 子继承父
// 继承:color 和 font-family子没有,会用父的
// 重载:font-size两者都有,以子为准
const Zi = styled(Fu)`
    font-size: 80px;    
    background: yellowgreen;
    width: 30vw;
`

export { Fu, Zi };

属性传递

在styled-components中可以使用动态传参的方式对部分需要变化的样式进行控制

// ======== ChuanDi.jsx文件 ==========
import React, { Component } from 'react';
// 导入样式组件
import { ChuanDiStyle } from './style2';

class ChuanDi extends Component {
    render() {
        return (
            <div>
                <ChuanDiStyle>原先的样式</ChuanDiStyle>
                <ChuanDiStyle bgColor={'yellow'} Size='30px'>传值的样式</ChuanDiStyle>
            </div>
        );
    }
}

export default ChuanDi;
// ======== style2.js文件 ==========
// ======== 属性传递 ========
import styled from 'styled-components';

// 动态属性传递样式的值
// 没传值就是默认值green,传了值就是值的属性
const ChuanDiStyle = styled.div`
    background: ${(props) => props.bgColor || 'red'};
    font-size: ${(props) => props.Size || '60px'};
    width: 20vw;
`
export { ChuanDiStyle };

参考文章

www.ruanyifeng.com/blog/2016/0… www.jianshu.com/p/9c7b0b36b…