• 技术文章 >web前端 >Vue.js

    如何覆盖组件库样式?React和Vue项目的解决方法浅析

    青灯夜游青灯夜游2022-05-16 21:01:41转载853
    如何覆盖组件库样式?下面本篇文章给大家介绍一下ReactVue项目中优雅地覆盖组件库样式的方法,希望对大家有所帮助!

    组件库的样式覆盖不掉,这应该是很多前端在工作中遇到过的问题。今天从实际案例出发分析原因,最后会给出在React和Vue项目中的最优解。

    本文会讲清:

    先不讲概念,直接从需求出发:我使用了Antd组件库来展示一个日历。

    1.png

    现在我想将当前日期上面的蓝色边框变成紫色。

    可以试试你能不能实现。

    不管是React还是Vue,整个Calendar是被封装起来的,我们没有办法在组件外简单加上style/class改动内部的样式。

    import { Calendar } from 'antd';
    ...
    <div className="myWrapper">
      <Calendar class="custom"/>
    </div>

    定位要覆盖的样式


    首先用开发者工具定位对应的样式:.ant-picker-calendar-date-today,这就是我们要修改的地方。

    2.png

    .ant-picker-calendar-full .ant-picker-panel .ant-picker-calendar-date-today {
        border-color: #1890ff; 
    }

    熟悉webpack的人应该知道,引入的CSS文件最终都会被style-loader处理。简单来说,它的作用就是把CSS文件打包,放在style标签内,最后塞进HTML中作为一个内部样式表。不管是组件库的样式还是我们写的自定义样式都是这样处理的。

    3.png

    我们要把组件库的样式先于自定义样式引入,这样自定义样式才能有更高的优先级。

    修改源文件


    直接改组件库的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会导致项目不好维护,不提倡使用)

    在这个基础上还有五种组合选择器要对优先级分数做累计,以类选择器为例:

    上面几个规则看着很复杂,其实用的多的就是第一个后代选择器,记住它就行。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.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组件,覆盖也就不会成功。

    4.png

    所以,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;
    }

    5.png

    借助: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元素都没有该属性。

    6.png

    所以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>

    7.png

    借助深度作用选择器,可以将要用于覆盖CSS“渗透”进组件内部。

    也可以将>>>写成/deep/或者::v-deep

    相较于React的:global,Vue的深度作用选择器是一种更优秀的方案,它必须要一个前导(也就是上面例子中的.myWrapper选择器),前导依旧会被打上哈希值作为属性选择器,要渗透进去的样式实际上是作为它的子选择器,只在当前这个文件下生效,彻底避免造成全局污染。

    结语

    本文通过如何修改UI组件内部样式为切入点,分析了几种解法。了解了组合选择器的优先级分数累加,以及在实际React、Vue项目用到的样式隔离方案——CSS Module和Scoped的原理,最后是介绍了在样式隔离的情况下,如何使用:global和深度作用选择器做样式覆盖。

    如果这篇文章对你有帮助,给我点个赞和在看吧~

    (学习视频分享:web前端开发编程基础视频

    以上就是如何覆盖组件库样式?React和Vue项目的解决方法浅析的详细内容,更多请关注php中文网其它相关文章!

    声明:本文转载于:掘金社区,如有侵犯,请联系admin@php.cn删除
    专题推荐:Vue vue.js React
    上一篇:浅析Vue前端路由中 hash 与 history的区别 下一篇:vue父子组件间如何进行通讯?(附示例)
    20期PHP线上班

    相关文章推荐

    • 【活动】充值PHP中文网VIP即送云服务器• react如何实现hooks?必须依赖 Fiber 么?• angular、react和vue有什么区别• 为什么react要用专门的组件来渲染列表• 通过9个Vue3 组件库,看看聊前端的流行趋势!• 聊聊vue指令中的修饰符,常用事件修饰符总结
    1/1

    PHP中文网