Vue样式隔离与样式穿透(deep使用)
1. Vue样式隔离scope
Vue组件之间没有做到样式隔离,Vue中的样式隔离,是通过scoped
属性来实现的。当在<style>
标签上使用scoped
属性时.基本原理概括为以下几个步骤:
- 为当前组件模板的所有DOM节点添加相同的attribute,添加的属性与其他的scope不重复,data属性(形如:data-v-123)来表示他的唯一性。
- 在每句css选择器的末尾(编译后的生成的css语句)加一个当前组件的data属性选择器(如.ipt input[data-v-123])来私有化样式
- 如果组件内部包含有其他组件,只会给其他组件的最外层标签加上当前组件的data属性
例如:
<template>
<div class="example">This is an example</div>
</template>
<style scoped>
.example {
color: blue;
}
</style>
经过Vue的处理,以上代码将被转换为类似下面这样的代码:
<template>
<div class="example" data-v-21e5b78>This is an example</div>
</template>
<style scoped>
.example[data-v-21e5b78] {
color: blue;
}
</style>
注意:虽然通过scoped
可以实现样式的局部化,但这并不意味着它可以阻止外部的全局样式影响当前组件,或者阻止当前组件的样式影响其子组件。
2. 为什么需要样式穿透
2.1 样式穿透的原因用法
因为vue在做样式隔离时,有子组件时只会在最外层的元素添加data属性,而css添加时,只会在最后添加属性选择器。而:deep
样式穿透就是要解决,css选择器添加问题。
例如:比如修改a-input的背景颜色
<template>
<div>
<el-input class="ipt"></el-input>
</div>
</template>
<style scoped lang="scss">
.ipt{
input{
background-color: red;
}
}
</style>
dom:
style样式
由此可见:不生效的原因是因为dom中选择器为ipt[data-v-b8cedf19] input
而style中默认为.ipt input[data-v-b8cedf19]
,所以导致样式不生效。
添加deep:
<template>
<div>
<el-input class="ipt"></el-input>
</div>
</template>
<style scoped lang="scss">
.ipt{
:deep(input){
background-color: red;
}
}
</style>
Vue 提供了样式穿透:deep() 他的作用就是用来改变 属性选择器的位置
2.2 使用ui组件库,样式穿透不生效
场景:在写一个组件时,使用了ant-design-vue组件库的a-modal,此时定义modal进行样式穿透不生效。
原因:
- Modal组件默认挂载在body上
- Modal组件使用getContainer进行挂载,直接使用deep不行,因为不会添加
data-v-xxx
属性
解决方案: 例如:有两个组件Scope中引用Mod组件,Mod组件中引用了a-modal,现在想要在Mod组件中直接修改Modal框的样式。
Scope组件:
<template>
<Mod v-model:visible="visible"></Mod>
<el-button type="primary" @click="visible = true">点击</el-button>
</template>
<script setup lang="ts">
import {ref} from 'vue'
import Mod from "./Mod.vue";
const visible = ref(false)
</script>
Mod组件:
<template>
<a-modal
:visible="props.visible"
wrapClassName="modal__container"
@cancel="handleCloseModal"
>
<template #title>
<span class="text-20px heading-color">拆单</span>
</template>
<div class="temp">
6666
</div>
</a-modal>
</div>
</template>
<script setup lang="ts">
type TProps = {
visible: boolean
}
const props = withDefaults(defineProps<TProps>(), {
visible: false,
})
const emits = defineEmits<{
(e: 'update:visible', value: boolean): void
}>()
const handleCloseModal = () => {
emits('update:visible', false)
}
</script>
<style scoped lang="scss">
.ant-modal-content{
padding: 50px;
}
:deep(.modal__container){
padding: 136px;
}
.temp{
font-size: 30px;
}
.heading-color{
color: red;
}
</style>
未指定挂载前:
dom结构如上:直接进行修改temp和heading-color的样式是生效的,都进行了scope的属性添加。自定义类名,css选择器无法选择到dom节点。
修改Mod组件中a-modal的挂载方式
Mod组件:
<template>
<div id="modals">
<a-modal
:visible="props.visible"
:getContainer="getContainer('modals')"
wrapClassName="modal__container"
@cancel="handleCloseModal"
>
<template #title>
<span class="text-20px heading-color">拆单</span>
</template>
<div class="temp">
6666
</div>
</a-modal>
</div>
</template>
<script setup lang="ts">
type TProps = {
visible: boolean
}
const props = withDefaults(defineProps<TProps>(), {
visible: false,
})
const emits = defineEmits<{
(e: 'update:visible', value: boolean): void
}>()
const getContainer = (idSelector: string) =>
document.getElementById(idSelector)!
const handleCloseModal = () => {
emits('update:visible', false)
}
</script>
<style scoped lang="scss">
#modals{
:deep(.ant-modal-content){
padding: 50px;
}
}
.temp{
font-size: 30px;
}
.heading-color{
color: red;
}
</style>
此时的dom结构
写入
#modals{
:deep(.ant-modal-content){
padding: 50px;
}
}
样式生效:
3. 总结
本次了解了deep穿透的原理,以及为什么穿透设置不生效的原因。希望对大家有帮助。(^o^)/~
参考文章:
转载自:https://juejin.cn/post/7251981828741939261