<style scoped>
的作用方式<style scoped>
的特殊選擇器<style scoped>
的作用方式在使用 SFC 檔進行開發的狀況下,我們很常用 scoped
來鎖住 CSS style 影響的區域,讓樣式只應用在這個元件(或頁面)上,不會影響到其他元件(或頁面)的樣式。
那 Vue 是怎麼做到把樣式 scoped 住的呢?
Vue 會透過 PostCSS 來做 CSS 的後處理,至於處理規則為何?
直接到瀏覽器觀察看看,一般 <style>
和使用 scoped
兩種狀況下,轉換出來的 CSS 內容有什麼差別?
畫面結構(後續的範例觀察都使用相同的畫面結構)
<template>
<div class="wrapper">
<h1>APP 層的 h1</h1>
<HelloWorld/>
</div>
<template/>
<template>
<div class="greetings">
<h1>子元件層的 h1</h1>
</div>
<template>
<style>
h1 {
color: darksalmon;
}
</style>
結果:
HTML 架構
<div class="wrapper">
<h1>APP 層的 h1</h1>
<div class="greetings">
<h1>子元件層的 h1</h1>
</div>
</div>
CSS 樣式
h1 {
color: darksalmon;
}
<style>
內寫選擇器方式一模一樣,也就代表,整個專案 App 內,只要有 <h1>
,就會吃到這個樣式。<style>
內,也會有一樣的效果,可以預想樣式管理會變得比較混亂。未 scope
的樣式會影響到全域,包含祖先和後代
<style scoped>
h1 {
color: darksalmon;
}
</style>
結果
HTML 架構
<div class="wrapper" data-v-7a7a37b1>
<h1 data-v-7a7a37b1>APP 層的 h1</h1>
<div class="greetings" data-v-7a7a37b1>
<h1>子元件層的 h1</h1>
</div>
</div>
CSS 樣式
h1[data-v-7a7a37b1]{
color: darksalmon;
}
<style>
加上 scoped 屬性後,從渲染後的 HTML 可以發現,該元件 template 內的所有 HTML 元素,都會被加上 data-v-xxxxx
屬性,這個屬性可以想成是 Vue 給每個元件的 id。[data-v-xxxxx]
),這樣產生的 CSS 樣式,就只會套用在特定元件上。scoped 的樣式只會影響到同個元件(或頁面)的 HTML 元素
:deep()
在特定情況下,會希望從父元件影響子元件的樣式,但又不希望將樣式影響到全域,這時候就可以用 :deep()
深度選擇器,來突破 scoped 的限制。:deep()
在要修改 UI framework 元件的樣式時很好用。(這個等到引入 Quasar 篇章時再來示範)
<style scoped>
:deep(h1) {
color: darksalmon;
}
</style>
經過轉換後會生成 CSS 樣式如下:
[data-v-7a7a37b1] h1 {
color: darksalmon;
}
轉換結果為後代選擇器(descendant selector),會在該 CSS 樣式前,加上屬性選擇器 (屬性為元件的 id - data-v-xxxxx
)。
從上面的 CSS 程式碼可以理解到,所有後代元件的 <h1>
都會被這個樣式影響到,包括兒子、孫子、曾孫...,也就是說,使用 :deep()
後,所有後代都會被影響,使用上要特別注意!
:slotted()
Vue 預設傳入元件的 <slot/>
內容樣式,不會被當層元件的 scoped style 影響,因為 <slot/>
內容屬於他的父元件,那如果想在當前的元件做樣式管理,可以用:slotted()
。
畫面結構
<template>
<div class="wrapper">
<h1>APP 層的 h1</h1>
<HelloWorld><h1>子元件層的 h1</h1> </HelloWorld>
</div>
<template/>
<template>
<slot></slot>
<template>
結果
<slot>
後的渲染結果。<div class="wrapper">
<h1>APP 層的 h1</h1>
<div class="greetings" data-v-e17ea971>
<h1 data-v-e17ea971-s>子元件層的 h1</h1>
</div>
</div>
<style>
都加上 scoped
屬性後的渲染結果<div class="wrapper" data-v-7a7a37b1>
<h1 data-v-7a7a37b1>APP 層的 h1</h1>
<div class="greetings" data-v-e17ea971 data-v-7a7a37b1>
<h1 data-v-7a7a37b1 data-v-e17ea971-s>子元件層的 h1</h1>
</div>
</div>
觀察渲染結果可以發現:
而從父層傳入子層元件的 slot (<h1>
) 元素上,會帶有父層的識別屬性(data-v-7a7a),所以會被父層的 scoped style 影響。
除了父層的識別屬性之外, slot 內的元素還帶有 data-v-e17e-s
,是子層元件的識別屬性加上 -s
字符,表示這個元素是子層元件的 slot。
那要如何在子層影響外來的 slot 內容?
可以在子層(HelloWorld元件)的<style>
中使用 :slotted()
選擇器,使用方式如下:
<style scoped>
:slotted(h1) {
color: darksalmon;
}
</style>
所生成的 CSS 樣式為:
h1[data-v-e17ea971-s] {
color: darksalmon;
}
:slotted()
選擇器就是利用 slot 特有的識別屬性去選到他。
:global()
Vue 還有提供 :global()
選擇器,可以在 <style scope>
內使用,指定樣式轉換為全域樣式。
<style scoped>
:global(h1) {
color: darksalmon;
}
</style>
經過轉換會生成 CSS 樣式如下:
h1 {
color: darksalmon;
}
但是,如果真的要在同一個 SFC 檔內,定義全域和區域樣式,與其使用 :global()
選擇器,不如合併使用 <style>
和 <style scoped>
,將全域和區域樣式分開管理!
<style>
h1 {
color: blue;
}
h1 {
color: blue;
}
<style scoped>
h1 {
color: blue;
}
[data-v-元件特殊識別符]
屬性,利用元件的特殊識別符,讓樣式只套用到指定元件。h1[data-v-元件特殊識別符] {
color: blue;
}
:deep()
:指定樣式會影響所有後代元件
:deep(h1) {
color: blue;
}
[data-v-元件特殊識別符] h1 {
color: blue;
}
:slot()
:指定樣式可以影響到父層傳進來的 slot 結構
<style scoped>
:slotted(h1) {
color: darksalmon;
}
</style>
${父元件識別符}-s
),讓樣式只套用到外層傳入的 slot 結構上。h1[data-v-e17ea971-s] {
color: darksalmon;
}
<style scope>
內使用 :global()
選擇器<style scope>
和 <style>
兩個區塊管理