一个简单的组件
<template>
<div class="test">
<div class="title" >
我是标题t
</div>
</div>
</template>
此时正常生效颜色和字体大小
此时会编译为
.test .title[data-v-xxx]
.test{
.title{
font-size: 20px;
color: red;
}
}
但是如果加上:deep(),则不会生效,我们知道,此时按理说.title的[data-v-xxx]会被去掉,应该会被编译为
.test[data-v-xxxx] .title
.test{
:deep(.title){
font-size: 20px;
color: red;
}
}
但此时在浏览器style调试栏里没有任何对应的.title选择器
element.style {
}
用户代理样式表
div {
display: block;
unicode-bidi: isolate;
}
如果去掉外围的.test选择器,则又会重新生效
:deep(.title){
font-size: 20px;
color: red;
}
此时在浏览器style调试页则会显示
[data-v-xxx] .title {
font-size: 20px;
color: red;
}
表明当前其实选择器被编译为了[data-v-xxxx] .title
查询了AI得知疑似是因为vue内部对带有父元素选择器的 deep()进行了过滤,不参与编译结果,所以最终没有输出到浏览器style调试页上 但是在AI给出的来源文档里并没有看到对应的内容?疑似AI编造资料,特此记录
又重新问了一遍ai,ai又换说法了,把这种现象解释为vue处理css的时候将带有父元素的deep()处理为了.test [data-v-123] .title与原dom结构不同,所以不生效,在让ai给出参考的源文档中...
最后确认了,问题代码在经过vite编译后的结果显示
<template>
<div class="test">
<div class="title">测试标题</div>
</div>
</template>
<style scoped>
.test {
:deep(.title) {
font-size: 20px;
color: red;
}
}
</style>
在最后输出的结果是.test [data-v-c64d93fd] .title,符合与DOM结构不同,所以没生效的原因
.test [data-v-c64d93fd] .title {
font-size: 20px;
color: red;
}
而之前的其他情况对应的是
:deep(.title) {
font-size: 20px;
color: red;
}
[data-v-16bfa729] .title {}符合原行为
添加一个祖先元素
<div class="tabbar">
<div class="test">
<div class="title">测试标题</div>
</div>
</div>
.tabbar {
.title {
}
}
最后的结果是.tabbar [data-v-9ecd31d2] .title 由于祖先元素和title元素之间隔了一个带[data-v-xxx]的父元素,所以依旧能选中title
最后是最正确的写法
.test {
.title {
font-size: 20px;
color: red;
}
}
.test .title[data-v-bcdf58ac] {}
符合默认行为
如果是形如这种结构
<template>
<div class="tabbar">
<div class="test">
<div class="title">测试标题</div>
</div>
</div>
</template>
<style scoped>
.tabbar {
.test {
:deep(.title) {
font-size: 20px;
color: red;
}
}
}
</style>
编译后结果为
.tabbar .test [data-v-81722b88] .title
由此终于能得到结论,如果闲的没事干,在自己的组件里使用了:deep()来样式穿透,vue会将原本的.title[data-v-bcdf58ac]转换成.[data-v-bcdf58ac] .title,如果.title前面有任何其他类,则会转换成.xxxx [data-v-bcdf58ac] .title,所以就会出现有时候能选中,有时候选不中
至于正常的使用,形如(子组件带scoped)
// 父组件
<div class="tabbar">
<div class="test">
我是父组件
<Cpn class="son" />
</div>
</div>
//子组件
<div class="cpn">
我是组件外层根元素
<div class="cpn-inner">
组件内部第二层
</div>
</div>
如果在父组件内直接对子组件的cpn-inner进行选中但不使用:deep()
.tabbar {
.test {
color: greenyellow;
font-size: 20px;
.cpn-inner {
color: red;
background-color: #666;
}
}
}
则最终的编译结果里,此时cpn的[data-v-xxx]是父组件的xxx,无法选中子组件
.tabbar .test .cpn-inner[data-v-110961b9] {
color: red;
background-color: #666;
}
在父组件内添加了:deep()之后
:deep(.cpn-inner) {
color: red;
background-color: #666;
}
最终的编译结果里,依旧是子组件的[data-v-xxx]被插入到之前的元素中,此时的[data-v-2eaeac9f]为外部父组件的[data-v],由于这个[data-v]其实也会被添加到子组件的根节点中,所以通过css子代选择器,就能被选中子组件内的模块
.tabbar .test [data-v-2eaeac9f] .cpn-inner {
color: red;
background-color: #666;
}
接下来再看看如果子组件不带scoped的情况
DOM结构则会变成引入第三方组件库的形式:仅有外层根节点会设置父组件的[data-v],内部模块不会添加[data-v]
<div data-v-5725aaa2 class="tabbar">
<div data-v-5725aaa2 class="test">
我是父组件
<div data-v-5725aaa2 class="cpn son">
我是组件外层根元素
<div class="cpn-inner">组件内部第二层</div>
</div>
</div>
</div>
.tabbar {
.test {
color: greenyellow;
font-size: 20px;
:deep(.cpn-inner) {
color: red;
background-color: #666;
}
}
}
最后css的编译结果则是与之前相同的结构,[data-v-5725aaa2]为父组件[data-v]
.tabbar .test [data-v-5725aaa2] .cpn-inner {
color: red;
background-color: #666;
}
就能正确更改子组件模块内的样式了
最后的最后,假如我们的子组件里面还有嵌套,我们在父组件内要选择第三层嵌套的子元素cpn-inner2
// 子组件
<div class="cpn">
我是组件外层根元素
<div class="cpn-inner">
组件内部第二层
<div class="cpn-inner2">
组件内部第三层
</div>
</div>
</div>
// 父组件内
.tabbar {
.test {
color: greenyellow;
font-size: 20px;
.cpn-inner {
color: red;
background-color: #666;
:deep(.cpn-inner2) {
color: purple;
}
}
}
}
输出此时css的编译结果
.tabbar .test[data-v-df3292a6] {
color: #adff2f;
font-size: 20px;
}
.tabbar .test .cpn-inner[data-v-df3292a6] {
color: red;
background-color: #666;
}
.tabbar .test .cpn-inner [data-v-df3292a6] .cpn-inner2 {
color: purple;
}
输出的html结构如下
<div data-v-df3292a6 class="tabbar">
<div data-v-df3292a6 class="test">
我是父组件
<div data-v-df3292a6 class="cpn son">
我是组件外层根元素
<div class="cpn-inner">
组件内部第二层
<div class="cpn-inner2">组件内部第三层</div>
</div>
</div>
</div>
</div>
从中我们发现了重要的问题,[data-v-df3292a6]始终都是父组件的[data-v]
因此在选择.cpn-inner2的时候,inner2和inner之间并没有任何的[data-v]属性,所以仍然不会选中
对于上一层的.cpn-inner[data-v-df3292a6]来说,在DOM结构里cpn-inner并没有带任何的[data-v]属性,所以inner也不会被选中
这就导致了,在我们的:deep()写法里,无法选中任何的子组件内容
现在原因知道了,之后的操作就简单了
:deep(.cpn-inner) {
color: red;
background-color: #666;
.cpn-inner2 {
color: purple;
}
}
选中子组件根节点下的第一个子元素,编译结果为
.test [data-v-df3292a6] .inner
.test [data-v-df3292a6] .cpn-inner .cpn-inner2
这时候两个子组件内的选中都能正常生效了
最后再做个总结:deep(.abc)的作用是:
如果当前为scoped环境,.abc有当前组件的[data-v]则将原本的.abc[data-v-xxx]转换为[data-v-xxx] .abc,
如果.abc有父元素,则形式为.parent [data-v-xxx] .abc
如果.abc是子组件内的嵌套子元素,那就会在前面添加[data-v-父组件data-id] .abc,
如果当前不是scoped环境,.abc没有[data-v],则会被转换为[data-v-父组件data-id] .abc,如果有父元素,则形式为.parent [data-v-父组件data-id] .abc
:deep()获取的[data-v]编号始终都是写:deep()时当前组件的[data-v-xxx]
知道这一点之后就明白该在哪里添加:deep()了
不过按照官方文档里的描述,在vue原始设计里:deep()是被设计成一个伪类
所以如果是按照伪类的正确写法,下面的代码编译后会变成
.test {
color: greenyellow;
font-size: 20px;
.son { // 子组件根节点
font-size: 14px;
&:deep(.cpn-inner2) {
color: purple;
}
}
}
.test .son[data-v-35d75636] .cpn-inner2 {}
此时父组件的[data-v]就正确的添加到了子组件的根节点上,之后一路写后面的子选择器即可,当然核心原理仍旧是:deep()对内部选择器的[data-v]的位置处理,搞懂了这点所有第三方组件的样式更改都能够通通拿下了