前言
在前端領域,antd無疑是react生態(tài)下使用最為廣泛的UI類庫,antd對常見的表單類輸入組件進行了高度封裝,帶來開箱即用的便利性,但是與此同時,對其樣式的控制也愈發(fā)困難。大家應該都有過被產品或者交互逼著修改antd樣式的經歷。次數多了,很難再通過"這是開源的控件,樣式不好修改"搪塞過去。那么問題來了,如何優(yōu)雅第修改antd的樣式?
具體方法
經過探索,大致有以下幾種思路:
- 直接添加類名或者style
這個是最常規(guī)的方法,能夠直接作用于當前的元素,但是有的時候我們會發(fā)現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;
}
接下來我們來檢測下成果。
第一種.png
輸入框的背景顏色并沒有如我們預期地發(fā)生改變。接下來我們分析下具體原因,從上面的截圖中可以看到,antd組件內部的css類名并沒有被css module處理,但是我們的自定義樣式是通過css module來實現的,導致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
}
}
}
],
},
]
}
從這里的配置我們可以看到,業(yè)務代碼使用了css moudle,由于antd的組件使用less編寫,這里我們使用了less loader來處理樣式,less的部分沒有開啟css loader故而在dom中看得到的類名還是原始類名。當然,如果你的項目并沒有啟用css module,直接使用原始類名來控制樣式,那就不存在這個問題了。
- 運用多種css文件打包策略
上文已經分析了問題的根源,接下來就是如何解決問題,最自然的思路就是針對這部分特殊的需求,采用特殊的css樣式打包策略,簡單來說就是針對antd組件的樣式,在外面掛上一個常規(guī)的字符串類名,使用單獨的css文件來控制樣式,在打包的時候,對這種文件采取特殊的策略,即不啟用css module,這樣就跟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 module處理,代碼我們做如下修改:
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;
}
接下來我們看下效果:
第二步.png
從這里我們可以看到,外圍的
feeInput類已經成功地通過類選擇器修改了antd自帶的.ant-input樣式。
- 終極解決方案
問題解決了,但是實操過程過于繁瑣,要多出來一個文件,還要新增webpack打包規(guī)則,不符合less is more的規(guī)則,那么有沒有更好的方案呢?這里有一點可以注意下,css module針對全局的樣式(使用:global包裹的),不會將類名進行hash化,換句話來說,我們可以利用這一點,將antd組件外部用來精細化控制樣式的類定義在:global中,這樣就避免了類名hash化,可以配合antd的類名規(guī)則,實現樣式控制。具體代碼如下:
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;
}
接下來我們看看效果:
第三步.png
效果如預期,完成了對antd組件內嵌樣式的完美修改!沒有第二種方案中引入多余文件的問題,簡潔高效。