vue學習(十四)Vue.js中scoped引發的CSS作用域探討
前言
在Vue.js的元件化開發中,常常會對某個元件的style標籤加上scoped屬性,如<style lang='less' scoped>
,這樣做的目的在於使這個元件的樣式不能輕易在其他地方被有意或無意修改,以達到封裝的目的。值得注意的是,當我們進行元件巢狀時,常常需要修改子元件的預設樣式,由元件封裝引發的樣式修改失效問題常常令人頭痛,這其中涉及到的CSS作用域並不簡單。因此,只有我們深入理解了Vue.js元件的CSS作用域問題,才能靈活地控制組件的樣式以達到預期效果。本文正是基於此,對Vue.js元件的CSS作用域問題進行探討。
一、簡單結構搭建
這裡將要闡述一個簡單的示例,父元件為App
Child
。
子元件Child
的定義檔案Child.vue
中程式碼如下:
<template>
<div id="child">
<h2>子元件h2</h2>
<h3>子元件h3</h3>
</div>
</template>
<script>
export default {
name: 'Child'
}
</script>
<style scoped>
h3 {
background-color: blue;
}
</style>
<style>
h2 {
background -color: blue;
}
</style>
可以看到,子元件Child
有一個根元素div#child
,根元素下有兩個子元素h2
和h3
。在樣式中設定h2
和h3
背景顏色也為藍色,為便於比較,h3
使用了scoped
,h2
則未使用scoped
。
現在來看父元件App
,它將巢狀子元件Child
,程式碼如下:
<template>
<div id="app">
<Child/>
<h2>父元件h2</h2>
<h3>父元件h3</h3>
</div>
</template>
<script>
import Child from './components/Child'
export default {
name: 'App',
components: {
Child
}
}
</script>
<style scoped>
h3 {
background-color: red;
}
</style>
<style>
h2 {
background-color: red;
}
</style>
父元件App
有一個根元素div#app
,根元素中首先嵌套了子元件Child
,然後是自有的h3和h2元素
。在樣式中設定h2
和h3
背景顏色為紅色,同樣,h3
使用了scoped
,h2
則未使用scoped
。
二、執果索因:先看效果
以上結構搭建完成後,我們很可能相當好奇:
我們先來看瀏覽器顯示的效果:
這個結果可能令我們相當意外。子元件顯示了子元件自身定義的樣式,然而父元件的h2
顯示了子元件中對h2
設定的樣式,父元件的h3
顯示了父元件自身對h3
的樣式設定。這可能相當令人困惑,事實上,這正是我們要探討的是否加scoped引發的樣式的作用域問題。
三、html結構規律:data-v-x
我們先在瀏覽器中檢視實際生成的html程式碼,如下:
<div data-v-04c2046b id="app">
<div data-v-10e5fd3c data-v-04c2046b id="child">
<h2 data-v-10e5fd3c>子元件h2</h2>
<h3 data-v-10e5fd3c>子元件h3</h3>
</div>
<h2 data-v-04c2046b>父元件h2</h2>
<h3 data-v-04c2046b>父元件h3</h3>
</div>
對於結構,我們應該不會有什麼疑問,重點是其中html元素
增加的data-v-x屬性(x是一串數字)
。
需要說明的是,這裡的x
不是特定的,可能隨測試環境的不同而不同,上面是筆者測試時生成的情況。
可以發現,父元件App的根元素及子元素
都增加了一個特定的[data-v-04c2046b]屬性
,子元件Child
的根元素及子元素
都增加了一個另一個特定的[data-v-10e5fd3c]屬性
,並且子元件根元素
同時增加[data-v-04c2046b]屬性
和[data-v-10e5fd3c]屬性`。
那麼是否可以總結出結論:元件的根元素、子元素、子元件根元素都會增加data-v-x屬性呢?
首先,並非所有元件都會增加data-v-x屬性
,事實上是否會增加data-v-x屬性
是由元件自身是否存在含scoped
的style
標籤決定的。
也就是說,如果元件中不存在style
標籤或者雖然有style標籤但是沒有加scoped
,那麼元件的元素便不會增加data-v-x屬性
。因此,得出第1個結論:
結論1:data-v-x
屬性的增加是由元件中包含scoped
的style
標籤引發。
第二個問題是,以上示例中,父元件和子元件都最多隻有兩層html結構,假使html元素巢狀得更深,那麼元件的後代元素是否都會增加data-v-x屬性
呢,而不僅僅是子元素?
答案是肯定的,即結論2:
結論2:元件的根元素、元件自身的後代元素、子元件的根元素都會加上該元件特定的data-v-x
屬性。
這裡,之所以用了“自身”這個詞,原因在於子元件的後代元素雖然也會編譯成父元件的後代元素,卻不會加上父元件的data-v-x
屬性(下同)。
那麼,子元件哪些元素會增加子元件特定的data-v-x
屬性呢?事實上,由結論2,只要將子元件看作一個沒有子元件的父元件,那麼不難得出:
結論3:子元件的根元素、子元件的後代元素會加上子元件特定的data-v-x
屬性。
四、CSS樣式應用規律
現在,我們來看CSS樣式是怎樣作用的。
(一)不加scoped的style樣式
父元件和子元件都設定了h2
元素的樣式,對於父元件和子元件的h2
元素,起作用的都是子元件的樣式設定,即:
h2 {
background-color: blue;
}
同時,瀏覽器中可以看到,父元件中的樣式設定被覆蓋:
h2 {
background-color: red;
}
首先,我們發現兩者h2
選擇器後面都沒有加上[data-v-x]
,即結論1:
結論1:無論父子元件,不加scoped的style標籤中的選擇器不會增加[data-v-x]
屬性選擇器。
同時,由於父元件的樣式被覆蓋,因此得出第二個結論:
結論2:對於同一個選擇器,子元件不含scoped的style標籤中的樣式優先順序高於父元件。
(二)加scoped的style樣式
對於父元件的h3
元素,起作用的是
h3[data-v-04c2046b] {
background-color: red;
}
對子元件的h3
元素,生效的是
h3[data-v-10e5fd3c] {
background-color: blue;
}
我們發現,只有元件自身含scoped的style標籤中對h3設定的樣式生效。
這不難理解,寫在含scoped
的style
標籤中的選擇器都會加上形如[data-v-04c2046b]
這樣的屬性選擇器字尾。雖然在父、子元件含scoped
的style
標籤中都對h3
樣式進行了設定,但h3
加上的是不同的data-v-x
屬性選擇器字尾,因此相互不能匹配,彼此毫無影響。
因此,得出以下結論:
結論3:無論父子元件,加scoped的style標籤中的選擇器都會增加[data-v-x]
屬性選擇器。
結論4:對於同一個選擇器,父子元件含scoped的style標籤中的樣式互不影響。
(三)同一個元件,加或不加scoped
如果我們在子元件含scoped的style中增加:
h2 {
background-color: orange;
}
那麼,最終效果如下:
現在,我們捋一捋為什麼會是這個效果:
首先,來看子元件的h3
,父元件含scoped
的style
對h3
的設定不會影響子元件(結論4),那麼子元件的h3
將顯示藍色。對於父元件的h3
,同樣由結論4,顯示紅色。對於父元件的h2,由結論2顯示藍色,並且子元件含scoped的style中對h2的設定影響不到父元件。
對於子元件的h2
,由結論2,本應顯示藍色,但是,子元件含scoped的style中對h2的設定將覆蓋子元件不含scoped的style中對h2的設定,因此顯示黃色。
由上,我們得出新的結論:
結論5:在同一個元件中,對於同一個選擇器,含scoped的style中設定的樣式優先順序高於不含scoped的style中設定的樣式。
這不難理解,含scoped的style中的選擇器會加上特定的data-v-x
屬性選擇器字尾,優先順序將超過不帶字尾的選擇器。
五、小結
html篇:
1、某個元件自身的元素是否會加上data-v-x
屬性,看該元件是否有帶scoped的style標籤,而不論這個style標籤中有無樣式設定,寫了什麼樣式。
2、若某個元件有帶scoped的style標籤,則該元件的根元素、元件自身的後代元素、子元件的根元素都會增加data-v-x
屬性。
3、子元件的根元素會受到父元件data-v-x
屬性影響,非根元素則始終不會。
CSS篇:
1、含scoped的樣式:父子元件互不影響,原因是增加了屬性選擇器字尾,彼此不能匹配。
2、不含scoped的樣式:父子元件相互影響,但子元件優先順序高於父元件。
3、同一個元件:不含scoped和含scoped的樣式,含scoped的優先順序更高,原因也是含scoped的選擇器增加了屬性選擇器字尾。