如何覆盖组件库样式?下面本篇文章给大家介绍一下react和vue项目中优雅地覆盖组件库样式的方法,希望对大家有所帮助!
组件库的样式覆盖不掉,这应该是很多前端在工作中遇到过的问题。今天从实际案例出发分析原因,最后会给出在react和vue项目中的最优解。
本文会讲清:
react中css module的原理是什么?:global是做什么的?
vue中scoped的原理是什么?深度作用选择器是什么?(学习视频分享:vue视频教程)
先不讲概念,直接从需求出发:我使用了antd组件库来展示一个日历。
现在我想将当前日期上面的蓝色边框变成紫色。
可以试试你能不能实现。
不管是react还是vue,整个calendar是被封装起来的,我们没有办法在组件外简单加上style/class改动内部的样式。
import { calendar } from 'antd';...<div classname="mywrapper"> <calendar class="custom"/></div>
定位要覆盖的样式首先用开发者工具定位对应的样式:.ant-picker-calendar-date-today,这就是我们要修改的地方。
.ant-picker-calendar-full .ant-picker-panel .ant-picker-calendar-date-today { border-color: #1890ff; }
熟悉webpack的人应该知道,引入的css文件最终都会被style-loader处理。简单来说,它的作用就是把css文件打包,放在style标签内,最后塞进html中作为一个内部样式表。不管是组件库的样式还是我们写的自定义样式都是这样处理的。
我们要把组件库的样式先于自定义样式引入,这样自定义样式才能有更高的优先级。
修改源文件直接改组件库的css源码是最简单粗暴的方法。打开你项目的node_modules文件夹,一层层点开,找到对应样式文件,按照需求修改即可。
个人项目这样处理确实可行,但是团队合作时,同步别人本地的node_modules就比较麻烦,只能算一个60分解法。
全局css文件之前提到,把自己写的的css文件放在组件库的样式后面,可以保障自定义有更高优先级。只要重写同名的样式,理论上就能实现覆盖组了。
但这样?处理会发现并不起作用:
/* 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>...
因为这里还涉及css组合选择器的优先级。
基础的优先级应该不用赘述:!important>内联样式>id选择器>类选择器>标签选择器。(!important这种hack会导致项目不好维护,不提倡使用)
在这个基础上还有五种组合选择器要对优先级分数做累计,以类选择器为例:
后代选择器(空格):.a .b选择.a元素后的所有.b元素,
子元素选择器(大于号):.a>.b选择.a元素的直接后代中的.b元素
相邻兄弟选择器(加号):.a+.b选择.a元素后紧邻的第一个兄弟.b元素
后续兄弟选择器(~号):.a~.b选择.a元素后所有的兄弟.b元素
交集选择器(连在一起):.a.b选择自身同时拥有.a和.b两个属性的元素
上面几个规则看着很复杂,其实用的多的就是第一个后代选择器,记住它就行。antd组件库用的就是它:
.ant-picker-calendar-full .ant-picker-panel .ant-picker-calendar-date-today { border-color: #1890ff; }
如果说一个类选择器优先级分数是10分,那三个形成的后代选择器就是30分。
而自定义的样式?只有10分,所以即使放在更后面引入,也不能成功覆盖。
.ant-picker-calendar-date-today { border-color: purple; // 覆盖为紫色}
需要完整重写整个选择器才能实现想要的效果。
这里补充一点,同样也是组合选择器,但并集选择器(逗号)优先级不累计:.a, .b选择.a或者.b元素(可以是逗号+空格)
样式隔离css module和scoped上面我们引入自定义的全局css文件,实现了样式的覆盖,但是这种解法只能给80分。因为在实际工作中,项目owner通常不允许使用全局css,这会造成样式污染:你定义了一个样式my_button,团队其他人恰巧也命名为my_button,这就造成样式冲突。
我们需要给每个文件做样式隔离,就好像是给它一个命名空间。通常使react项目使用的是用的是css module,vue项目使用scoped标记。
接下来会讲清两种样式隔离的原理,以及使用样式隔离时怎么覆盖组件库的样式。
react的css module
首先来了解一下css module的原理。它的使用很简单,在css文件加一个后缀.module,然后当做一个变量引入到js文件中。
// src/demo.jsimport 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和深度作用选择器做样式覆盖。
如果这篇文章对你有帮助,给我点个赞和在看吧~
(学习视频分享:web前端开发、编程基础视频)
以上就是如何覆盖组件库样式?react和vue项目的解决方法浅析的详细内容。
