对组件内使用:deep()修改自身样式和样式穿透的记录

一个简单的组件

js 复制代码
<template>
    <div class="test">
        <div class="title" >
            我是标题t
        </div>
    </div>
</template>

此时正常生效颜色和字体大小

此时会编译为 .test .title[data-v-xxx]

css 复制代码
.test{
    .title{
        font-size: 20px;
        color: red;
    }
}

但是如果加上:deep(),则不会生效,我们知道,此时按理说.title的[data-v-xxx]会被去掉,应该会被编译为

.test[data-v-xxxx] .title

css 复制代码
.test{
    :deep(.title){
        font-size: 20px;
        color: red;
    }
}

但此时在浏览器style调试栏里没有任何对应的.title选择器

css 复制代码
element.style {
}

用户代理样式表

div {
 display: block;
unicode-bidi: isolate;
}

如果去掉外围的.test选择器,则又会重新生效

css 复制代码
    :deep(.title){
        font-size: 20px;
        color: red;
    }

此时在浏览器style调试页则会显示

css 复制代码
   [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编译后的结果显示

js 复制代码
<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结构不同,所以没生效的原因

css 复制代码
.test [data-v-c64d93fd] .title {
  font-size: 20px;
  color: red;
}

而之前的其他情况对应的是

css 复制代码
:deep(.title) {
  font-size: 20px;
  color: red;
}

[data-v-16bfa729] .title {}符合原行为


添加一个祖先元素

js 复制代码
  <div class="tabbar">
    <div class="test">
      <div class="title">测试标题</div>
    </div>
  </div>
  
.tabbar {
    .title {
  }
}

最后的结果是.tabbar [data-v-9ecd31d2] .title 由于祖先元素和title元素之间隔了一个带[data-v-xxx]的父元素,所以依旧能选中title


最后是最正确的写法

css 复制代码
.test {
  .title {
    font-size: 20px;
    color: red;
  }
}

.test .title[data-v-bcdf58ac] {}

符合默认行为


如果是形如这种结构

js 复制代码
<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)

js 复制代码
// 父组件
  <div class="tabbar">
    <div class="test">
      我是父组件
      <Cpn class="son" />
    </div>
  </div>
  
//子组件
    <div class="cpn">
        我是组件外层根元素
        <div class="cpn-inner">
            组件内部第二层
        </div>
    </div>

如果在父组件内直接对子组件的cpn-inner进行选中但不使用:deep()

css 复制代码
.tabbar {
  .test {
    color: greenyellow;
    font-size: 20px;

    .cpn-inner {
      color: red;
      background-color: #666;
    }

  }
}

则最终的编译结果里,此时cpn的[data-v-xxx]是父组件的xxx,无法选中子组件

css 复制代码
.tabbar .test .cpn-inner[data-v-110961b9] {
  color: red;
  background-color: #666;
}

在父组件内添加了:deep()之后

css 复制代码
    :deep(.cpn-inner) {
      color: red;
      background-color: #666;
    }

最终的编译结果里,依旧是子组件的[data-v-xxx]被插入到之前的元素中,此时的[data-v-2eaeac9f]为外部父组件的[data-v],由于这个[data-v]其实也会被添加到子组件的根节点中,所以通过css子代选择器,就能被选中子组件内的模块

css 复制代码
.tabbar .test [data-v-2eaeac9f] .cpn-inner {
  color: red;
  background-color: #666;
}

接下来再看看如果子组件不带scoped的情况

DOM结构则会变成引入第三方组件库的形式:仅有外层根节点会设置父组件的[data-v],内部模块不会添加[data-v]

html 复制代码
    <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]

css 复制代码
.tabbar .test [data-v-5725aaa2] .cpn-inner {
  color: red;
  background-color: #666;
}

就能正确更改子组件模块内的样式了


最后的最后,假如我们的子组件里面还有嵌套,我们在父组件内要选择第三层嵌套的子元素cpn-inner2

html 复制代码
// 子组件
<div class="cpn">
        我是组件外层根元素
        <div class="cpn-inner">
            组件内部第二层
            <div class="cpn-inner2">
                组件内部第三层
            </div>
        </div>
    </div>
css 复制代码
// 父组件内
.tabbar {
  .test {
    color: greenyellow;
    font-size: 20px;

    .cpn-inner {
      color: red;
      background-color: #666;

      :deep(.cpn-inner2) {
        color: purple;
      }
    }
  }
}

输出此时css的编译结果

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结构如下

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()写法里,无法选中任何的子组件内容


现在原因知道了,之后的操作就简单了

css 复制代码
    :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()时的当前组件

知道这一点之后就知道该在哪里添加:deep()了

相关推荐
何中应2 小时前
<el-tree>标签问题
前端·vue.js·elementui
思慕很大很大2 小时前
使用Vue开发,是不是不会出现XSS漏洞了?
vue.js·浏览器
xcs194052 小时前
前端 vue this.$nextTick(() => {
前端·javascript·vue.js
滕青山2 小时前
基于 pdf-lib 的图片转PDF工具核心JS实现
前端·javascript·vue.js
会联营的陆逊2 小时前
Vite + Vue3 构建优化:CDN 外部化方案
前端·vue.js
SuniaWang2 小时前
Vue 3 + Spring Boot 21 全栈 RAG 项目Docker Compose 容器化部署
vue.js·人工智能·spring boot·spring·阿里云·docker·milvus
小林攻城狮2 小时前
el-tabs 页签中表格组件宽度闪动问题解决方案
前端·vue.js
李剑一3 小时前
告别冗余代码!Cesium点位图标模糊、重叠?自适应参数调优攻略,一次封装终身复用!
前端·vue.js·cesium
踩着两条虫3 小时前
🔥 实测对比:VTJ.PRO凭啥让头部企业放弃自研低代码?
前端·vue.js·ai编程