CSS Modules 详解及 React 中实践

CSS Modules 详解及 React 中实践

2016/01/18 · CSS · CSS
Modules,
React

初藳出处: pure render –
camsong   

图片 1

CSS 是前面三个领域中升华最慢的一块。由于 ES二〇一五/二〇一五 的快捷分布和
Babel/Webpack 等工具的迅猛发展,CSS
被远远甩在了前面,渐成大型项目工程化的痛点。也变为了后面一个走向绝望模块化前必需化解的难点。

CSS 模块化的化解方案有众多,但入眼有两类。生机勃勃类是通透到底放弃 CSS,使用 JS 或
JSON
来写样式。Radium,jsxstyle,react-style
属于那大器晚成类。优点是能给 CSS 提供 JS
同样强大的模块化技能;短处是不可能采纳成熟的 CSS 预微处理器(或后计算机)
Sass/Less/PostCSS,:hover:active
伪类管理起来复杂。另生龙活虎类是仍旧接受 CSS,但利用 JS 来保管体制重视,代表是
CSS
Modules。CSS
Modules 能最大化地组成现成 CSS 生态和 JS 模块化手艺,API
简洁到大概零学学开支。发表时还是编写翻译出单身的 JS 和 CSS。它并不依赖于
React,只要你使用 Webpack,能够在 Vue/Angular/jQuery
中采取。是本人觉着当下最棒的 CSS
模块化解决方案。近日在类型中山大学量使用,上面具体享受下实行中的细节和想法。

 

CSS Modules 入门及 React 中实践

2017/03/25 · CSS ·
React

原作出处:
AlloyTeam   

CSS 模块化遇到了什么难题?

CSS 模块化主要的是要消除多数个难点:CSS
样式的导入和导出。灵活按需导入以便复用代码;导出时要能够遮盖其间效用域,防止变成全局污染。Sass/Less/PostCSS
等持续试图缓慢解决 CSS
编程能力弱的主题素材,结果它们做的也着实能够,但那并不曾减轻模块化最要紧的难题。Facebook程序员 Vjeux 首先抛出了 React 开荒中相遇的后生可畏雨后冬笋 CSS
相关难题。加上自己个人的眼光,计算如下:

  1. 全局污染

