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

6 阅读4分钟

一个简单的组件

<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>

最后css的编译结果则是与之前相同的结构,[data-v-5725aaa2]为父组件[data-v]

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

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