最近要修改一下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…













