1. scoped 与 样式穿透 :deep()
1.1 scoped 原理
vue 中的 scoped 通过在 DOM 结构以及 css 样式上加唯一的标记 data-v-hash 的方式,达到样式私有化的目的
总结一下 scoped 三条渲染规则
1. 给 HTML 的 DOM 节点加一个不重复的 data 属性(形如:data-v-123)来表示他的唯一性
2. 在每句 css 选择器的末尾(编译后生成的 css 语句)加一个当前组件的 data 属性选择器(如:[data-v-123])来私有化样式
3. 如果组件内部包含有其它组件,只会给其它组件的最外层标签加上当前组件的 data 属性
例如:
在 App.vue 里使用了自定义的 Deep.vue 组件,在 Deep 里面引用了 Element-UI 的 el-input 组件
可以看到:只有最外层 div(class="el-input") 上有 [data-v-hash] 属性,而往下的标签是没有的
那么当选中 .el-input 类设置样式(scoped 情况下)时
浏览器会解析成如下 CSS 代码,类选择+属性选择,保证全局的唯一性
页面效果如下,虽然是设置了红色背景,但被子元素覆盖了,不是我们想要的效果
1.2 样式穿透
假如我们直接给 el-input 组件的最本质的 input 标签(class="el-input__inner")设置背景色,会发现实现不了
因为在 CSS 中它最终被解析成这样
这个属性 [data-v-75b2bfc6]是它父亲的,但根据 scoped 的第二条渲染规则,它被放到了选择器的最后面,然而 .el-input__inner 类所属标签身上没有这个属性,所以选不中原本的那条标签
为了解决这个问题,就需要用到样式穿透了
样式穿透的作用就是避免 浏览器自动生成的唯一标记属性 总是放到选择器的最后面,而导致设置 css 样式失效。多用于修改 组件库中 组件深层次标签 的样式
具体使用
/* 用 :deep() 包裹标签的类名,然后设置样式,就可以实现这个属性穿透到 [data-v-hash] 的后面 */
:deep(.el-input__inner){
background-color: red;
}
解析结果
页面效果,成功实现
2. 插槽选择器 :slotted
在父组件(App.vue)中引入子组件(Slotted.vue)
<template>
<slotted>
<h1 class="a">插槽选择器</h1>
</slotted>
</template>
<script setup lang="ts">
import Slotted from './components/Slotted.vue'
</script>
然后在子组件中写插槽中的样式时,会发现不起作用
<template>
<slot></slot>
</template>
<style scoped>
.a {
color: red;
}
</style>
这时候就需要另一个选择器(:slotted())来表明这个选择器(.a)是插槽标签的
<template>
<slot></slot>
</template>
<script setup lang="ts">
</script>
<style scoped>
:slotted(.a){
color: red;
}
/* .a {
color: red;
} */
</style>
这时候就起作用了
3. 全局选择器 :global
与上面两个选择器的用法类似,:global(css选择器) 这样写就可以在任何地方的 <style scoped> 写全局的样式
4. 动态 CSS:v-bind(响应式数据)
可以在 css 中应用 v-bind 绑定响应式的数据,这样就可以在 js 中控制 css
字符串写法
<template>
<h3 class="title">动态 CSS</h3>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const titleColor = ref('red')
</script>
<style scoped>
.title {
color: v-bind(titleColor);
}
</style>
对象写法
<template>
<h3 class="title">动态 CSS</h3>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const titleStyle= ref({
color: 'red'
})
</script>
<style scoped>
.title {
/* 此处一定要写 '' ,不然会报错 */
color: v-bind('titleStyle.color');
}
</style>
5. CSS module
<template>
<!-- 字符串写法 和 数组写法,前缀默认都为 $style -->
<h2 :id="$style.title" :class="[$style.bacc, $style.border]" >CSS module</h2>
</template>
<script setup lang="ts">
</script>
<style module>
#title {
color: red
}
.bacc {
background-color: aqua;
}
.border {
border: 2px solid orange;
}
</style>
可以给 module 加上 key,表示这个 module 的名称
<template>
<!-- 自定义了一个 module 名称,为 mod -->
<h2 :id="mod.title" :class="[mod.bacc, mod.border]" >CSS module</h2>
</template>
<script setup lang="ts">
</script>
<style module="mod">
#title {
color: red
}
.bacc {
background-color: aqua;
}
.border {
border: 2px solid orange;
}
</style>
效果是一样的
同时,在 ts 中提供了一个 hook useCssModule() 来获取 css 模块
<script setup lang="ts">
import { useCssModule } from 'vue';
const mod = useCssModule('mod')
console.log(mod)
</script>
打印结果如下:
一般是在 tsx 中使用这种模块化的方式比较多