Vue中scoped及/deep/解析

2,110 阅读2分钟

今天,看到小伙伴在开发Vue项目,看到了他使用/deep/来修改第三方组件的样式,给我的第一感觉是不是和:global类似,能够避免选择器被hash(哈西),但其实不然。下面,我将详细介绍使用这两个关键字有什么作用。

首先,我创建了一个App.vue单文件组件,在其中使用了ant-design-vue的Slider组件。

<template>
  <div id="app"> 
    <span>123</span>
    <div id="parent">
      <span>234</span>
      <a-slider id="test" :default-value="30" :disabled="disabled" />
    </div>
  </div>
</template>

<script>

import Vue from 'vue';
import 'ant-design-vue/dist/antd.css';
import { Slider } from 'ant-design-vue';
Vue.component(Slider.name, Slider);

export default {
  name: 'App',
  data() {

    return {
      disabled: true
    }
  }
}
</script>

<style lang="less" scoped>
#app {
  & > span {
    color: red;
  }
  
  #parent {
    & > span {
      color: green;
    }
  }
}
</style>

App.vue效果如下:

看下渲染出来的dom以及编译之后的css样式什么样的吧。首先,可以发现渲染出来的dom在template下的html标签都加上了data-v-xxxx属性,而第三方组件只有根节点才会加上data-v-xxx属性;其次,可以看到css被编译成了最后的选择器才会加上data-v-xxx属性。

如果我们想要修改Slider组件的样式,有两种情况,分别为修改根节点和非根节点。

  1. 根节点

根节点.ant-slider的高度为12px,此时,如果需要修改它的高度为20px,则直接在style下写这个选择器就可以修改了。

<style lang="less" scoped>
#app {

  & > span {
    color: red;
  }
  
  #parent {
    .ant-slider {
       height: 20px;
    }
    & > span {
      color: green;
    }
  }
}

</style>

  1. 非根节点

非根节点.ant-slider-rail的background-color: #f5f5f5,如果需要修改它为red,可能会照葫芦画瓢,直接在style下写这个选择器修改,会不会起作用呢?

<style lang="less" scoped>
#app {

  & > span {
    color: red;
  }

  #parent {
    & > span {
      color: green;
    }

    .ant-slider-rail {
      background-color: yellow;
    }
  }
}
</style>

实际上,这并不能起作用,.ant-slider-rail的背景颜色仍然没有发生变化。

我们可以看一下less编译成css的结果,可以看到在.ant-slider-rail选择器的后面加一个data-v-xxx的属性选择器,而.ant-slider-rail并没有该属性,所以样式不会起作用。

为了解决该问题,引入了/deep/,俗称样式穿透,用法如下。

<style lang="less" scoped>

#app {

  & > span {
    color: red;
  }
  
  #parent {
  
    & /deep/ .ant-slider-rail {
      background-color: red;
    }

    & > span {
      color: green;
    }
  }
}
</style>

进度条变红了,看下编译后的css,可以加上/deep/并没有在.ant-slider-rail后面添加data-v-xxx属性,而是在#parent后添加了data-v-xxx属性,此时的选择器规则可以命中.ant-slider-rail元素,背景颜色被修改为红色。

最后,我也尝试:global的写法,看下样式能否起作用。

<style lang="less" scoped>

#app {

  & > span {
    color: red;
  }

  #parent {

    :global .ant-slider-rail {
      background-color: yellow;
    }
    
    & > span {
      color: green;
    }
  }
}
</style>

进度条的背景颜色没有变成绿色,很遗憾,:global失效了。看看编译后的css结果,你就知道为什么了。:global并没有被正确解析,直接出现在编译后的结果。

有一种常犯的错误,我们可能会在自定义组件中给根节点定义相同的类名,例如:container。但这样,虽然子组件的样式正确,但却可以发现该子组件应用和父组件相同样式,只是被在子组件定义相同样式覆盖了而已,这同样是由上面介绍的scoped导致的,所以不推荐大家在组件中给根节点定义相同的类名。