How to override component library styles? The following article will introduce to you how to elegantly override component library styles in React and Vue projects. I hope it will be helpful to you!
The style of the component library cannot be overwritten. This should be a problem that many front-ends have encountered in their work. Today we will analyze the reasons based on actual cases, and finally give the optimal solutions in React and Vue projects.
This article will explain clearly:
What is the principle of CSS Module in React? What does :global
do?
What is the principle of Scoped in Vue? What is a depth action selector? (Learning video sharing: vue video tutorial)
Let’s not talk about the concept first, and start directly from the needs: I used the Antd component library to display a calendar.
Now I want to change the blue border above the current date to purple.
You can try to see if you can achieve it.
Whether it is React or Vue, the entire Calendar is encapsulated. We cannot simply add style/class outside the component to change the internal style.
import { Calendar } from 'antd'; ... <div className="myWrapper"> <Calendar class="custom"/> </div>
First use the developer tools to locate the corresponding style: .ant-picker-calendar-date-today
, this is what we want to modify.
.ant-picker-calendar-full .ant-picker-panel .ant-picker-calendar-date-today { border-color: #1890ff; }
Those who are familiar with webpack should know that the imported CSS files will eventually be processed by style-loader. Simply put, its function is to package the CSS file, place it in the style tag, and finally insert it into HTML as an internal style sheet. This is done whether it is the style of the component library or the custom style we wrote.
We need to introduce the styles of the component library before the custom styles, so that the custom styles can have a higher priority.
The simplest and crudest method is to directly change the CSS source code of the component library. Open the node_modules folder of your project, click on it layer by layer, find the corresponding style file, and modify it according to your needs.
It is indeed feasible to handle personal projects in this way, but when working in a team, it is more troublesome to synchronize other people's local node_modules, and it can only be regarded as a 60 decomposition method.
As mentioned before, putting your own CSS files behind the styles of the component library can ensure higher customization priority. As long as you rewrite the style with the same name, you can theoretically implement an override group.
But this? The processing will find that it does not work:
/* src/demo.css */ .ant-picker-calendar-date-today { border-color: purple; /* 覆盖为紫色 */ }
// src/Demo.js // 组件库的样式 import 'ant-design-vue/dist/antd.css'; // 自定义样式 import './demo.css' import { Calendar } from 'antd'; ... <div className="myWrapper"> <Calendar /> </div> ...
Because this also involves the priority of the CSS combination selector.
The basic priority should go without saying: !important>Inline style>ID selector>Class selector>Tag selector
. (!important hacks like this will make the project difficult to maintain and are not recommended for use)
On this basis, there are five combination selectors that need to accumulate priority scores. Take the class selector as an example:
Descendant selector (space): .A .B
Selects all .B elements after the .A element,
Child element selector (greater than sign): .A>.B
selects the .B element among the direct descendants of the .A element
adjacent sibling selector (plus Number): .A .B
Select the first sibling .B element immediately after the .A element
.A~.BSelect all sibling .B elements after .A element
.A.BSelect itself at the same time Elements with two attributes, .A and .B
.ant-picker-calendar-full .ant-picker-panel .ant-picker-calendar-date-today { border-color: #1890ff; }
.ant-picker-calendar-date-today { border-color: purple; // 覆盖为紫色 }
One thing to add here is that it is also a combination selector, but the priority of the union selector (comma) is not cumulative:.A, .B
Select the .A or .B element ( Can be comma space)
上面我们引入自定义的全局CSS文件,实现了样式的覆盖,但是这种解法只能给80分。因为在实际工作中,项目Owner通常不允许使用全局CSS,这会造成样式污染:你定义了一个样式my_button
,团队其他人恰巧也命名为my_button
,这就造成样式冲突。
我们需要给每个文件做样式隔离,就好像是给它一个命名空间。通常使React项目使用的是用的是CSS Module,Vue项目使用Scoped标记。
接下来会讲清两种样式隔离的原理,以及使用样式隔离时怎么覆盖组件库的样式。
React的CSS Module
首先来了解一下CSS Module的原理。它的使用很简单,在CSS文件加一个后缀.module
,然后当做一个变量引入到JS文件中。
// src/Demo.js import styles from './demo.module.css'; export default function Demo() { return ( <div className={styles.myWrapper}> <Calendar /> </div> ); }
/* src/demo.module.css */ .myWrapper { border: 5px solid black; }
被编译后?,插入的样式表和元素的class属性都会加上一个哈希值作为命名空间。
<style> .demo_myWrapper__Hd9Qg { border: 5px solid black; } </style> <div class="demo_myWrapper__Hd9Qg"> ... </div>
可以看到,原本的CSS选择器和HTML元素类名都从myWrapper
变成了demo_myWrapper__Hd9Qg
,前面加上了文件名,后面加上了哈希值,这样就能保障样式只在当前这个文件下生效了。
但是在这种样式隔离情况下,我们原本用作覆盖的CSS也被加上了哈希值,就像下图这样,这时没有办法选中UI组件,覆盖也就不会成功。
所以,React给我们提供了一个语法:global
。它生效范围内的样式会被当作全局CSS。
具体使用如下,在CSS文件中,使用:global
包裹希望全局生效的样式
:global(.ant-picker-calendar-full .ant-picker-panel .ant-picker-calendar-date-today) { border-color:purple; /* 覆盖为紫色 */ }
SCSS或SASS中,还可以使用嵌套语法:
:global { .ant-picker-calendar-full .ant-picker-panel .ant-picker-calendar-date-today { border-color:purple; } }
最后编译出来的代码如下:
/* 加上了哈希*/ .demo_myWrapper__Hd9Qg { border: 5px solid black; } /* :global作用域下都不会加上哈希*/ .ant-picker-calendar-full .ant-picker-panel .ant-picker-calendar-date-today { border-color:purple; }
借助:global
语法,即使使用CSS Module进行样式隔离也可以如愿实现覆盖功能。
Vue中的Scoped
Vue中也有类似的样式隔离功能,使用Scoped标记CSS部分,使用也很简单?:
<style scoped> .myWrapper{ border: 5px solid black } </style> ... <div class="myWrapper" > <Calendar /> </div> ...
编译出来的代码如下:
<style> .myWrapper[data-v-2fc5154c] { border: 5px solid black } </style> <div class="myWrapper" data-v-2fc5154c> ... </div>
可以看到,它的原理和CSS Module不太一样,Vue的Scoped会使CSS选择器后加上一个中括号。
这并不是Vue独创的语法,而是属性选择器。.myWrapper[data-v-2fc5154c]
代表选择拥有data-v-2fc5154c这个属性的、同时是myButton类的HTML元素。只有这个文件内部的HTML元素才会被打上data-v-2fc5154c这个属性。其余文件的HTML元素即使是myWrapper类,这个样式也不会对他生效。
回到相同的问题,假如Vue项目在使用了Scoped做样式隔离,我们用于覆盖的样式也会加上属性选择器,但是UI组件内部的HTML元素都没有该属性。
所以Vue提供了一个类似的语法:深度作用选择器。
使用很简单,把要“渗透“进组件内部的样式前面加上,作用域内的CSS样式都不会带上哈希值作为属性选择器。
<style scoped> .myWrapper>>> .ant-picker-calendar-full .ant-picker-panel .ant-picker-calendar-date-today{ border-color:purple } </style> <template> <div class="myWrapper" > <Calendar /> </div> </template>
编译后
<style> .myWrapper[data-v-2fc5154c] .ant-picker-calendar-full .ant-picker-panel /* 作用域内的CSS都没有带上属性选择器 */ .ant-picker-calendar-date-today { border-color:purple } </style> <div class="myWrapper" data-v-2fc5154c> <div class="ant-picker-calendar-full" data-v-2fc5154c> <div class="ant-picker-date-panel"> <td class="ant-picker-cell-today"></td> </div> </div> </div>
借助深度作用选择器,可以将要用于覆盖CSS“渗透”进组件内部。
也可以将
写成
/deep/
或者::v-deep
。
相较于React的:global
,Vue的深度作用选择器是一种更优秀的方案,它必须要一个前导(也就是上面例子中的.myWrapper选择器),前导依旧会被打上哈希值作为属性选择器,要渗透进去的样式实际上是作为它的子选择器,只在当前这个文件下生效,彻底避免造成全局污染。
本文通过如何修改UI组件内部样式为切入点,分析了几种解法。了解了组合选择器的优先级分数累加,以及在实际React、Vue项目用到的样式隔离方案——CSS Module和Scoped的原理,最后是介绍了在样式隔离的情况下,如何使用:global和深度作用选择器做样式覆盖。
如果这篇文章对你有帮助,给我点个赞和在看吧~
The above is the detailed content of How to override component library styles? A brief analysis of solutions to React and Vue projects. For more information, please follow other related articles on the PHP Chinese website!