VUE 如何覆蓋element組件樣式


      最近在用element UI開發一個toB系統時,發現設計稿和UI庫有不小的出入,由於不是內部系統,所以這塊的還原度沒辦法“得過且過”。我整理了一些覆蓋UI庫樣式的“手段”

為什么UI庫(這里用的是element UI)的組件不好直接覆蓋?

         我們通常的vue工程都是用vue-cli自動生成出來的,不知道大家有沒有發現一個細節——生成的*.vue文件上會默認帶上“scoped”,如下圖:

                                   

UI庫不好覆蓋的問題也基本從這里開始了。首先看“scoped”是什么?首先“scoped”並不是vue的專利,(“scoped”屬性是HTML5的新特性,如果使用該屬性,則樣式僅僅應用到style元素的父元素及其子元素。)說人話就是vue用了scoped屬性,導致當前*.vue文件里的style僅僅作用於當前組件的元素,而對部分element UI的組件無效(一些簡單的組件,例如el-button這種簡單替換的還是可以覆蓋的)。

“scoped”在工程中是如何工作的?

        我們可以用自己的工程運行起來看一下。看看生成的頁面是什么樣的。

          

        可以看到,在vue中引入了scoped這個概念,scoped的設計思想就是讓當前組件的樣式不會修改到其它地方的樣式,使用了data-v-hash的方式來使css有了它對應模塊的標識,這樣寫css的時候不需要加太多額外的選擇器,方便很多。

  但是要注意scoped的作用域,因為權重的問題,如果是在子組件使用了scoped,那么在父組件中是不能直接修改子組件的樣式的,需要在父組件中使用vue的深度作用選擇器。

       問題來啦,我們在自己組件上用scoped初衷是好的。還拿上面的例子來說,我們data-v-a2a7b732是我們自己組件的模塊的標識,這里element UI對“簡單組件”並沒有用data-v-hash管理,我們再舉個“復雜組件”的例子,比如帶浮層的例如el-select,我們想把【全部數據的按鈕邊框去掉】

     

                                                                         

這樣寫出來發現在瀏覽器的選擇器里並沒有生效。對於“el-input__inner”生的只有庫本身的css樣式,我們看似“合理”的css繼承關系為什么沒有生效呢?我們來看一下我們自己的代碼到底生成了什么:

首先父級長這樣:data-type有了一個屬性選擇器確保唯一

工程生成的長這樣: 發現問題了嗎?

  • 給HTML的DOM節點加一個不重復data屬性(形如:data-v-a2a7b732)來表示他的唯一性
  • 在每句css選擇器的末尾(編譯后的生成的css語句)加一個當前組件的data屬性選擇器(如[data-v-a2a7b732])來私有化樣式

也就是說scope的操作是這樣的:我們的組件作為父級組件,調用其他組件(element UI)的場景下,scope僅僅作用於我們的當前組件,我們的組件每一級dom上多了一個data-v-hash,生成的css結尾加上了屬性"[data-v-hash]"(注意是每個css規則的結尾),這樣做的策略是保證css命中的葉子節點是在scope規則下的。那么我們在父級嵌套element ui時“el-input__inner”作為葉子節點即一條css規則的末尾被加上了后綴"[data-v-hash]",但是實際渲染DOM上element UI組件並不會加上屬性"[data-v-hash]"。因此后綴"[data-v-hash]"的css無法匹配屬性"[data-v-hash]"的DOM元素。也就不會生效。

為什么有的時候能覆蓋?

剛才簡單提到過,比較簡單的elementUI組件能搞定,我們看看為什么。這里有一個按鈕,我要附加一些樣式:

   

注意這里我就是正常寫了,給button加上一個自己的class:add-account,但是生效了。

        原因在於,elememt-ui中有一些組件,其實是單層級組件,比如:<el-button>替換成了<button class="el-button">而且會拷貝所有的class,因此element替換后仍是當前組件里的scoped控制,而上面提到的情況是組件內部深層元素不受scoped影響的情況。所以scoped場景下,仍可以控制其他組件的最外層dom,深層次規則就會出現前面的情況。而element-ui的“簡單組件”基本是單層結構,也就是當前層仍能添加"[data-v-hash]"。

下面我們來看一下幾種解決方案:

1、去掉scoped

剛才提到css不能覆蓋的原因是屬性"[data-v-hash]"導致的,那么最簡單的方法就是去掉“scoped”,但是一旦這樣做,當前組件中的css就可能污染組件外的空間,vue工程本來就比較龐大復雜,一個頁面很可能會加載很多的組件,這些組件頁有可能多人維護,難免名字相同,除非你的css有比較好的“命名空間管理”就像element-ui一樣。我個人建議還是不要輕易去掉。

2、多個<style></style>

一個vue文件可以寫多個<style>標簽,我們可以把大部分代碼寫在<style scoped>里,少數需要覆蓋子組件的寫在普通<style>中。但是這樣只是減小了污染,並沒有解決

3、/deep/ 或者 >>> 深度作用選擇器

還用最開始的el-select舉例,我添加了/deep/如下圖:

            生效了!沒有border了

某些預處理器(如Sass)可能無法>>>正確解析。在這些情況下,您可以使用/deep/組合器 - 它是別名>>>並且工作完全相同。這種方法,我比較推薦,頁很好用,但是並不是萬能的。

4、css import 

有一些element-ui組件會產生脫離當前從屬結構的DOM元素,比如el-dialog會在body中插入一段html,這個dom就不符合當前組件的從屬關系了,並不是當前組件的子元素。第一種方法是css import,這種元素仍可以產生一些特征來減少污染,element-ui對這類組件提供了一個css的命名權,即,你可以對子組件的某個結構單獨命名。例如el-dialog和el-table都有這樣的屬性:

 或 

這樣在body中插入的html就有了一個和組件名相關的class。我們可以在組件路徑下封一個單獨的css處理(不推薦寫到common里,不好維護)。

5、style-function

這也是我發現element獨特支持的方法,還用剛才的el-table舉例。

 我們可以傳入一個函數,return你要的樣式。

 

這種方法相當於是向特定dom上加上style。完全不污染全局,但是依賴ui庫自身提供接口。但是可以根據具體參數靈活計算。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM