前言
首先,我們來回憶一下「CSS 作用域」這一概念,它的本質是通過讓每一個選擇器成爲一個「unique」的存在,這樣就自然而然地形成了作用域。
而提到「Vue」中「作用域 CSS」,我想大家應該立即想到以 scoped
的方式形成的帶有作用域的 css
。但是,值得一提的是,在「Vue」中還支持了一種「作用域 CSS」,即「CSS Module」。
提及 「CSS Module」,想必大家會有點陌生,相信有很多同學在平常開發中都是用 scoped
來實現「Vue」組件中的「作用域CSS」。所以,今天我們就來詳細認知一下這兩者。
一、scoped 作用域
「scoped 作用域」是「Vue」通過「postcss」來實現對每一個在 scoped
標籤中定義的選擇器的特殊作用域標識,例如:
<template>
<div id="app">
<div class="out-box">
</div>
</div>
</template>
<style lang="scss" scoped>
#app {
.out-box {
width: 200px;
height: 200px;
background-color: #faa;
}
}
</style>
標識後展示在頁面上的:
<div data-v-7ba5bd90 id="app">
<div data-v-7ba5bd90 class="out-box">
</div>
</div>
<style>
#app .out-box[data-v-7ba5bd90] {
width: 200px;
height: 200px;
background-color: #faa;
}
</style>
可以看到,本質上是在原有的「選擇器」的基礎上通過「postcss」加上了一串 attr
。
並且,在我們平常開發中,很常見的場景就是我們在使用一些已有的組件或第三方組件時,我們需要對原有組件的樣式進行一些微小的改動。那麼,這個時候就需要使用穿透來實現樣式的改動,例如:
<style>
div >>> .out-box {
background-color: #aaf;
}
</style>
這裏需要注意的是 >>> 只是一種穿透方式,並不是所有場景下都是可以用 >>> 實現。例如,「iView」需要使用 /deep/ 的方式,「ElementUI」需要使用 ::v-deep 的方式。
二、CSS Module 作用域
相比較「scoped 作用域」,「CSS Modeul 作用域」它所具備的能力更強,所以內容也相對較多。
什麼是 CSS Module
「CSS Module」指的是可以將一個定義好的「CSS」文件以變量的形式導入,然後通過使用這個變量對「HTML」中的元素進行樣式的修飾,例如:
a.css
.box {
width: 100%;
height: 100%;
background-color: #afa;
}
b.js
import style from './a.css'
const boxElem = document.createElement('div');
boxElem.className = style.box
然後,渲染到頁面的時候,它對應的 HTML 看起來會是這樣:
<div class="a-box_jlpNx"></div>
可以看出,此時的「類選擇器」同樣是隨機生成的,這也是「CSS Module」形成作用域的所在。
值得一提的是,「CSS Module」還具備其他的能力,例如可以定義全局的「選擇器」,寫起來會是這樣:
:global(.title) {
color: green;
}
接下來的使用和局部的一樣。至於,其他用法,有興趣的同學可以去 GitHub 上自行閱讀
CSS Module 在 Vue 中的應用
回到本文所說的,在「Vue」中也對「CSS Module」做了相應的支持,當我們在 style 標籤上添加 module 屬性時,「Vue」 會在當前「組件實例」上注入一個計算屬性 $style
,然後我們可以通過 $style
來使用我們定義好的「選擇器」,例如:
<template>
<div>
<div :class="$style['inner-box']"></div>
</div>
</template>
<style lang="scss" module>
.inner-box {
width: 100px;
height: 100px;
background-color: #aaf;
}
</style>
然後,它渲染到頁面時,對於的 HTML 會是這樣:
<div class="HelloWorld_inner-box_jlpNx"></div>
那麼,這個時候,又回到和「scoped 作用域」一樣的問題,使用了「CSS Module」來定義組件的樣式,那麼我在使用它的時候,如何進行覆蓋?
標準的答案,對於「CSS Module」並沒有覆蓋的說法,有的只是爲一個組件設置不同的主題。
但是,如果真的需要弄,那隻能通過對該模塊對應的
style
標籤中定義你需要的樣式,然後根據傳入組件的props
來動態綁定class
那麼,爲組件設置主題,我們需要在設計組件的時候,對這個組件預留 props
,並將該 props
添加到 $style
中,然後在這個組件中的相應元素中使用,例如:
Box.vue 組件
<template>
<div>
<div :class="$style[themeColor]"></div>
</div>
</template>
<script>
export default {
props: {
themeColor: {
type: String,
default: 'line'
}
}
};
</script>
<style lang="scss" module>
.line {
width: 100px;
height: 100px;
background-color: #aaf;
}
.card {
background-color: #aaf;
}
</style>
然後,我們在使用該組件的時候通過 props
傳入 line
或者 card
,從而實現切換組件不同的背景色。
三、兩種方式的優劣勢
「scoped 作用域」:
- 對組件沒有硬性要求
- 不易於管理組件樣式,需要藉助第三方變量定義支持
- 易於覆蓋組件樣式,即通過穿透來實現對樣式的覆蓋
「CSS Module 作用域」:
- 適合於高度沉澱下的組件使用
- 易於管理組件樣式,即可以通過
style
管理組件中的選擇器 - 組件樣式無法通過外部直接覆蓋
這裏所說的管理,是指通過
JavaScript
便捷地控制組件樣式。
寫在最後
其實,對比「scoped」和「CSS Module」兩者,各有千秋。至於,要用哪一着得看具體需求,建議大項目中可以使用「CSS Module」,小項目的話用用「scoped」應該綽綽有餘。
寫作不易,如果你覺得有收穫的話,可以帥氣三連擊!!!