🧑💻 寫在開頭
點贊 + 收藏 === 學會🤣🤣🤣
“你用 Element Plus 寫了個按鈕,想改下 hover 顏色,結果死活不生效!最後查了半天,發現得加個 :deep() 才行”
其實,這是 Vue 中一個非常常見的坑:樣式作用域衝突。那為什麼用 UI 庫時,加上 :deep()、::v-deep 或 >>>後,樣式就能生效呢?
它們是什麼?有什麼區別?什麼時候該用哪個?
一、先説背景
我們在 Vue 單文件組件(.vue 文件)裏寫樣式時,通常會加上 scoped 屬性:
<template>
<el-button>點我</el-button>
</template>
<style scoped>
.el-button {
background: red;
}
</style>
加了 scoped 後,Vue 會自動給這個組件裏的所有元素加上一個唯一的屬性(比如 data-v-123456),然後把 CSS 選擇器也加上這個屬性,變成:
.el-button[data-v-123456] {
background: red;
}
這樣做的好處是:樣式只作用於當前組件,不會污染全局。、
但問題來了:Element Plus 的 <el-button> 組件內部結構,是在它自己的組件裏定義的。也就是説,你寫的 .el-button 元素,其實是 Element Plus 渲染出來的子組件,它身上沒有你當前組件的 data-v-xxx 屬性!
所以你的樣式根本匹配不到它,自然就失效了。
二、那怎麼辦?
為了解決這個問題,Vue 提供了樣式穿透(style penetration)的語法,讓你能穿透當前組件的作用域,去影響子組件內部的元素。
Vue 社區出現過三種寫法:
下面我們一個個拆解。
1. >>>:曾經的快捷方式,但問題很多
早期 Vue2 時代,很多人用:
<style scoped>
.parent >>> .child {
color: blue;
}
</style>
它的意思是:從 .parent 開始,穿透到所有後代中的 .child。
但問題在於:
- Sass/Less 等預處理器不認
>>>,會報錯。 - 不是標準 CSS 語法。
- Vue3 已經明確不再支持。
所以現在基本可以忘掉它了。
2. ::v-deep:Vue2 到 Vue3 的橋樑
為了兼容預處理器,Vue 引入了 ::v-deep:
<style scoped lang="scss">
.parent ::v-deep(.child) {
color: blue;
}
</style>
或者更常見的寫法:
.parent {
::v-deep(.child) {
color: blue;
}
}
它在 Vue2 和 Vue3 中都能用,算是一個安全的過渡方案。
但注意:在 Vue3 中,官方文檔已經明確建議使用 :deep() 替代它。
3. :deep():Vue3 的標準答案
Vue3 引入了更簡潔、更符合 CSS 規範的偽類函數寫法:
<style scoped>
:deep(.el-button) {
background: red !important;
}
</style>
或者配合父級選擇器:
<style scoped>
.my-wrapper :deep(.el-input__inner) {
border-radius: 10px;
}
</style>
優點:
- 語法清晰,像原生 CSS。
- 支持所有預處理器(Sass/Less/Stylus)。
:deep() 本質上是一個編譯時轉換,Vue 在構建時會把它展開成帶 data-v-xxx 的複雜選擇器,從而實現穿透。
三、怎麼正確修改 Element Plus 的樣式?
舉個真實例子:你想把 Element Plus 的輸入框圓角改成 8px。
錯誤寫法(不生效):
<style scoped>
.el-input__inner {
border-radius: 8px;
}
</style>
正確寫法:
<template>
<div class="my-form">
<el-input v-model="value" />
</div>
</template>
<style scoped>
.my-form :deep(.el-input__inner) {
border-radius: 8px;
}
</style>
為什麼要加 .my-form 這個父級?
避免全局污染!如果直接寫 :deep(.el-input__inner),那麼這個頁面裏所有 Element 輸入框都會被改掉。加上父級限定,就能精準控制範圍。