一、我的问题是什么?
在使用 ant design
的项目中,使用了 Icon
组件的自定义图标,我们把自定义图标的样式放在了 global
中,这样在一个项目里面也不会有问题。但是我们的项目是几个项目组合起来的,就出现了问题:每个项目中都定义了图标的字体,并且都是在 global
中定义的,这样就出现了字体的相互覆盖,展示出现了问题。
针对上述问题,我们给出的解决方案就是使用 CSS
模块化代替 global
,避免全局污染,所以下面介绍一下 CSS
模块化的相关问题。
二、css modules
能解决什么问题?
1、CSS
作用域问题
CSS
的规则都是全局的,任何一个组件的样式规则,都对整个页面有效。这样就产生了上述规则覆盖的问题,这种问题之前一般通过 !important
这种丑陋的方式解决,这种方法不但丑陋并且也不是一个万能的方法,比如上述问题就不能通过这种方法解决。
2、JS
与CSS
共享变量问题
复杂组件要使用 JS
和 CSS
来共同处理样式,就会造成有些变量在 JS
和 CSS
中冗余,Sass/PostCSS/CSS
等都不提供跨 JS
和 CSS
共享变量这种能力。
3、组件真正实现模块化的问题
我们平时开发一个可复用组件,CSS
也会在全局起作用,需要复用的组件间也会互相影响,这样开发的就不是一个真正意义上的可独立使用的组件。
三、项目中的实践
既然 CSS
模块化可以解决这么多问题,那么在我们的项目中具体怎么使用呢?
下面的介绍基于 react
项目并且 webpack 4.0
进行打包
module.exports = {
module: {
rules: [
{
test: /\.css$/,
loader: 'css-loader',
options: {
modules: true,
},
},
],
},
};
在 webpack
中设置了 modules: true
就表示项目中的 .css
文件启用了 CSS
模块化。
然后再业务代码中需要手动引入 css
文件的 class
名:
import React from 'react';
import styles from './index.css';
export default class Index extends React.Component {
render () {
return <div className={styles.wrapper}>
<div className={styles.btn}>
Button
</div>
</div>;
}
}
渲染出来的组件代码:
<div class="-h95cTYFwBAAIlzF-qcc4">
<div class="_2WlqvlnhuY4sYgg2YppNvQ">
Button
</div>
</div>
通过我们介绍的 css modules
解决了全局污染的问题,但是还是没能解决我之前遇到的问题,这是为什么呢?
经过仔细查看发现虽然我们使用了 CSS
模块化,可是每个项目中都使用了一样的产生 hash
值的方法,上面也介绍了我们项目是几个项目组合起来的,所以产生的样式的名字也是一样的,加入A项目中的弹窗在B项目的页面上运行,产生的样式名一样,A的弹窗就使用了 B的样式,就出现了问题,这个问题该怎样解决呢?这边就需要使用css-loader
的一个属性localIdentName
,该属性用来设置生成的样式名字,为了区分不同项目的样式我们只需在该属性中加入项目名即可,代码如下:
module.exports = {
module: {
rules: [
{
test: /\.css$/,
loader: 'css-loader',
options: {
modules: true,
localIdentName: appName + '__[name]__[local]--[hash:base64:5]',
},
},
],
},
};
也可以通过属性 getLocalIdent
来指定样式名字,具体可参考文档。
但是我一直有个疑问样式名字如果设置很长是不是增加 css 文件的大小,带着这个疑问做了一些实验,主要就是看看 uglifyjs-webpack-plugin
插件能不能按照一定规则进行压缩,结论是并没有。 uglifyjs-webpack-plugin
插件内部使用的是cssnano
,这边我们使用cssnano-cli
进行了测试。
我先使用了如下代码进行压缩:
.main__index__iconStyle--KRCN4 {
font-size: 16px;
}
.main__icon__iconfont--AHO8L {
color: red;
}
.main__icon__icon-yichang--1cej0 {
text-align: center;
}
得到的结果为:
.main__index__iconStyle--KRCN4{font-size:16px}.main__icon__iconfont--AHO8L{color:red}.main__icon__icon-yichang--1cej0{text-align:center}
然后,我又测试了下面的代码:
.index__iconStyle--KRCN4 {
font-size: 16px;
}
.icon__iconfont--AHO8L {
color: red;
}
.icon__icon-yichang--1cej0 {
text-align: center;
}
得到的结果为:
.index__iconStyle--KRCN4{font-size:16px}.icon__iconfont--AHO8L{color:red}.icon__icon-yichang--1cej0{text-align:center}
从上面的结果我个人暂时得到的的结论就是,样式名过长会增大 css 文件的大小,但是仍需要再求证一下。
四、babel-plugin-react-css-modules
插件
使用了css modules 后,我们每次都需要写className={styles.xxx}
,比较麻烦,babel-plugin-react-css-modules
插件可以实现使用styleName
属性自动加载CSS
模块。我们通过该babel插件来进行语法树解析并最终生成className
。具体事例参考官方文档。
下面再说下webpack
的配置:
module.exports = {
module: {
rules: [
{
test: /\.css$/,
loader: 'css-loader',
options: {
generateScopedName:appName + '__[name]__[local]--[hash:base64:5]', // 要与`localIdentName`命名一致
filetypes: {
".less": "sugerss"
},
},
},
],
},
};