CSS
使用全局选取器机制来安装样式,优点是造福重写样式。短处是装有的体制都以大局生效,样式也许被错误覆盖,因而发生了那三个难看的
!important,甚至 inline !important
和复杂的[选料器权重计数表](Selectors Level
3卡塔尔(قطر‎,升高犯错概率和动用资金。Web
Components 规范中的 Shadow DOM
能深透消除这些主题素材,但它的做法有一些极端,样式彻底局地化,形成外界无法重写样式,损失了灵活性。

  1. 命名混乱

 

由于全局污染的主题材料,多人同台开荒时为了防止样式冲突,选用器越来越复杂,轻便造成不一致的命名风格,很难统生机勃勃。样式变多后,命名帅特别混乱。

  1. 依赖管理不到头

构件应该相互独立,引进贰个组件时,应该只引进它所急需的 CSS
样式。但不久前的做法是除了要引进 JS,还要再引进它的 CSS,并且 Saas/Less
很难落到实处对各类组件都编写翻译出单身的 CSS,引入全体模块的 CSS 又产生浪费。JS
的模块化已经充裕干练,借使能让 JS 来管理 CSS
正视是很好的杀绝办法。Webpack 的 css-loader 提供了这种力量。

  1. 没辙分享变量

复杂组件要动用 JS 和 CSS 来协作管理体制,就能诱致有些变量在 JS 和 CSS
中冗余,Sass/PostCSS/CSS 等都不提供跨 JS 和 CSS 分享变量这种力量。

  1. 代码压缩不干净

鉴于活动端网络的不鲜明性,未来对 CSS
压缩已经到了失常的档期的顺序。非常多压缩工具为了节省叁个字节会把 ’16px’ 转成
‘1pc’。但对分外长的 class 名却心有余而力不足,力未有用到刀刃上。

位置的主题材料就算只凭 CSS 自个儿是力所不及解决的,尽管是由此 JS 来治本 CSS
就很好扫除,因而 Vjuex 给出的实施方案是一丝一毫的 CSS in
JS,但这一定于完全摒弃CSS,在 JS 中以 Object 语法来写
CSS,推测刚看见的小友人都震动了。直到现身了 CSS Modules。

 

写在前边

读文先看此图,能先有个大概概念:

图片 2

开卷本文要求 11m 24s。

CSS Modules 模块化方案

图片 3

CSS Modules 内部通过 [ICSS](css-modules/icss ·
GitHub卡塔尔国来消除体制导入和导出那四个难题。分别对应 :import:export
四个新添的伪类。

JavaScript

:import(“path/to/dep.css”) { localAlias: keyFromDep; /* … */ }
:export { exportedKey: exportedValue; /* … */ }

1
2
3
4
5
6
7
8
:import("path/to/dep.css") {
  localAlias: keyFromDep;
  /* … */
}
:export {
  exportedKey: exportedValue;
  /* … */
}

 

但直接行使那多个关键字编制程序太难为,实际项目中少之甚少会一向动用它们,大家必要的是用
JS 来保管 CSS 的力量。结合 Webpack 的 css-loader 后,就能够在 CSS
中定义样式,在 JS 中程导弹入。
启用 CSS Modules

JavaScript

// webpack.config.js
css?modules&localIdentName=[name]__[local]-[hash:base64:5]

1
2
// webpack.config.js
css?modules&localIdentName=[name]__[local]-[hash:base64:5]

加上 modules 即为启用,localIdentName 是设置生成样式的命名准绳。

JavaScript

/* components/Button.css */ .normal { /* normal 相关的具有样式 */ }
.disabled { /* disabled 相关的有所样式 */ }

1
2
3
/* components/Button.css */
.normal { /* normal 相关的所有样式 */ }
.disabled { /* disabled 相关的所有样式 */ }

JavaScript

// components/Button.js import styles from ‘./Button.css’;
console.log(styles); buttonElem.outerHTML = `<button
class=${styles.normal}>Submit</button>`

1
2
3
4
// components/Button.js
import styles from ‘./Button.css’;
console.log(styles);
buttonElem.outerHTML = `<button class=${styles.normal}>Submit</button>`

生成的 HTML 是

<button class=”button–normal-abc53″>Submit</button>

1
<button class="button–normal-abc53">Submit</button>

 

注意到 button--normal-abc53 是 CSS Modules 按照 localIdentName
自动生成的 class 名。当中的 abc53
是根据给定算法生成的体系码。经过那样模糊管理后,class
名基本就是唯风姿浪漫的,大大缩小了花色中样式覆盖的概率。同不常间在生养条件下改革准则,生成更加短的
class 名,能够提升 CSS 的压缩率。

上例中 console 打印的结果是:

JavaScript

Object { normal: ‘button–normal-abc53’, disabled:
‘button–disabled-def886’, }

1
2
3
4
Object {
  normal: ‘button–normal-abc53’,
  disabled: ‘button–disabled-def886’,
}

CSS Modules 对 CSS 中的 class 名都做了处理,使用对象来保存原 class
和歪曲后 class 的应和关系。

经过这么些轻松的拍卖,CSS Modules 达成了以下几点:

  • 具有样式都以 local 的,消逝了命名冲突和全局污染难点
  • class 名生成法则配置灵活,能够此来压缩 class 名
  • 只需引用组件的 JS 就会解决组件全数的 JS 和 CSS
  • 长久以来是 CSS,大约 0 学习开销

体制暗许局地

选拔了 CSS Modules 后,就一定于给各种 class 名外加加了一个
:local,以此来完毕样式的局地化,假让你想切换来全局方式,使用相应的
:global

:local:global 的界别是 CSS Modules 只会对 :local 块的 class
样式做 localIdentName 准绳管理,:global 的体制编译后不改变。

JavaScript

.normal { color: green; } /* 以上与下部等价 */ :local(.normal) {
color: green; } /* 定义全局样式 */ :global(.btn) { color: red; } /*
定义三个全局样式 */ :global { .link { color: green; } .box { color:
yellow; } }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
.normal {
  color: green;
}
 
/* 以上与下面等价 */
:local(.normal) {
  color: green;
}
 
/* 定义全局样式 */
:global(.btn) {
  color: red;
}
 
/* 定义多个全局样式 */
:global {
  .link {
    color: green;
  }
  .box {
    color: yellow;
  }
}

Compose 来整合样式

对此样式复用,CSS Modules 只提供了唯生龙活虎的法子来拍卖:composes 组合

JavaScript

/* components/Button.css */ .base { /* 全体通用的样式 */ } .normal {
composes: base; /* normal 其余样式 */ } .disabled { composes: base;
/* disabled 其余样式 */ }

1
2
3
4
5
6
7
8
9
10
11
12
/* components/Button.css */
.base { /* 所有通用的样式 */ }
 
.normal {
  composes: base;
  /* normal 其它样式 */
}
 
.disabled {
  composes: base;
  /* disabled 其它样式 */
}

JavaScript

import styles from ‘./Button.css’; buttonElem.outerHTML = `<button
class=${styles.normal}>Submit</button>`

1
2
3
import styles from ‘./Button.css’;
 
buttonElem.outerHTML = `<button class=${styles.normal}>Submit</button>`

生成的 HTML 变为

<button class=”button–base-fec26
button–normal-abc53″>Submit</button>

1
<button class="button–base-fec26 button–normal-abc53">Submit</button>

由于在 .normal 中 composes 了 .base,编译后会 normal 会造成几个class。

composes 还足以结合外界文件中的样式。

JavaScript

/* settings.css */ .primary-color { color: #f40; } /*
components/Button.css */ .base { /* 全部通用的体裁 */ } .primary {
composes: base; composes: primary-color from ‘./settings.css’; /*
primary 其余样式 */ }

1
2
3
4
5
6
7
8
9
10
11
12
13
/* settings.css */
.primary-color {
  color: #f40;
}
 
/* components/Button.css */
.base { /* 所有通用的样式 */ }
 
.primary {
  composes: base;
  composes: primary-color from ‘./settings.css’;
  /* primary 其它样式 */
}

 

对于多数门类,有了 composes 后意气风发度不复要求Sass/Less/PostCSS。但假如您想用的话,由于 composes 不是标准的 CSS
语法,编写翻译时会报错。就一定要使用预微处理机自身的语法来做样式复用了。
class 命名本领

CSS Modules 的命名规范是从 BEM 扩张而来。BEM 把体制名分为 3
个等第,分别是:

  • Block:对应模块名,如 Dialog
  • Element:对应模块中的节点名 Confirm Button
  • Modifier:对应节点相关的动静,如 disabled、highlight

综上,BEM 最后获得的 class 名叫
dialog__confirm-button--highlight。使用双标识 __--
是为着和区块内单词间的相间符区分开来。即便看起来有一些意外,但 BEM
被特别多的大型项目和团体选择。我们实行下来也很承认这种命超格局。

CSS Modules 中 CSS 文件名刚刚对应 Block 名,只供给再思量 Element 和
Modifier。BEM 对应到 CSS Modules 的做法是:

JavaScript

/* .dialog.css */ .ConfirmButton–disabled { /* … */ }

1
2
3
4
/* .dialog.css */
.ConfirmButton–disabled {
  /* … */
}

你也得以不坚决守护完全的命名标准,使用 camelCase 的写法把 Block 和 Modifier
放到一同:

JavaScript

/* .dialog.css */ .disabledConfirmButton { }

1
2
3
/* .dialog.css */
.disabledConfirmButton {
}

什么促成CSS,JS变量分享

注:CSS Modules 中并未有变量的定义,这里的 CSS 变量指的是 Sass 中的变量。

地点提到的 :export 关键字能够把 CSS 中的 变量输出到 JS
中。下边演示怎么样在 JS 中读取 Sass 变量:

JavaScript

/* config.scss */ $primary-color: #f40; :export { primaryColor:
$primary-color; }

1
2
3
4
5
6
/* config.scss */
$primary-color: #f40;
 
:export {
  primaryColor: $primary-color;
}

 

JavaScript

/* app.js */ import style from ‘config.scss’; // 会输出 #F40
console.log(style.primaryColor);

1
2
3
4
5
/* app.js */
import style from ‘config.scss’;
 
// 会输出 #F40
console.log(style.primaryColor);

CSS Modules介绍

CSS Modules是何许事物吗?首先,让我们从官方文书档案动手:
GitHub – css-modules/css-modules: Documentation about
css-modules

A CSS Module is a CSS file in which all class names and animation
names are scoped locally by default.
CSS模块就是有着的类名都唯有大器晚成部分效率域的CSS文件。

所以CSS
Modules既不是官方正规,亦不是浏览器的个性,而是在构建步骤(比如利用Webpack或Browserify)中对CSS类名接受器约束成效域的风度翩翩种方法(通过hash达成近似于命名空间的主意)。

It doesn’t really matter in the end (although shorter class names mean
shorter stylesheets卡塔尔(قطر‎ because the point is that they are dynamically
generated, unique, and mapped to the correct
styles.在选择CSS模块时,类名是动态变化的,唯风流倜傥的,并准确对应到源文件中的各种类的体裁。

那也是得以完结样式功效域的原理。它们被节制在特定的模板里。比方大家在buttons.js里引入buttons.css文件,并使用.btn的样式,在其余零器件里是不会被.btn影响的,除非它也引进了buttons.css.

可大家是由于怎么样指标把CSS和HTML文件搞得这么零碎呢?大家怎么要选择CSS模块呢?

CSS Modules 使用技巧

CSS Modules 是对现存的 CSS
做减法。为了追求**粗略可控**,小编建议依照如下原则:

  • 不应用接纳器,只行使 class 名来定义样式
  • 不层叠多少个 class,只利用二个 class 把装有样式定义好
  • 不嵌套
  • 使用 composes 组合来贯彻复用

地点两条原则相当于减弱了体制中最灵敏的部分,初使用者很难选拔。第一条实践起来难度非常的小,但第二条假诺模块状态过多时,class
数量将倍加上涨。

肯定要驾驭,下边之所以称之为提议,是因为 CSS Modules
并不强逼你必定要这么做。听起来有个别水火不容,由于超过一半 CSS
项目存在深厚的历史遗留难题,过多的约束就象征扩充迁移花销和与外表合作的老本。前期使用中一定须要有的低头。幸运的是,CSS
Modules 那一点做的很好:

1. 借使本人对贰个成分选用七个 class 呢?

没难题,样式依然生效。

2. 什么本身在一个 style 文件中利用同名 class 呢?

没难题,那么些同名 class 编写翻译后即使可能是随机码,但仍为同名的。

3. 若是本人在 style 文件中应用了 id 选取器,伪类,标签选用器等呢?

没难题,全体这几个选取器将不被调换,未有丝毫改换的面世在编译后的 css
中。也正是说 CSS Modules 只会转移 class 名相关样式。

但注意,上面 3 个“借使”尽量不要发生

为什么大家要求CSS模块化

CSS Modules 结合 React 实践

className 处直接行使 css 中 class 名即可。

JavaScript

.root {} .confirm {} .disabledConfirm {}

1
2
3
.root {}
.confirm {}
.disabledConfirm {}

import classNames from ‘classnames’; import styles from ‘./dialog.css’;
export default class Dialog extends React.Component { render() { const
cx = classNames({ confirm: !this.state.disabled, disabledConfirm:
this.state.disabled }); return <div className={styles.root}> <a
className={styles.disabledConfirm}>Confirm</a> … </div>
} }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import classNames from ‘classnames’;
import styles from ‘./dialog.css’;
 
export default class Dialog extends React.Component {
  render() {
    const cx = classNames({
      confirm: !this.state.disabled,
      disabledConfirm: this.state.disabled
    });
 
    return <div className={styles.root}>
      <a className={styles.disabledConfirm}>Confirm</a>
      …
    </div>
  }
}

注意,平日把组件最外层节点对应的 class 名称为 root。这里运用了
[classnames](https://www.npmjs.com/package/classnames卡塔尔(英语:State of Qatar)库来操作 class 名。

借使您不想频仍的输入 styles.**,能够试一下
[react-css-modules](gajus/react-css-modules ·
GitHub卡塔尔,它通过高阶函数的款式来防止重新输入
styles.**

CSS Modules 结合历史遗留项目施行

好的手艺方案除了功效强盛光彩夺目,还要能成功现成项目能平滑迁移。CSS Modules
在此一点上展现的特别灵活。

外界怎么样覆盖局地样式

当生成混淆的 class 名后,能够息灭命名冲突,但因为不或许预见最后 class
名,不可能通过常常选拔器覆盖。大家明天项目中的实施是足以给组件关键节点加上
data-role 属性,然后经过质量采用器来覆盖样式。

// dialog.js return <div className={styles.root}
data-role=’dialog-root’> <a className={styles.disabledConfirm}
data-role=’dialog-confirm-btn’>Confirm</a> … </div>

1
2
3
4
5
// dialog.js
  return <div className={styles.root} data-role=’dialog-root’>
      <a className={styles.disabledConfirm} data-role=’dialog-confirm-btn’>Confirm</a>
      …
  </div>

 

JavaScript

/* dialog.css */ [data-role=”dialog-root”] { // override style }

1
2
3
4
/* dialog.css */
[data-role="dialog-root"] {
  // override style
}

因为 CSS Modules 只会转变类选取器,所以那边的属性选用器没有必要加多
:global

什么样与全局样式共存

后面一个项目不可防止会引进 normalize.css 或任何大器晚成类全局 css 文件。使用
Webpack 能够让全局样式和 CSS Modules
的生机勃勃部分样式和睦共存。下边是大家项目中动用的 webpack 部分公司署代码:

JavaScript

小说权归笔者全体。 商业转发请联系小编获得授权,非商业转发请申明出处。
笔者:camsong 链接:
来源:知乎 // webpack.config.js 局部 module: { loaders: [{ test:
/.jsx?$/, loader: ‘babel’ }, { test: /.scss$/, exclude:
path.resolve(__dirname, ‘src/styles’), loader:
‘style!css?modules&localIdentName=[name]__[local]!sass?sourceMap=true’
}, { test: /.scss$/, include: path.resolve(__dirname, ‘src/styles’),
loader: ‘style!css!sass?sourceMap=true’ }] }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
著作权归作者所有。
商业转载请联系作者获得授权,非商业转载请注明出处。
作者:camsong
链接:http://zhuanlan.zhihu.com/purerender/20495964
来源:知乎
 
// webpack.config.js 局部
module: {
  loaders: [{
    test: /.jsx?$/,
    loader: ‘babel’
  }, {
    test: /.scss$/,
    exclude: path.resolve(__dirname, ‘src/styles’),
    loader: ‘style!css?modules&localIdentName=[name]__[local]!sass?sourceMap=true’
  }, {
    test: /.scss$/,
    include: path.resolve(__dirname, ‘src/styles’),
    loader: ‘style!css!sass?sourceMap=true’
  }]
}

JavaScript

/* src/app.js */ import ‘./styles/app.scss’; import Component from
‘./view/Component’ /* src/views/Component.js */ // 以下为组件相关样式
import ‘./Component.scss’;

1
2
3
4
5
6
7
/* src/app.js */
import ‘./styles/app.scss’;
import Component from ‘./view/Component’
 
/* src/views/Component.js */
// 以下为组件相关样式
import ‘./Component.scss’;

目录布局如下:

JavaScript

src ├── app.js ├── styles │ ├── app.scss │ └── normalize.scss └── views
├── Component.js └── Component.scss

1
2
3
4
5
6
7
8
src
├── app.js
├── styles
│   ├── app.scss
│   └── normalize.scss
└── views
    ├── Component.js
    └── Component.scss

那般所有全局的体裁都放到 src/styles/app.scss
中引进就足以了。其余具备目录满含 src/views 中的样式都以一些的。

CSS全局功能域难点

CSS的平整都是大局的,任何叁个零器件的体制准则,都对全体页面有效。相信写css的人都会超越样式冲突(污染)的标题。

于是日常这么做(笔者都做过):
* class命名写长一点啊,减少冲突的概率
* 加个父成分的选择器,节制范围
* 重新命名个class吧,相比较保障

据此亟待化解的主题素材就是css局地成效域幸免全局样式冲突(污染)的标题

总结

CSS Modules 很好的化解了 CSS 最近边临的模块化难点。扶植与
Sass/Less/PostCSS
等搭配使用,能丰盛利用现存技能积淀。同不常候也能和全局样式灵活搭配,便于项目中国和东瀛渐搬迁至
CSS Modules。CSS Modules
的兑现也属轻量级,以后有专门的职业施工方案后方可低本钱迁移。要是您的制品中恰巧遭受相通主题素材,特别值得风流洒脱试。

1 赞 2 收藏
评论

图片 4

JS CSS不可能分享变量

复杂组件要使用 JS 和 CSS 来协同管理体制,就可以以致有些变量在 JS 和 CSS
中冗余,CSS预微型机/后甩卖器 等都不提供跨 JS 和 CSS 分享变量这种力量。

康泰并且扩展方便的CSS

用作有追求的技术员,编写健壮并且扩大方便的CSS一贯是大家的目的。那么什么样定义强壮何况增加方便?有多个要点:

  • 面向组件 – 管理 UI 复杂性的精品执行就是将 UI 分割成三个个的小组件
    Locality_of_reference
    。假让你正在选拔四个合理的框架,JavaScript
    方面就将原生援助(组件化)。比方,React
    就慰勉高度组件化和撤销合并。大家意在有三个 CSS 布局去相称。
  • 沙箱化(Sandboxed) –
    要是二个组件的体制会对其余构件产生不须求以致敬外的影响,那么将
    UI
    分割成组件并不曾什么用。就那地点来讲,CSS的全局意义域会给你变成担负。
  • 惠及 –
    大家想要全体好的东西,并且不想发生越多的行事。也正是说,大家不想因为运用这一个布局而让我们的开采者体验变得更糟。也许的话,我们想开荒者体验变得更加好。

CSS模块化方案分类

CSS 模块化的解决方案有成都百货上千,但要害有三类。

CSS 命名约定

规范化CSS的模块化施工方案(举个例子BEM BEM — Block Element
Modifier,OOCSS,AMCSS,SMACSS,SUITCSS)
但存在以下难点:
* JS CSS之间依旧未有开掘变量和接受器等
* 复杂的命名

CSS in JS

绝望抛弃 CSS,用 JavaScript 写 CSS 准则,并内联样式。 React: CSS in JS
// Speaker
Deck。Radium,react-style
归属那风姿浪漫类。但存在以下难题:
* 不能选取伪类,媒体询问等
* 样式代码也会现身多量双重。
* 不可能接纳成熟的 CSS 预微型机(或后计算机)

发表评论

电子邮件地址不会被公开。 必填项已用*标注