webpack入坑记 02

所谓成熟就是,你要习惯任何人的忽冷忽热,也要看淡任何人的渐行渐远。

前言

在react中css没有作用域 scoped 的概念,虽然是为每个组件创建单一的.scss文件并在入口引入,但依然是全局的。scss的嵌套理想状态和css的选择器层叠一样不错过3层,处理器也很难保证为每个组件都编译出单独的css文件。

使用

不管是在哪种环境之下使用,CSS Modules的使用都不会有太大差异,只会稍微的细节上的差异。

样式默认局部

使用了 CSS Modules 后,就相当于给每个 class 名外加加了一个 :local,使用:global转换。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
.normal {
color: green;
}

/* 以上与下面等价 */
:local(.normal) {
color: green;
}

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

/* 定义多个全局样式 */
:global {
.link {
color: green;
}
.box {
color: yellow;
}

Composes 组合样式

CSS Modules 只提供了唯一的方式 composes来复用样式

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

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

.disabled {
composes: base;
/* disabled 其它样式 */
}
1
2
3
import styles from './Button.css';

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

由于在 .normal 中 composes 了 .base,编译后会 normal 会变成两个 class

composes 还可以组合外部文件中的样式

1
2
3
4
5
6
7
8
9
10
11
12
/* 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 扩展

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

BEM 最终得到的 class 名为 dialog__confirm-button--highlight。使用双符号 __-- 是为了和区块内单词间的分隔符区分开来。

CSS Modules 中 CSS 文件名恰好对应 Block 名,只需要再考虑 Element 和 Modifier。

1
2
3
4
5
6
7
8
/* .dialog.css */
.ConfirmButton--disabled {
}

//你也可以不遵循完整的命名规范,使用 camelCase 的写法把 Block 和 Modifier 放到一起:
/* .dialog.css */
.disabledConfirmButton {
}

另外,CSS Modules采用驼峰命名只是建议,而不是强制,因为{style.class-name}不支持,但是你依然可以通过 {style['class-name']}使用。

实现CSS,JS变量共享

:export 关键字可以把 CSS 中的 变量输出到 JS 中。

下面演示如何在 JS 中读取 Sass 变量:

1
2
3
4
5
6
7
8
9
10
11
/* config.scss */
$primary-color: #f40;

:export {
primaryColor: $primary-color;
}
/* app.js */
import style from 'config.scss';

// 会输出 #F40
console.log(style.primaryColor);

使用技巧

使用CSS Modules 一般要遵循一些规则

  • 不使用选择器,只使用 class 名来定义样式
  • 不层叠多个 class,只使用一个 class 把所有样式定义好
  • 所有样式通过 composes 组合来实现复用
  • 不嵌套

react实践

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

/* dialog.css */
.root {}
.confirm {}
.disabledConfirm {}


import classNames from 'classnames';
import styles from './dialog.css';

export default class Dialog extends React.Component {
render() {
const cx = classNames({
[styles.confirm]: !this.state.disabled,
[styles.disabledConfirm]: this.state.disabled
});

return <div className={styles.root}>
<a className={cx}>Confirm</a>
...
</div>
}

注意,一般把组件最外层节点对应的 class 名称为 root

另外 ,如果你不想频繁的输入 styles.**,可以试一下 react-css-modules,它通过高阶函数的形式来避免重复输入 styles.**

CSS Modules 结合历史遗留项目实践

外部如何覆盖局部样式

当生成混淆的 class 名后,可以解决命名冲突,但因为无法预知最终 class 名,不能通过一般选择器覆盖。我们现在项目中的实践是可以给组件关键节点加上 data-role 属性,然后通过属性选择器来覆盖样式。

1
2
3
4
5
6
7
8
9
10
// dialog.js
return <div className={styles.root} data-role='dialog-root'>
<a className={styles.disabledConfirm} data-role='dialog-confirm-btn'>Confirm</a>
...
</div>


// dialog.css
[data-role="dialog-root"] {
// override style

因为 CSS Modules 只会转变类选择器,所以这里的属性选择器不需要添加 :global

如何与全局样式共存

前端项目不可避免会引入 normalize.css 或其它一类全局 css 文件。使用 Webpack 可以让全局样式和 CSS Modules 的局部样式和谐共存。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
//webpack部分配置
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'
}]
}


/* src/app.js */
import './styles/app.scss';
import Component from './view/Component'

/* src/views/Component.js */
// 以下为组件相关样式
import './Component.scss';


//目录结构如下

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

这样所有全局的样式都放到 src/styles/app.scss 中引入就可以了。其它所有目录包括 src/views 中的样式都是局部的。

原文链接:https://blog.csdn.net/xiangzhihong8/article/details/53195926

-------------要说再见啦感谢大佬的光临~-------------