Libertus Chen-U
  1. 1 Libertus Chen-U
  2. 2 Time Bomb Veela
  3. 3 かかってこいよ NakamuraEmi
  4. 4 One Last You Jen Bird
  5. 5 Last Surprise Lyn
  6. 6 Warcry mpi
  7. 7 Flower Of Life 发热巫女
  8. 8 Hypocrite Nush
  9. 9 The Night We Stood Lyn
  10. 10 Quiet Storm Lyn
  11. 11 Life Will Change Lyn
2018-06-02 18:47:24

CSS模块化管理方案综述

前端发展这么多年来,由于babel,webpack等工程化工具的快速发展,JS的模块化管理能力已经远远强于CSS,另外也有很大一部分原因在于JS本身作为一门编程语言就有自己的作用域,而CSS本身的特性决定了其规则都是全局作用域。但如果要实现项目的工程化,这又是不得不解决的一个难点,下面针对CSS发展以来一些比较流行合理的模块化方案分别做一个思路梳理。

预处理器

CSS难以管理的本质还是这门语言缺少相对应的编程能力,当语言能力不足时,很自然的就想到通过另一门语言开发,然后编译到该底层再运行的选项。这就是预处理器的目的:通过增强的语法赋予CSS更强的编程能力。常用的预处理器有Less,Sass,Stylus,也都是大同小异,关键的能力都是有的。

文件切分

传统的CSS如果要切分文件只能使用原生的@import指令或者加载多个CSS文件,但这些方法都会增加http请求导致性能问题,预处理器拓展了@import的能力,它将不同的css从语义上导入,最终将编译成一个CSS文件。这也就给予了我们切分模块管理CSS的能力。

变量,函数,mixin

增强CSS的抽象和复用能力,相当于补全了CSS缺少了通用编程语言功能,让代码更加DRY。

嵌套

可以让一系列CSS规则呈现出类似于HTML的层级关系,使得CSS更加易读。

BEM

除了通过增强CSS本身的能力实现模块化之外,通过语义化约束的方式也是一种方案。BEM就是最为流行的该类方案,简单来说就是通过模块(block)+元素(element)+修饰符(modifier)的命名规范来确保模块的唯一性和可重用性。

block

一个块就是一个组件, 该命名应该具有唯一的意义,无论是语义或是视觉表现上的。比如模态框或者列表等。块的命名应该遵循如下原则:

  • 只能使用class
  • 每一条规则必须属于一个块

比如一个列表块的CSS命名如下:

.list {

}

element

块中的子元素,类名必须用父级块的名称作为前缀,通常使用双下划线连接。比如上例列表块中的某个列表项元素则可以命名如下:

.list__item {

}

modifier

修饰符则是一个块的特定状态,通常前缀必须为块或者元素,通常用双横线连接。比如一个选中的列表,或者一个蓝色背景的列表项:

.list--selected {

}

.list__item--blue {

}

小结

简单来说BEM作为一种命名约束,当然会存在类名过长,命名繁琐等问题,应该可以归类为轻管理方案,并不和其他模块化方案互斥,可以视情况配合其他模块化方案使用。

CSS Modules

可以看到以上的方案虽然在一定程度上实现了模块化,但并没有在语法上实现真正的封装,CSS作用依然是全局的,还是需要人为的注意class命名的不重复,而这些在工程化项目的的最佳实践理应由工具去解决。CSS Modules就是为此而生的。

原理

CSS Modules根据localIdentName使用混淆算法生成一个原class和混淆class的映射对象来保存CSS和JS的模块依赖关系,并且由于混淆class的独一无二,相当于创建了一个CSS的局部作用域,这样就有了模块化最重要的两个要素:依赖管理和局部作用域。通常配合Webpack配置localdentName构建。

作用域修改

CSS Modules默认会使用:local装饰所有class实现局部作用域,可以通过:global修改为全局。

/* 定义局部 */
.normal {

}

:local(.normal) {

}

/* 定义全局 */
:global(.normal) {

}

样式复用

CSS Modules只提供了composes组合实现样式复用,也支持外部样式复用。实例如下:

.base {

}

.base-compose {
  composes: base;
}

外部复用

/* another.css */
.add {

}

/* main.css */
.base {
    composes: add from './another.css';
}

变量依赖

CSS Modules的新增伪类:export可以实现变量的输出。

/* another.scss */
$blue: #0c77f8;
:export {
    blue: $blue;
}

/* main.js */
import style from './another.scss';
style.blue // '#0c77f8'

总的来说,CSS Modules可以看做是增强版的自动化BEM并且赋予了使用JS管理的能力。

CSS IN JS —— Styled Components

以上方案其实都可以看做是同一类CSS模块化方案——旨在增强CSS本身的能力。而这一类方案则从出发点上就不一样——通过JS完全代理CSS的编写,在这一类方案的代表实现就是React的Styled Components了。

思想

其将React中的组件按样式应用与否分为逻辑组件和展示组件,顾名思义,通过解耦为两类组件,我们可以更专注于JS中样式的编写。使用方法如下:

实例

import React from 'react';
import styled from 'styled-components';
import { render } from 'react-dom';

const SpanView = styled.span`
    font-size: 1.5rem;
`;

class App extends React.Component {
    render() {
        return (
            <SpanView>Hello</SpanView>
        );
    }
}

简单使用就是如此,可以看到我们能直接通过JS变量定义样式,再结合JSX,相当于HTML,CSS,JS全部统一了编写模式并聚合到了一个组件中,算是做到了真正符合直觉的以组件为粒度的模块化。这里就不做具体介绍,更多的使用方法可以查阅官方文档。

对比

相比于第一类方案,优缺点也是显而易见的。

优点:

  • 组件内样式全部使用JS管理更加灵活易用。
  • 跨平台的良好支持,尤其是本身没有CSS的运行环境。

缺点:

  • 不易结合CSS的生态工具,例如postcss等。
  • 无法利用CSS本身的特性,比如层叠等。

总结

综上可以看出,没有哪一类模块化管理方案现在是完美的,还是要根据具体项目的特性来决定。

-- EOF --

添加在分类「 前端开发 」下,并被添加 「CSS」「JavaScript」「工程化」 标签。