scoped 屬性的作用是避免父元件的 CSS 樣式會污染到子元件的 CSS 樣式。Deep selector 的作用是相反,即使在父元件設定了 scoped css,仍然容許讓父元件的 CSS 樣式穿透到子元件的 CSS 樣式。
以下會詳細討論答案的內容。
在 .vue
檔案裏,在 style
裏加上 scoped
屬性的作用是避免目前元件的 style 會污染到子元件的 style。開發時,大多情況都建議加上 scoped
。
scoped
的效果會經由 vue-loader
來處理。vue-loader
是一個 Webpack 的 loader,它負責解析文件,如有需要會再引用其他 loader 來解析文件內容。最後把所有解析好的資源輸出為一個 ES Module,並預設以物件型別滙出。
回到重點,要實現 scoped
,我們只需在 style 裏加上 scoped
:
<style lang="scss" scoped>
</style>
舉例說,現在有一個 Home
元件,裏面再包一個 Message
元件。
我們在 Home
元件裏的 style 使用 scoped
,並且設定 span 的顏色為紅色。結果是只在 Home
裏的 span 會變成紅色。
Home:
<template>
<div>
<span> 首頁的文字 </span>
<Message />
</div>
</template>
<script>
import Message from "@/components/Message";
export default {
components: {
Message,
},
};
</script>
<style lang="scss" scoped>
span {
color: red;
}
</style>
Message:
<template>
<div>
<span>
Message 外層文字
<span> Message 內層文字 </span>
</span>
</div>
</template>
<script>
export default {};
</script>
<style lang="scss">
</style>
結果:
看看目前的 HTML 結構:
Vue loader 編釋後,加上 data attribute 來指定只有 Home 元件才會套上 color: red;
這個樣式。因此只有「首頁的文字」才會變成紅色。
在父元件加上 scoped
,看似就不會污染到子元件。但其實會污染到子元件的根部。
重用以上例子,如果把 Message
裏最外層的 div
刪走,結果是「外層 Message」和「內層 Message」都會受影響。以下示範把 Message
最外層的 div
拿走:
<template>
<span>
Message 外層文字
<span> Message 內層文字 </span>
</span>
</template>
<script>
export default {};
</script>
<style lang="scss">
</style>
結果:
因為沒有了 div,而 span 是行內元素,所以這三段文字都排在同一行。而重點是,現在 Message 元件裏的所有文字都變成紅色。
HTML 結構:
在前一個示範,本來 data-fae5bece
這個屬性是套用在 div
上,現在因為把 div
刪掉,所以直接套在 span
上,以致整個 Message
元件裏的 span
都是紅色。
這就是官方文件提及的意思:
使用 scoped 后,父组件的样式将不会渗透到子组件中。不过一个子组件的根节点会同时受其父组件的 scoped CSS 和子组件的 scoped CSS 的影响。这样设计是为了让父组件可以从布局的角度出发,调整其子组件根元素的样式。
重用以上例子,如果在 Message
加上 scoped,並設定 span { color: blue; }
。那麼目前 Message
元件就會同時受父元件和它自己的 scoped 影響,換言之,它會有兩個 data attribute。
以下示範在 Message
元件加上它自己的 span 樣式,顏色是藍色。
Message.vue:
<template>
<span> Message 外層文字
<span> Message 內層文字 </span>
</span>
</template>
<script>
export default {}
</script>
<style lang="scss" scoped>
span {
color: blue;
}
</style>
結果:
HTML 結構:
最外層的 span
被加上兩個 data attribute,但 Home 的 data attribute 被劃掉,因此可見父元件的 scoped CSS 是優先於子元件的 scoped CSS。
https://codesandbox.io/s/scoped-css-lbc9z
deep selector 就是做相反的事。意思是無視 scoped 的限制,即使設定了 scoped,還是可以把父元件的樣式穿透到子元件。
deep selector 的寫法有幾種:
.home >>> .message {
color: red;
}
.home:deep .message {
color: red;
}
.home /deep/ .message {
color: red;
}
.home::v-deep .message {
color: red;
}
另外也可以把前面的父元件省略:
:deep .message {
color: red;
}
/deep/ .message {
color: red;
}
::v-deep .message {
color: red;
}
注意:
>>>
不能被編釋。:deep
代替 ::v-deep
。修改之前的例子,在 Home 最外層加上 home
class,在 Message 的外層加上 message
class。最後,在 Home.vue 檔案使用 deep selector:
Home.vue:
<template>
<div class="home">
...
</div>
</template>
<script>
...
</script>
<style scoped lang="scss">
span {
color: red;
}
.home:deep .message {
color: red;
}
</style>
Message.vue:
<template>
<div class="message">
...
</div>
</template>
<script>
export default {}
</script>
<style lang="scss" scoped></style>
結果:
HTML 結構:
因為我的例子是使用 scss 來寫 CSS,所以不能使用 >>>
的寫法。另外,如果我們在子元件設定 span 樣式,像以下做法:
Message.vue:
<template>
<div class="message">
<span> Message 外層文字
<span> Message 內層文字 </span>
</span>
</div>
</template>
<script>
export default {
}
</script>
<style lang="scss" scoped>
span {
color: blue;
}
</style>
結果是子元件的文字會變成藍色,優先於父元件設定的紅色:
HTML 結構:
https://codesandbox.io/s/scoped-css-deep-selector-k9111?file=/src/components/Message.vue
當我們以元件的方式來載入第三方套件,並且想修改它的樣式,就有機會要用 deep selector。例如我曾經在專案中以元件方式載入 CKEditor,但找不到在哪裏可以修改 CKEditor 輸入框的高度,於是我使用 deep selector 的方式來處理:
引入 ckeditor 元件來載入 CKEditor:
<ckeditor v-model="editorData" :editor="editor" :config="editorConfig"></ckeditor>
我當時在 style ,針對 CKEditor 裏某個 class 的設定:
:deep .ck-editor__editable {
height: 400px;
}
.vue
檔案,以及透過 data attribute 的方法,實現 scoped CSS。>>>
、::v-deep
(不建議使用)、 :deep
、/deep/
。 但sass 或 scss 無法編釋 >>>
。/deep/ 是什麼? — 聊聊 Vue 裡的 scoped css
Vue Loader - Deep Selectors