首頁  >  文章  >  web前端  >  如何覆蓋元件庫樣式? React和Vue專案的解決方法淺析

如何覆蓋元件庫樣式? React和Vue專案的解決方法淺析

青灯夜游
青灯夜游轉載
2022-05-16 11:15:524341瀏覽

如何覆蓋元件庫樣式?以下這篇文章跟大家介紹一下ReactVue專案中優雅地覆蓋元件庫樣式的方法,希望對大家有幫助!

如何覆蓋元件庫樣式? React和Vue專案的解決方法淺析

元件庫的樣式覆蓋不掉,這應該是很多前端在工作上遇到過的問題。今天從實際案例出發分析原因,最後會給出在React和Vue專案中的最優解。

本文會講清楚:

  • React中CSS Module的原理是什麼? :global是做什麼的?

  • Vue中Scoped的原理是什麼?深度作用選擇器是什麼? (學習影片分享:vue影片教學

先不講概念,直接從需求出發:我使用了Antd元件庫來展示一個行事曆。

如何覆蓋元件庫樣式? React和Vue專案的解決方法淺析

現在我想將目前日期上面的藍色邊框變成紫色。

可以試試你能不能實現。

不管是React還是Vue,整個Calendar是被封裝起來的,我們沒有辦法在元件外簡單加上style/class改變內部的樣式。

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

定位要覆寫的樣式


先用開發者工具定位對應的樣式:.ant-picker-calendar-date-today ,這就是我們要修改的地方。

如何覆蓋元件庫樣式? React和Vue專案的解決方法淺析

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

熟悉webpack的人應該知道,引入的CSS檔案最終都會被style-loader處理。簡單來說,它的作用就是把CSS檔案打包,放在style標籤內,最後塞進HTML中作為一個內部樣式表。不管是元件庫的樣式還是我們寫的自訂樣式都是這樣處理的。

如何覆蓋元件庫樣式? React和Vue專案的解決方法淺析

我們要把元件庫的樣式先於自訂樣式引入,這樣自訂樣式才能有更高的優先權。

修改原始檔


#直接改元件庫的CSS原始碼是最簡單粗的方法。打開你專案的node_modules資料夾,一層層點開,找到對應樣式文件,依照需求修改即可。

個人專案這樣處理確實可行,但是團隊合作時,同步別人本地的node_modules就比較麻煩,只能算一個60分解法。

全域CSS檔案


之前提到,把自己寫的的CSS檔案放在元件庫的樣式後面,可以保障自訂有更高優先級。只要重寫同名的樣式,理論上就能實現覆蓋組了。

但這樣?處理會發現並不起作用:

/* src/demo.css */
.ant-picker-calendar-date-today {
  border-color: purple; /* 覆盖为紫色 */
}
// src/Demo.js

// 组件库的样式
import &#39;ant-design-vue/dist/antd.css&#39;; 
// 自定义样式
import &#39;./demo.css&#39;
import { Calendar } from &#39;antd&#39;;
...
<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.js
import styles from &#39;./demo.module.css&#39;;
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和Vue專案的解決方法淺析

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

如何覆蓋元件庫樣式? React和Vue專案的解決方法淺析

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

如何覆蓋元件庫樣式? React和Vue專案的解決方法淺析

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

如何覆蓋元件庫樣式? React和Vue專案的解決方法淺析

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

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

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

结语

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

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

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

以上是如何覆蓋元件庫樣式? React和Vue專案的解決方法淺析的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:juejin.cn。如有侵權,請聯絡admin@php.cn刪除