一個(gè)web應(yīng)用是離不開html、css與js带到,其中css充斥的整個(gè)web項(xiàng)目中。css它有一個(gè)特定,它是全局的超营。這樣的特性導(dǎo)致的結(jié)果是庆揩,一旦你在不同的地方定義了相同的css命名镊叁,那么它們的樣式就會(huì)相互覆蓋唾琼,最終導(dǎo)致的style錯(cuò)亂,從而影響整個(gè)網(wǎng)頁(yè)布局疯趟。
我相信對(duì)于每一個(gè)前端開發(fā)者都遇到過這種css樣式覆蓋的情況拘哨,值得慶幸的是,這些問題前輩都已經(jīng)給出了解決方案迅办。
在Vue中我們通過Scoped與Module來解決宅静。下面我會(huì)分別對(duì)scoped與module解決方案進(jìn)行說明,最后在分析它們的利弊與選擇站欺。如果你還未使用過或者說對(duì)它們之間的利弊與選擇存在疑問的姨夹,相信這篇文章能夠幫你解惑。
Scoped
假設(shè)我們有如下一段代碼:
index.vue
<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.vue
<template>
<div class="content">
<div class="title-wrap">我是綠色的</div>
</div>
</template>
<style lang="scss">
.content {
.title-wrap {
font-size: 20px;
color: green;
}
}
</style>
最終這屏幕上展示的是兩行紅色的文字矾策,這就是父組件與子組件都定義了title-wrap的樣式磷账,導(dǎo)致子組件的樣式被父組件所覆蓋。
遇到這種情況贾虽,可以在style標(biāo)簽中添加scoped屬性
<style lang="scss" scoped>
.content {
.title-wrap {
font-size: 20px;
color: red;
}
}
</style>
scoped作用的阻止上層的css樣式傳遞到下層逃糟,限制當(dāng)前css作用域,使其只對(duì)當(dāng)前組件生效。
知道了它的作用绰咽,下面我們?cè)陂_深入看下它的實(shí)現(xiàn)菇肃。
前面的是沒有添加scoped的源碼,后面是添加了scoped的源碼取募。我們進(jìn)行一一對(duì)比琐谤,發(fā)現(xiàn)前面的兩個(gè)div標(biāo)簽都使用了title-wrap樣式,自然導(dǎo)致樣式覆蓋玩敏;而后面的兩個(gè)div標(biāo)簽斗忌,第一個(gè)增加了data-v-67e6b31f的前綴,這就是父組的style中增加scoped的效果旺聚,區(qū)別與第二個(gè)div中的title-wrap樣式织阳。
scoped的實(shí)現(xiàn)是借助了PostCSS實(shí)現(xiàn)的,一旦增加了scoped砰粹,他會(huì)將之前覆蓋的樣式轉(zhuǎn)換成下面的樣式
<style lang="scss">
.content[data-v-67e6b31f] {
.title-wrap[data-v-67e6b31f] {
font-size: 20px;
color: red;
}
}
</style>
通過這種轉(zhuǎn)換方式唧躲,間接的改變了原有的css命名。防止上層組件樣式覆蓋下層組件樣式碱璃。
特性
細(xì)心的讀者可能會(huì)發(fā)現(xiàn)上面的后一張?jiān)创a圖中第二個(gè)div的content中也有data-v-67e6b31f惊窖,可能會(huì)疑問,第二個(gè)content不是子組件中的css嗎厘贼?子組件中未添加scoped,為什么還會(huì)添加data-v-67e6b31f前綴圣拄?
這是scoped的一個(gè)特性嘴秸,使用 scoped 后,父組件的樣式將不會(huì)滲透到子組件中庇谆。不過一個(gè)子組件的根節(jié)點(diǎn)會(huì)同時(shí)受其父組件有作用域的 CSS 和子組件有作用域的 CSS 的影響岳掐。這樣設(shè)計(jì)是為了讓父組件可以從布局的角度出發(fā),調(diào)整其子組件根元素的樣式饭耳。
所以如果我們將子組件做如下修改
<template>
<!-- <div class="content"> -->
<div class="title-wrap">我是綠色的</div>
<!-- </div> -->
</template>
由于父組件scoped特性串述,所以會(huì)影響到子組件的title-wrap,也會(huì)添加data-v-67e6b31f前綴
那么又有個(gè)疑問寞肖,增加了scoped是否就一定不能傳遞的下層組件呢纲酗?畢竟我們可能有需要個(gè)別樣式傳遞到下層的需求。別急新蟆,接著看觅赊,這個(gè)也能很方便的解決。
深度作用
如果你希望scoped中的某個(gè)樣式能夠作用的更深琼稻,影響到子組件吮螺,你可以使用>>>
操作符
<style scoped>
.content >>> .title-wrap {
font-size: 20px;
color: red;
}
</style>
注意看我將style中的lang="scss"去掉了,因?yàn)榧恿祟A(yù)處理器后無(wú)法正確解析>>>
,這種情況可以使用/deep/代替鸠补,本質(zhì)是>>>
的別名
<style lang="scss" scoped>
.content {
/deep/ {
.title-wrap {
font-size: 20px;
color: red;
}
}
}
</style>
將會(huì)編譯成
.content[data-v-67e6b31f] .title-wrap {
font-size: 20px;
color: red;
}
通過 v-html 創(chuàng)建的 DOM 內(nèi)容不受作用域內(nèi)的樣式影響萝风,但是你仍然可以通過深度作用選擇器來為他們?cè)O(shè)置樣式
Module
針對(duì)上面的覆蓋問題,還可以通過設(shè)置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>
module的用法也很簡(jiǎn)單紫岩,只要在style中增加module
屬性即可规惰。不同之處是它在布局中的引用,都需要添加前綴$style
被因。因?yàn)橥ㄟ^module作用的style都被保存到$style
對(duì)象中卿拴。我可以通過console查看它的具體引用名。
mounted() {
console.log(this.$style)
console.log(this.$style['title-wrap'])
}
通過觀察梨与,發(fā)現(xiàn)引用名有一定的規(guī)律堕花。都是已index開頭,后面再接著style中定義的命名粥鞋,最后再接個(gè)后綴缘挽。這里的index是父組件的文件名index.vue。所以通過module作用的style將會(huì)重新命名為:文件名原style名不定后綴呻粹。
這么命名又有什么好處呢壕曼?我們?cè)賮砜聪抡故镜男Ч?/p>
當(dāng)我們?cè)跒g覽的控制臺(tái)查看Elements時(shí),優(yōu)點(diǎn)顯而易見等浊。相對(duì)于scoped的方式腮郊,module的方式能夠一眼知道該元素時(shí)屬于哪個(gè)文件組件中。在大型項(xiàng)目中能夠幫助我們迅速定位到要查找的組件筹燕。
除了上述的快速定位轧飞,由于module會(huì)將所有的style都?xì)w入$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>
module還有一個(gè)特性非常不錯(cuò),它可以導(dǎo)出定義的變量制妄,將變量歸入$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>
更多module相關(guān)操作可以點(diǎn)擊查看
總結(jié)
scoped與module都非常簡(jiǎn)單、易用耕捞,那么又該如何選擇呢衔掸?
通過上面的使用對(duì)比,發(fā)現(xiàn)scoped不需要額外的知識(shí)砸脊,只要在style中定義scoped屬性即可具篇,使用非常簡(jiǎn)便。但它的局限性是適用于中小項(xiàng)目中凌埂。因?yàn)閟coped作用的style對(duì)于我們來說不直觀驱显,對(duì)于快速查找定位,module更加合適,同時(shí)module對(duì)于style向下傳遞的控制權(quán)也非常靈活埃疫;額外的還有變量導(dǎo)出等便捷功能伏恐。
所以如果你是小項(xiàng)目中且低成本的使用,scoped更加適合栓霜;而對(duì)大項(xiàng)目module更加合適翠桦,雖然有一點(diǎn)學(xué)習(xí)成本,但對(duì)于用更好的控制權(quán)胳蛮、可觀性與定位速度來說也就不值一提销凑。
公眾號(hào)
感覺不錯(cuò)的可以來一波關(guān)注,微信搜索關(guān)注公眾號(hào):怪談時(shí)間
仅炊,及時(shí)獲取最新知識(shí)技巧與互聯(lián)網(wǎng)新動(dòng)態(tài)斗幼。