likes
comments
collection
share

Vue样式隔离与样式穿透(deep使用)

作者站长头像
站长
· 阅读数 11

1. Vue样式隔离scope

Vue组件之间没有做到样式隔离,Vue中的样式隔离,是通过scoped属性来实现的。当在<style>标签上使用scoped属性时.基本原理概括为以下几个步骤:

  1. 为当前组件模板的所有DOM节点添加相同的attribute,添加的属性与其他的scope不重复,data属性(形如:data-v-123)来表示他的唯一性。
  2. 在每句css选择器的末尾(编译后的生成的css语句)加一个当前组件的data属性选择器(如.ipt input[data-v-123])来私有化样式
  3. 如果组件内部包含有其他组件,只会给其他组件的最外层标签加上当前组件的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:

Vue样式隔离与样式穿透(deep使用)

style样式

Vue样式隔离与样式穿透(deep使用)

由此可见:不生效的原因是因为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() 他的作用就是用来改变 属性选择器的位置

Vue样式隔离与样式穿透(deep使用)

Vue样式隔离与样式穿透(deep使用)

2.2 使用ui组件库,样式穿透不生效

场景:在写一个组件时,使用了ant-design-vue组件库的a-modal,此时定义modal进行样式穿透不生效。

原因:

  1. Modal组件默认挂载在body上
  2. 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>

未指定挂载前: Vue样式隔离与样式穿透(deep使用) dom结构如上:直接进行修改temp和heading-color的样式是生效的,都进行了scope的属性添加。自定义类名,css选择器无法选择到dom节点。

Vue样式隔离与样式穿透(deep使用)

修改Mod组件中a-modal的挂载方式

Vue样式隔离与样式穿透(deep使用)

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结构 Vue样式隔离与样式穿透(deep使用)

写入

#modals{
	:deep(.ant-modal-content){
		padding: 50px;
	}
}

Vue样式隔离与样式穿透(deep使用)

样式生效:

Vue样式隔离与样式穿透(deep使用)

3. 总结

本次了解了deep穿透的原理,以及为什么穿透设置不生效的原因。希望对大家有帮助。(^o^)/~


参考文章:

小满zs