React中CSS Modules的使用

栏目: CSS · 发布时间: 5年前

内容简介:最近项目开始换React的工程,感觉好多东西都得重新开始,特别在撸CSS的时候。说实话和Vue的工程相比,体感差完了。在Vue的工程中除了比如最简单的一个按钮组件,居然要这么撸:

最近项目开始换React的工程,感觉好多东西都得重新开始,特别在撸CSS的时候。说实话和Vue的工程相比,体感差完了。在Vue的工程中除了 Modules之外还可以使用CSS的作用域 scoped 的概念。用久了Vue的同学,在这方面的感觉爽死了,但是突然切到React的工程体系之下,这方面的感觉突然不要不要。拿个实例来说吧(可能我做得不到位),组件的CSS是全局的,有时覆盖起来,除了蛋疼之外,而费时,费成本。

React项目中写CSS的姿势

比如最简单的一个按钮组件,居然要这么撸:

React中CSS Modules的使用

虽然为每个组件创建一个单一的 .scss 文件,并在入口引入相应的样式文件,发现React中的CSS没有域的概念,是全域的。

React中CSS Modules的使用

很多时候需要去覆盖组件初始样式,不得不重新定义样式类或提高选择器权重来处理。着实的蛋疼。除此之外,项目是多人开发,各种各样的类名都有,未统一起来,从覆盖上也增加了不少工作量。另外,还会碰到一些常见的CSS问题,比如:

  • 全局污染
  • 命名混乱
  • 依赖关系复杂
  • 无法共享变量
  • 代码冗余,难维护

所以最近在重新考虑如何在React项目中编写CSS。以便找到一条更适合自己甚至团队编写、维护CSS的方式:

  • 行内样式(在JS中写CSS,最终样式编译到标签元素的 style 内)
  • CSS-in-JS,较为流行的有 styled-componentsstyled-jsxreact-style
  • CSS功能模块(Functional CSS),比如 tailwindcsstachyons 等(怎么看都有点类似早期的OOCSS)
  • CSS Modules

初步对比了React中编写CSS的几种方式,我个人更趋向于CSS Modules,不过在React项目中配置CSS Modules要比在PostCSS或者Vue中配置复杂的多(主要还是自己对Webpack太弱)。这次在配置的时候踩了一些坑,特意梳理一下,以备后用。

我要的目标

习惯了Vue的开发,开始撸React还真不习惯。而我想要的目标是:

React中编写CSS能不能像Vue一样,有作用域的概念,组件的CSS只作用于相应的组件,并不会影响其他组件。

为什么选择CSS Modules

时至今日都在提模块化管理,而前面提到的在React中处理样式的方案都有模块化的身影。而CSS模块化的解决方案主要分为:

  • 彻底抛弃CSS,使用JavaScript或JSON来写,比如这两年聊得多的CSS-in-JS。其优点是能给CSS提供JavaScript同样强大的模块化能力;缺点是不能使用CSS处理器以及较难处理伪类选择器的样式
  • 依旧使用CSS,但借助JavaScript来管理样式的依赖关系,比如我们今天要聊的CSS Modules。其最大的特点就是最大化的结合了CSS生态和JavaScript模块化能力

不管是哪种CSS模块化,其主要目的是解决:

  • CSS样式的导入和导出:灵活的按需导入以便最大化的复用代码;导出时能隐藏内部作用域,以免造成不必要的全局污染
  • 解决CSS的编程能力:虽然CSS的处理器(比如Sass、LESS、Stylus和PostCSS)赋予了CSS一些编程能力,但还有有鸡肋之处,它们依旧无法解决模块化最重要的问题

而在React中编写CSS时,有关于这些方面所涉及到的问题表现的更为真切,比如上面提到的:

全局污染

