大家都知道css根据样式表的名称给名称匹配的html元素添加相应的效果,但是有时候一个html元素使用了多个css样式表,那么这些样式表之间会发生什么关系呢? 最终的页面效果会是什么样子呢?
下面我们就来说说css的三大特性,彻底了解多个css样式表是如何相爱的。
我们可以看到,我们给了一个div容器两个不同的样式表,但是最终浏览器解释的结果是name1样式生效了,而name2好像就断开了。 为什么是这样?
CSS(Cascading Style Sheets),又称级联样式表,具有级联性质,用于解决上述情况引起的样式冲突。
当多个样式应用于同一个(同一个类)标签时,样式会发生冲突,并且总是执行后面的代码(级联后面的代码旁边的代码)css行内样式,无论标签调用选择器的顺序如何。 所以我们可以看到,样式中name2是在name1之前生成的css行内样式,但是即使name1写在name2之后,也不会被覆盖,因为决定因素是css样式表中的写入顺序。 这是因为浏览器渲染网页时,会先下载文档内容,加载腹中的样式资源,然后按照从上到下、从外到内的顺序渲染 DOM 内容,所以其上写入的样式将被覆盖。
2. 继承
CSS 继承意味着包裹的标签将具有外部标签的样式属性。 他的前提是元素之间存在包含关系。
并不是所有具有包含关系的元素都可以继承样式,字体、文本等属性都可以在css中继承,比如文本颜色、大小、字体、粗细等。 行距和背景等属性不会被继承。
特例:
h 系列无法继承文本大小。
标签不能继承文本颜色。
我们来看一下效果图:
3. 优先权
在定义css样式的时候,经常会出现两个或者多个规则应用到同一个元素上的情况,这时候就会出现优先级的问题。
有几种不同的情况:
第一个对比:当只有一个选择器时,可以看到id选择器选择的p元素样式生效。
第二个对比:当有多个选择器时,可以明显看出,多个选择器选择的样式生效,单个选择器无法匹配。
第三个比较:多个选择器选择之间的比较,可以看出id选择器+id选择器是最强大的。
前言
在后端领域,antd无疑是React生态中使用最广泛的UI解释器。 antd 对常见的表单类型输入组件进行了高度封装,带来了开箱即用的便利css内部样式,但同时,其样式 Control 也变得越来越难。 每个人都应该有过被产品或者交互逼迫改变antd风格的经历。 次数太多了,很难用“这是一个开源控件,风格不容易改变”来搪塞。 那么问题来了,如何改变Noble中antd的风格呢?
具体方式
经过摸索,大致有以下想法:
直接添加类名或样式
这是最常见的方式,可以直接影响当前元素,但是有时候我们会发现antd组件最终会在dom层进行多层嵌套。 仅仅改变外部元素并让子元素继承样式并不能完全达到目的; 这个时候,很自然的就需要使用css选择器来检索下层元素了。 总体代码逻辑如下:
import s from './index.css';
import { Input, AutoComplete } from 'antd';
// ...省略无关代码
<div className={s.feeWrap}>
<Input
disabled
value={lanWrap('Transfer fee')}
addonAfter={fee}
className={cx(s.feeInput, 'tInput')}/>
</div>
CSS代码
.feeInput .ant-input {
background-color: #ffffff !important;
font-size: .14rem;
color: #555555 !important;
user-select: none;
}
接下来我们来检查一下结果。
输入框的背景颜色没有按预期改变。 接下来我们来分析一下具体原因。 从里面的截图我们可以看到antd组件内部的css类名并没有被css模块处理,但是我们自定义的样式是通过css模块实现的,导致dom无法匹配到正确的样式。 我们看一下webpack的配置:
module: {
rules: [
{
test: /(?<!antd).(css|scss)$/,
exclude: /node_modules/,
use: [
//MiniCssExtractPlugin.loader,
'style-loader',
{
loader: 'typings-for-css-modules-loader',
options: {
modules: true,
namedExport: true
}
}
]
},
{
// 专门处理antd的css样式
test: /.(less)$/,
include: /node_modules/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
{
loader: "less-loader",
options: {
lessOptions: {
javascriptEnabled: true
}
}
}
],
},
]
}
从这里的配置我们可以看到业务代码使用了css moudle。 由于antd的组件都是使用less编译的,所以这里我们使用less loader来处理样式。 less部分没有打开css加载器,所以在dom中可以看到类名还是原来的类名。 当然,如果你的项目没有启用css模块,直接使用原来的类名来控制样式css内部样式,那么就不存在这个问题。
2.使用多种css文件打包策略
上面已经分析出了问题的症结,接下来就是如何解决问题了。 最自然的思路就是针对这部分特殊需求,采用特殊的css样式封装策略。 对于之前常规的字符串类名,是用单独的css文件来控制样式的。 打包时,对这些文件采取了特殊的策略,即不启用css模块,使其与antd组件的类名处理方式一致,可以直接通过 的类名选择器来控制样式CSS。 webpack配置如下
module: {
rules: [
{
// 为了给antd定制样式,使用非获取匹配,反向肯定预查,不使用css module
// 文件名中包含antd字样的,不启用css module
test: /(?<=antd).(css|scss)$/,
exclude: /node_modules/,
use: [
//MiniCssExtractPlugin.loader,
'style-loader',
{
loader: 'typings-for-css-modules-loader',
options: {
modules: false,
namedExport: true
}
}
]
},
{
// 文件名中不包含antd字样的,启用css module
test: /(?<!antd).(css|scss)$/,
exclude: /node_modules/,
use: [
//MiniCssExtractPlugin.loader,
'style-loader',
{
loader: 'typings-for-css-modules-loader',
options: {
modules: true,
namedExport: true
}
}
]
},
]
}
对于antd的特殊样式文件,我们将其命名为index.antd.css,这样它就不会被css模块处理。 我们进行以下代码更改:
import s from './index.css';
import './index.antd.css';
// ...省略无关代码
<div className={s.feeWrap}>
<Input
disabled
value={lanWrap('Transfer fee')}
addonAfter={fee}
className={cx('feeInput', 'tInput')}/>
</div>
在index.antd.css中有以下内容
.feeInput .ant-input {
background-color: #ffffff !important;
font-size: .14rem;
color: #555555 !important;
user-select: none;
}
接下来我们看一下效果:
从这里我们可以看到外围的feeInput类已经通过类选择器成功改变了antd自带的.ant-input样式。
3、最终解决方案
问题解决了,但是实际操作过程太冗长,需要多下载一个文件,而且还要添加webpack打包规则,不符合less is more的规则,那么有没有更好的解决方案呢? 这里需要注意一件事, css 模块不会对全局样式(用 :global 包裹)的类名进行哈希处理。 也就是说,我们可以利用这一点,在 antd 组件之外定义类,对样式进行细粒度的控制,在 :global 中,这样就防止了类名的哈希,可以通过以下方式实现样式控制配合antd的类名规则。 具体代码如下:
import s from './index.css';
// ...省略无关代码
<div className={s.feeWrap}>
<Input
disabled
value={lanWrap('Transfer fee')}
addonAfter={fee}
className={cx('fee11Input', 'tInput')}/>
</div>
js代码不需要引入专门的index.antd.css文件,css文件改成这样:
:global(.fee11Input .ant-input) {
background-color: #ffffff !important;
font-size: .14rem;
color: #555555 !important;
user-select: none;
}
接下来我们看一下效果:
效果符合预期,antd组件嵌入样式的完美更改完成! 第二种方案不存在引入冗余文件的问题,简洁高效。