Vue:scoped與module的使用與利弊

clipboard.png

一個web應用是離不開html、css與js,其中css充斥的整個web項目中。css它有一個特定,它是全局的。這樣的特性致使的結果是,一旦你在不一樣的地方定義了相同的css命名,那麼它們的樣式就會相互覆蓋,最終致使的style錯亂,從而影響整個網頁佈局。css

我相信對於每個前端開發者都遇到過這種css樣式覆蓋的狀況,值得慶幸的是,這些問題前輩都已經給出瞭解決方案。html

在Vue中咱們經過Scoped與Module來解決。下面我會分別對scoped與module解決方案進行說明,最後在分析它們的利弊與選擇。若是你還未使用過或者說對它們之間的利弊與選擇存在疑問的,相信這篇文章可以幫你解惑。前端

Scoped

假設咱們有以下一段代碼:vue

index.vuegit

<template>
  <div class="content">
    <div class="title-wrap">我是紅色的</div>
    <green-title></green-title>
  </div>
</template>
 
<style lang="scss">
.content {
  .title-wrap {
    font-size: 20px;
    color: red;
  }
}
</style>

GreenTitle.vuegithub

<template>
  <div class="content">
    <div class="title-wrap">我是綠色的</div>
  </div>
</template>
 
<style lang="scss">
.content {
  .title-wrap {
    font-size: 20px;
    color: green;
  }
}
</style>

clipboard.png

最終這屏幕上展現的是兩行紅色的文字,這就是父組件與子組件都定義了title-wrap的樣式,致使子組件的樣式被父組件所覆蓋。web

遇到這種狀況,能夠在style標籤中添加scoped屬性佈局

<style lang="scss" scoped>
.content {
  .title-wrap {
    font-size: 20px;
    color: red;
  }
}
</style>

clipboard.png

scoped做用的阻止上層的css樣式傳遞到下層,限制當前css做用域,使其只對當前組件生效。學習

知道了它的做用,下面咱們在開深刻看下它的實現。this

clipboard.png

clipboard.png

前面的是沒有添加scoped的源碼,後面是添加了scoped的源碼。咱們進行一一對比,發現前面的兩個div標籤都使用了title-wrap樣式,天然致使樣式覆蓋;然後面的兩個div標籤,第一個增長了data-v-67e6b31f的前綴,這就是父組的style中增長scoped的效果,區別與第二個div中的title-wrap樣式。

scoped的實現是藉助了PostCSS實現的,一旦增長了scoped,他會將以前覆蓋的樣式轉換成下面的樣式

<style lang="scss">
.content[data-v-67e6b31f] {
  .title-wrap[data-v-67e6b31f] {
    font-size: 20px;
    color: red;
  }
}
</style>

經過這種轉換方式,間接的改變了原有的css命名。防止上層組件樣式覆蓋下層組件樣式。

特性

細心的讀者可能會發現上面的後一張源碼圖中第二個div的content中也有data-v-67e6b31f,可能會疑問,第二個content不是子組件中的css嗎?子組件中未添加scoped,爲何還會添加data-v-67e6b31f前綴?

這是scoped的一個特性,使用 scoped 後,父組件的樣式將不會滲透到子組件中。不過一個子組件的根節點會同時受其父組件有做用域的 CSS 和子組件有做用域的 CSS 的影響。這樣設計是爲了讓父組件能夠從佈局的角度出發,調整其子組件根元素的樣式。

因此若是咱們將子組件作以下修改

<template>
  <!-- <div class="content"> -->
    <div class="title-wrap">我是綠色的</div>
  <!-- </div> -->
</template>

clipboard.png

clipboard.png

因爲父組件scoped特性,因此會影響到子組件的title-wrap,也會添加data-v-67e6b31f前綴

那麼又有個疑問,增長了scoped是否就必定不能傳遞的下層組件呢?畢竟咱們可能有須要個別樣式傳遞到下層的需求。別急,接着看,這個也能很方便的解決。

深度做用

若是你但願scoped中的某個樣式可以做用的更深,影響到子組件,你可使用>>>操做符

