内容简介:最近项目开始换React的工程,感觉好多东西都得重新开始,特别在撸CSS的时候。说实话和Vue的工程相比,体感差完了。在Vue的工程中除了比如最简单的一个按钮组件,居然要这么撸:
最近项目开始换React的工程,感觉好多东西都得重新开始,特别在撸CSS的时候。说实话和Vue的工程相比,体感差完了。在Vue的工程中除了 Modules之外还可以使用CSS的作用域 scoped
的概念。用久了Vue的同学,在这方面的感觉爽死了,但是突然切到React的工程体系之下,这方面的感觉突然不要不要。拿个实例来说吧(可能我做得不到位),组件的CSS是全局的,有时覆盖起来,除了蛋疼之外,而费时,费成本。
React项目中写CSS的姿势
比如最简单的一个按钮组件,居然要这么撸:
虽然为每个组件创建一个单一的 .scss
文件,并在入口引入相应的样式文件,发现React中的CSS没有域的概念,是全域的。
很多时候需要去覆盖组件初始样式,不得不重新定义样式类或提高选择器权重来处理。着实的蛋疼。除此之外,项目是多人开发,各种各样的类名都有,未统一起来,从覆盖上也增加了不少工作量。另外,还会碰到一些常见的CSS问题,比如:
- 全局污染
- 命名混乱
- 依赖关系复杂
- 无法共享变量
- 代码冗余,难维护
所以最近在重新考虑如何在React项目中编写CSS。以便找到一条更适合自己甚至团队编写、维护CSS的方式:
- 行内样式(在JS中写CSS,最终样式编译到标签元素的
style
内) - CSS-in-JS,较为流行的有
styled-components
、styled-jsx
、react-style
- CSS功能模块(Functional CSS),比如
tailwindcss
、tachyons
等(怎么看都有点类似早期的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经过这么多年的发展,从 SMACSS 到 OOCSS ,再到 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。
- 如果你是在Vue环境下开发,需要构建CSS Modules的话可以阅读《 Vue中的作用域CSS和CSS模块的差异 》一文
- 如果你不借助任何JavaScript框架进行开发,但会使用PostCSS来构建开发体系,那么要构建CSS Modules的话可以阅读《 PostCSS-modules: 让CSS变得更强大 》一文
接下来的内容主要来看如何在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效果 。
使用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的使用 。
注意,我写使用案例时的基本环境是 node: v10.9.0
、 npm: v6.9.0
、 Create 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/
。
在 config/
目录下可以看到两个配置文件 webpack.config.js
和 webpackDevServer.config.js
。如果你需要配置所需的功能,可以在 webpack.config.js
中添加配置。如果你需要将项目打包输出的话,还得配置 webpack.config.prod.js
。具体如何配置,这里不说了。因为太复杂了。在后面的内容,我们会聊聊Webpack中怎么配置CSS Modules(纯Webpack环境之下,即不依赖Create React APP构建的项目工程)。
CSS Modules的基本使用
前面提到过了,Create React APP默认具备了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; }
效果如下:
CSS Modules将本地名 .button
编译成全局名 .Button_button__1o_YA
。在组件中( Button
)可以使用像 .button
的名称来声明类名,而不必担心类名的冲突。如果你不信,可以写一个简单的小示例,假设在 App.js
中使用一个带 .button
类名的元素,看看是否会受组件 Button
中的 .buttonn
的影响:
<div className="button">我是一个带button类名的元素</div>
渲染出来的结果告诉我们,组件中的类名不会影响别的组件中的同类名。这已经达到我们想要的两个目的 —— 解决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的使用》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- RecyclerView使用指南(一)—— 基本使用
- 如何使用Meteorjs使用URL参数
- 使用 defer 还是不使用 defer?
- 使用 Typescript 加强 Vuex 使用体验
- [译] 何时使用 Rust?何时使用 Go?
- UDP协议的正确使用场合(谨慎使用)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Clojure编程
Chas Emerick、Brian Carper、Christophe Grand / 徐明明、杨寿勋 / 电子工业出版社 / 2013-3-26 / 99.00元
Clojure是一种实用的通用语言,它是传奇语言LISP的方言,可与Ruby、Python等动态语言相媲美,更以无缝Java库、服务,以及拥有JVM系统得天独厚的资源优势而胜出。本书既可以用来熟悉Clojure基础知识与常见例子,也可了解其相关的实践领域与话题,更可以看到这一JVM平台上的LISP如何帮助消除不必要的复杂性,为大家在编程实践中解决最具挑战性的问题开辟新的选择——更具灵活性,更适于W......一起来看看 《Clojure编程》 这本书的介绍吧!