众所周知,CSS是没有作用域名的概念(虽然CSS自定义属性的出现,解决了一些作用域的问题),因此写的样式都是一个全局的。很多时候要去覆盖这些样式,也因此会造成样式可能被错误覆盖。搞不好,你会看到好多样式中会有 !important 这样的关键词,甚至更为离谱的是,在行内样式中还会有 !important 身影。

另一个更为复杂的是CSS选择器权重,更易于让CSSer犯错,从而也提高了样式代码覆盖的成本。仅管 Web Components 中的 Shadow DOM 能彻底解决这个问题,但它的做法有点极端,样式彻底局部化,造成样式重写难度,从而损失了灵活性。

命名混乱

由于全局污染的问题,加上多人协同开发,最易于造成的问题就是 样式冲突 。为了避免样式冲突,对于元素的命名就有着更高的要求,虽然很多CSS的方法论(比如BEM、OOCSS、Atomic、ITCSS等)可以让我们在编写CSS时尽可能的避免命名的冲突(样式混乱),但并没有彻底解决问题(特别是在一些新生团队),在写CSS的时选择器会非常的复杂(复杂到有超六、七层的嵌套,而最佳的是不超三层),而且命名风格还很难统一。

工程越大,人员越多,命名越乱,选择器越复杂,样式越难覆盖 —— 死循环

依赖管理不彻底

编写组件最理想的状态 —— 应该 相互独立 。在引入一个组件时,应该只引入组件自己所需的样式。但现在的做法是除了要引入JavaScript之外,还要引入它的CSS(而CSS处理器以很难做到每个组件编译出单独的CSS)。而在独立的页面中引入所有CSS又会造成不必要的浪费(这也是我不喜欢而没选择 Functional CSS 原因之一)。

虽然JavaScript模块化已经非常成熟了。比如借助Webpack的 css-loader 的能力,可以帮助我们管理CSS依赖。这也是目前较好的方案之一。

无法共享变量

复杂组件要使用JavaScript和CSS来共同处理样式(特别是一些强交互的组件),就会造成有些变量在JavaScript和CSS中冗余。而CSS的处理器是无法提供跨JavaScript和CSS共享变量的这种能力。

值得庆幸的是,CSS的自定义属性可以让我们在JavaScript和CSS共享变量的能力(注意,其实不是变量,是CSS自定义属性)。

代码压缩不彻底

很多压缩 工具 对于较常的 class 类名压缩却无能为力。

事实上,上面提到的这几点都是CSS中一直以来存在的,而又难以解决的。不过借助JavaScript的能力来管理CSS的话,问题就会变得简单的多。这也是CSS-in-JS流行的主要原因,也就出现前面提到的现象,以对象的方式在JavaScript中撸CSS,从而也让不少的同学难以接受这种方式。但CSS Modules的出现,既可以借助JavaScript能力来管理CSS,也方便了CSSer撸代码的习惯,可以说一举两得。这也是为什么选择CSS Modules主要原因。

另外一点,CSS经过这么多年的发展,从 SMACSSOOCSS ,再到 BEM ,可谓是不断的在优化和改进。而CSS Modules与实践单一职责原则的Web组件非常匹配。

我更为好奇的是CSS Modules在设计前端系统中的可能性。CSS Modules基本上是CSS文件,默认情况下类名的作用域名是局部的( 本地的 )。在任何语言中,全局作用域都被认为是一种不好的实践,而CSS却又是这样的一种模式,只不过这么多年来,大家都无耐的接受了这样的一个现实,也已经学会了在CSS中如何使用它。有了CSS Modules(和其他一些模式),我们就可以跳出CSS的全局作用域,构建模块化系统。

很多同学一直觉得CSS非常简单。事实上,CSS一开始的确很简单,但随着项目的增长,CSS会变得越来越复杂,越来越难以编写和维护,即使是专业的开发人员(CSS大神)也会发现很难在复杂的大型系统中维护和组织样式。(事实上,我也非常的害怕,特别是和一些不太了解CSS的同学一起开发项目)。

CSS Modules的出现,其主要目的就是 解决CSS的问题

通过封装CSS的组件作为闭包,从而遵循组件单一的职责。

如何在工程体系中构建CSS Modules

既然CSS Modules有这么大的优势,那么我们就需要在工程中构建CSS Modules。那么问题来了,如何构建CSS Modules呢?这是当下我们要去解决的问题。

使用Webpack来构建CSS Modules,这也是目前主流方式之一。

而在不同的工程中构建CSS Modules方式是不一样的,比如PostCSS、Vue、React等不同的工程体系中,构建的方式都不一样。除了PostCSS之外借助PostCSS相关插件,其他的工程体系(不管是Vue还是React),都可以借助Webpack来构建。而使用Webpack在React项目中构建CSS Modules又不是一件难事。

对于我这样不太了解Webpack工作机制的人来说,还是一件难事。最起码我觉得比Vue环境下难得了。这个时候我需要一位Webpack高级配置工程师和我一起来构建CSS Modules。

抛开所有JavaScript框架而言,不管是在哪处框架底下,都可以通过Webpack下的 css-loader 在配置文件中的加载器选项来启用它。当然,如果你还依赖Sass或PostCSS这样的开发套件的话,那么会相对的增加配置难度。不过,只要一步一步来,一切都不是太大的问题。接下来的内容就来看如何在React的开发环境上构建CSS Modules。

接下来的内容主要来看如何在React环境下配置CSS Modules以及如何使用CSS Modules。

CSS Modules在create-react-app休系下的的使用

React社区中有一个构建React开发体系的工具 Create React App ,俗称 create-react-app ,有点类似于Vue社区中的 Vue-CLI

在这里不会阐述Create React App如何使用,更多的是会聊聊CSS Modules在Create React App构建的工程体系下如何工作。为了更好的用示例向大家演示CSS Modules的使用,我在Github上创建了一个仓库, 在不同的分支下能看到相应的Demo效果

React中CSS Modules的使用

使用Create React APP创建项目

你可以直接从Github上克隆我创建的示例:

git clone git@github.com:airen/css-modules.git
cd css-modules
cd react-modules

安装工程所需要的包:

npm i
npm start

这样工程就可以跑起来了。或者你使用:

npx create-react app <project-name> // 我这里创建的项目名称react-modules
cd project-name
npm start

你将看到的初始效果如下:

React中CSS Modules的使用

上面的这一切都并不重要,重要的是下面的内容 —— React中CSS Modules的使用

注意,我写使用案例时的基本环境是 node: v10.9.0npm: v6.9.0Create React APP(v2) 。不同环境,估计效果略有差异,最终以你本地电脑运行结果为准。

使用 Create React App 第二版本构建的React项目 ,在使用CSS Modules时,不需要做任何的配置。只需要创建 .css.sass.sass 文件时有相应的要求,即**使用 [name].module.css 文件命名约定支持 CSS Modules 和常规 CSS 。 CSS Modules 允许通过自动创建 [filename]\_[classname]\_\_[hash] 格式的唯一 classname 来确定 CSS 的作用域。同样的,如是要是 .sass.scss 的话,文件名格式应该是 [name].module.sass[name].module.scss

或许你会好奇,不需要配置就具备CSS Modules运行环境吗?事实上的确如此。当然,Create React APP环境默认配置了一些功能,如果这些功能达不到你工程所需的要求,那就需要手动进行配置。只不过Create React APP的配置文件隐藏的较深,需要执行不可逆转的命令:

npm run eject

执行完上面的命令之后,会新增两个目录 script/config/

React中CSS Modules的使用

config/ 目录下可以看到两个配置文件 webpack.config.jswebpackDevServer.config.js 。如果你需要配置所需的功能,可以在 webpack.config.js 中添加配置。如果你需要将项目打包输出的话,还得配置 webpack.config.prod.js 。具体如何配置,这里不说了。因为太复杂了。在后面的内容,我们会聊聊Webpack中怎么配置CSS Modules(纯Webpack环境之下,即不依赖Create React APP构建的项目工程)。

CSS Modules的基本使用

前面提到过了,Create React APP默认具备了CSS Modules的功能:

React中CSS Modules的使用

在接下来的内容把重点放在 CSS Modules的使用上 。不管是在哪种环境之下使用,CSS Modules的使用都不会有太大差异,只会稍微的细节上的差异。

CSS Modules中类名的使用

将分支切换到 demo1 查看示例代码。

类名的使用是CSS中最基础的部分,那么CSS Modules中类名又是如何使用呢?第一个示例就向大家演示CSS Modules中类名的使用。

首先创建了一个基本组件 Button

|--src/
|----Button/
|------Button.js            // 组件模板在这个文件中构建
|------Button.module.css    // CSS代码在这个文件中书写

前面提到过了,Create React APP中使用默认的CSS Modules功能,创建 .css 文件时需要以 [name].module.css 格式创建,正如上面示例中的 Button.module.css 所示。

这个组件非常简单,就是一个按钮:

// Button.js
import React from 'react';
import styles from './Button.module.css';

class Button extends React.Component {
    render() {
        return (
            <div className={styles.button} role="button">Click Me</div>
        )
    }
}

export default Button;

Button.module.css 中的代码也非常的简单:

// Button.module.css
.button {
    --primary: #fe90af;
    --color: #fff;

    background: var(--primary);
    color: var(--color);

    padding: 5px 10px;
    border-radius: 4px;
    margin: 5px;
}

调用 Button 组件之后,编译出来的HTML会像下面这样:

<div class="Button_button__1o_YA" role="button">Click Me</div>

对应的CSS的选择器 .button 编译成了 Button_button__1o_YA ,而CSS样式编译成:

.Button_button__1o_YA {
    --primary: #fe90af;
    --color: #fff;
    background: var(--primary);
    color: var(--color);
    padding: 5px 10px;
    border-radius: 4px;
    margin: 5px;
}

效果如下:

React中CSS Modules的使用

CSS Modules将本地名 .button 编译成全局名 .Button_button__1o_YA 。在组件中( Button )可以使用像 .button 的名称来声明类名,而不必担心类名的冲突。如果你不信,可以写一个简单的小示例,假设在 App.js 中使用一个带 .button 类名的元素,看看是否会受组件 Button 中的 .buttonn 的影响:

<div className="button">我是一个带button类名的元素</div>

React中CSS Modules的使用

渲染出来的结果告诉我们,组件中的类名不会影响别的组件中的同类名。这已经达到我们想要的两个目的 —— 解决CSS的命名冲突不会污染全局

顺便说一下, .Button_button__1o_YA 中的 __1o_YA 是一个随机的 hash 值,以确保具有相同名称的多个CSS Modules的唯一性。

在Webpack的配置文件 webpack.config.dev.js 中可以,配置CSS Modules编译器如何重写类名,并且 hash 值是可选的。

在继续往下介绍CSS Modules中类的使用前先打断一下,我们在引用 Button.module.css 是以JavaScript对象的方


以上所述就是小编给大家介绍的《React中CSS Modules的使用》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Clojure编程

Clojure编程

Chas Emerick、Brian Carper、Christophe Grand / 徐明明、杨寿勋 / 电子工业出版社 / 2013-3-26 / 99.00元

Clojure是一种实用的通用语言,它是传奇语言LISP的方言,可与Ruby、Python等动态语言相媲美,更以无缝Java库、服务,以及拥有JVM系统得天独厚的资源优势而胜出。本书既可以用来熟悉Clojure基础知识与常见例子,也可了解其相关的实践领域与话题,更可以看到这一JVM平台上的LISP如何帮助消除不必要的复杂性,为大家在编程实践中解决最具挑战性的问题开辟新的选择——更具灵活性,更适于W......一起来看看 《Clojure编程》 这本书的介绍吧!

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具