最近要修改一下Antd的默认样式,遇到了一些奇奇怪怪的地方。今天来做个复现
全局修改某一组件样式
比如我有两个Button
按钮,现在他们都是默认Antd
中primary
样式,如果我们想统一修改primary
的默认样式呢?这时候我们应该怎么办
<div> <Button type="primary">按钮一</Button> <br /> <br /> <Button type="primary">按钮二</Button> </div>
我们发现Antd
中的ant-btn-primary
就是控制primary
的样式的,我们只要覆盖对应的样式即可
方法一:通过id选择器
通过id选择器的高优先级覆盖原本的样式
#root .ant-btn-primary { background-color: green }
在任意地方引入这个css文件都可以,但是为了统一规范,在我的Umi项目中,是放在global.less
文件夹下面
在.umi
文件夹下面它会先引入global.less
文件
它现在已经变成我喜欢的绿色了
方法二:通过:global
:global(.ant-btn-primary) { background-color: green; }
此时发现没有生效,样式没有改变的原因,因为原本的.ant-btn-primary中已经存在background-color,同时:global的优先级又没有#root高,因此不会覆盖,解决这个问题的方法就是加上 ! important;
:global(.ant-btn-primary) { background-color: green !important; }
但是在Antd 5.x
中,当我们使用如下覆盖样式时,会发现还是没有生效
:global .ant-btn-primary{ background-color: green !important; }
看一下样式,发现前面有个没有看到过的选择器(:where(.css-dev-only-do-not-override-ph9edi))
查了一下,发现他们做了一些更新 What is this css-dev-only
class?,我们不需要使用global
和!import
,直接可以覆盖
.ant-btn-primary{ background-color: green; }
修改某一个组件样式
有一个需求就是我不需要全局修改组件的样式,我只要改变其中的一个就可以,这时候我们需要给要修改的组件加个className
<div> <Button className={styles.primary}>按钮一</Button> <Button>按钮二</Button> </div>
.primary { background-color: green; }
我们写的样式名虽然是primary
,但是它会经过css-loader
编译,转化成另一个样式名,这样也可以直接达到效果
当我们将className赋值为一个字符串的时候,是不会被css-loader编译的
<div> <Button className='primary'>按钮一</Button> <br /> <br /> <Button>按钮二</Button> </div>
CSS Modules 允许使用:global(.className)
的语法,声明一个全局规则。凡是这样声明的class
,都不会被编译成哈希字符串。
因为这时候className没有被编译,global中的className也没有被编译,正好又可以对应上,样式也是同样可以生效
:global(.primary) { background-color: green; }
如果我们将样式写成这个样子
<div> <Button className='primary'>按钮一</Button> <br /> <br /> <Button>按钮二</Button> </div>
.primary { background-color: green !important; }
这时候样式就会失效了,因为className不会被编译,但是css文件里面的primary还是会被编译,就找不到对应的样式了,从而失效
总结
当我们想要修改Antd
的默认样式的时候,可以使用
- 通过
root
节点,更改对应的属性名 - 通过
:global
加上!important
修改局部样式的时候,要注意css-loader
会将className={xxx.xxx}
编译成哈希字符串,可以通过将className设置成为一个字符串解决,同时使用:global
不会进行编译
另外:CSS Module并不是React的能力,当我们用官方的脚手架创建一个项目的时候,并不支持CSS Module,需要我们手动配置。
思考
在 Webpack 中处理 CSS 文件,通常需要用到:
css-loader
:该 Loader 会将 CSS 等价翻译为形如module.exports = "${css}"
的JavaScript 代码,使得 Webpack 能够如同处理 JS 代码一样解析 CSS 内容与资源依赖;style-loader
:该 Loader 将在产物中注入一系列 runtime 代码,这些代码会将 CSS 内容注入到页面的<style>
标签,使得样式生效;mini-css-extract-plugin
:该插件会将 CSS 代码抽离到单独的.css
文件,并将文件通过<link>
标签方式插入到页面中
css-loader
提供了很多处理 CSS 代码的基础能力,包括 CSS 到 JS 转译、依赖解析、Sourcemap、css-in-module 等,基于这些能力,Webpack 才能像处理 JS 模块一样处理 CSS 模块代码。
经过css-loader
处理之后的结果,我手动换行了一下
Module\n___CSS_LOADER_EXPORT___.push( [module.id, \"body,html {\\r\\n height: 100%;\\r\\n background: red;\\r\\n}\\r\\n\\r\\n.primary {\\r\\n background-color: green;\\r\\n}\", \"\" ]);
但这段字符串只是被当作普通 JS 模块处理,并不会实际影响到页面样式,后续还需要:
- 开发环境:使用
style-loader
将样式代码注入到页面<style>
标签; - 生产环境:使用
mini-css-extract-plugin
将样式代码抽离到单独产物文件,并以<link>
标签方式引入到页面中。
经过 css-loader
处理后,CSS 代码会被转译为等价 JS 字符串,但这些字符串还不会对页面样式产生影响,需要继续接入 style-loader
加载器。
与其它 Loader 不同,style-loader
并不会对代码内容做任何修改,而是简单注入一系列运行时代码,用于将 css-loader
转译出的 JS 字符串插入到页面的 style
标签。
经过 style-loader
+ css-loader
处理后,样式代码最终会被写入 Bundle 文件,并在运行时通过 style
标签注入到页面。这种将 JS、CSS 代码合并进同一个产物文件的方式有几个问题:
- JS、CSS 资源无法并行加载,从而降低页面性能;
- 资源缓存粒度变大,JS、CSS 任意一种变更都会致使缓存失效。
因此,生产环境中通常会用 mini-css-extract-plugin
插件替代 style-loader
,将样式代码抽离成单独的 CSS 文件。
在我们使用的Umi项目中,会将className编码,应该是Umi配置好了css-loader
,自带modules
功能
这样每个样式都是一个独立的小模块,类名不会出现重复,样式不会覆盖
参考:
www.ruanyifeng.com/blog/2016/0…
webpack.docschina.org/loaders/css…