<style scoped>
.content >>> .title-wrap {
    font-size: 20px;
    color: red;
}
</style>

注意看我將style中的lang="scss"去掉了,由於加了預處理器後沒法正確解析>>>,這種狀況可使用/deep/代替,本質是>>>的別名

<style lang="scss" scoped>
.content {
  /deep/ {
    .title-wrap {
      font-size: 20px;
      color: red;
    }
  }
}
</style>

將會編譯成

.content[data-v-67e6b31f] .title-wrap {
    font-size: 20px;
    color: red;
}

clipboard.png

經過 v-html 建立的 DOM 內容不受做用域內的樣式影響,可是你仍然能夠經過深度做用選擇器來爲他們設置樣式

Module

針對上面的覆蓋問題,還能夠經過設置module來解決

<template>
  <div :class="$style.content">
    <div :class="$style['title-wrap']">我是紅色的</div>
    <green-title></green-title>
  </div>
</template>
 
<style lang="scss" module>
.content {
  .title-wrap {
    font-size: 20px;
    color: red;
  }
}
</style>

clipboard.png

module的用法也很簡單,只要在style中增長module屬性便可。不一樣之處是它在佈局中的引用,都須要添加前綴$style。由於經過module做用的style都被保存到$style對象中。我能夠經過console查看它的具體引用名。

mounted() {
  console.log(this.$style)
  console.log(this.$style['title-wrap'])
}

clipboard.png

經過觀察,發現引用名有必定的規律。都是已index開頭,後面再接着style中定義的命名,最後再接個後綴。這裏的index是父組件的文件名index.vue。因此經過module做用的style將會從新命名爲:文件名_原style名_不定後綴。

這麼命名又有什麼好處呢?咱們再來看下展現的效果

clipboard.png

當咱們在瀏覽的控制檯查看Elements時,優勢顯而易見。相對於scoped的方式,module的方式可以一眼知道該元素時屬於哪一個文件組件中。在大型項目中可以幫助咱們迅速定位到要查找的組件。

除了上述的快速定位,因爲module會將全部的style都納入$style中,因此咱們能夠很靈活的將任意的父組件樣式傳遞到任意深層的子組件中。例如,將父組件中的title-wrap經過props傳遞到子組件中

<template>
  <div :class="$style.content">
    <div :class="$style['title-wrap']">我是紅色的</div>
    <green-title :styleTitle="$style['title-wrap']"></green-title>
  </div>
</template>
<template>
  <div class="content">
    <div :class="styleTitle">我是綠色的</div>
  </div>
</template>
<script>
 
export default {
  props: {
    styleTitle: String,
  },
}
</script>

clipboard.png

clipboard.png

module還有一個特性很是不錯,它能夠導出定義的變量,將變量納入$style中,例如:

<template>
  <div :class="$style.content">
    <div :class="$style['title-wrap']">我是紅色的</div>
    <green-title :styleTitle="$style['title-wrap']"></green-title>
    <div>{{$style.titleColor}}</div>
  </div>
</template>
 
<style lang="scss" module>
$title-color: red;
:export {
  titleColor: $title-color
}
.content {
  .title-wrap {
    font-size: 20px;
    color: $title-color;
  }
}
</style>

clipboard.png

更多module相關操做能夠 點擊查看

總結

scoped與module都很是簡單、易用,那麼又該如何選擇呢?

經過上面的使用對比,發現scoped不須要額外的知識,只要在style中定義scoped屬性便可,使用很是簡便。但它的侷限性是適用於中小項目中。由於scoped做用的style對於咱們來講不直觀,對於快速查找定位,module更加合適,同時module對於style向下傳遞的控制權也很是靈活;額外的還有變量導出等便捷功能。

因此若是你是小項目中且低成本的使用,scoped更加適合;而對大項目module更加合適,雖然有一點學習成本,但對於用更好的控制權、可觀性與定位速度來講也就不值一提。

公衆號

感受不錯的能夠來一波關注,掃描下方二維碼,關注公衆號:怪談時間,及時獲取最新知識技巧與互聯網新動態。

clipboard.png

相關文章
相關標籤/搜索