我们在每个vue项目或多或少都有会设置组件css样式或者覆盖第三方(element)组件样式的需求。但是有些朋友可能会遇见在覆盖第三方组件样式失效或者设置的样式污染了其他组件的样式。最近我在做一个可视化项目中又遇见了这个问题,于是决定找出问题的关键,通过方法彻底解决这个BUG;

在解决这个问题之前,我们首先需要了解一下vue组件的css作用域及scoped属性,以及improt属性。

1.vue-loader官网文档:https://vue-loader.vuejs.org/zh/guide/css-modules.html

2.@import导入规则:https://developer.mozilla.org/zh-CN/docs/Web/CSS/@import

3.参考文章:https://www.cnblogs.com/goloving/p/9119460.html

https://blog.csdn.net/qq_39043923/article/details/88687046

在经过网上查阅文档和一些文章之后,我将内容进行了一个总结,具体内容如下:

css作用域(scoped属性解析)

vue中引入了scoped属性。scoped的设计目的就是私有化组件,让当前组件的样式不会影响到其他组件的样式,它在组件渲染的时候使用了data–v-hash的方式,将本组件里的dom,css设置了它独有的标识,这样就避免了不同组件之间的样式污染。

<div data-v-732c7eee class='wind'>
    <div data-v-732c7eee>测试内容</div>
</div>

.wind[data-v-732c7eee] {
	color: #fff;
}

也就是说:我们在一个vue组件的style中使用scoped属性后,会对本组件的dom和css做私有化处理;

  • 给html的DOM节点上添加一个属于本组件的data属性。
  • 给每句css选择器的末尾添加一个本组件的data属性选择器,来私有化样式。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    注意:但是对于当前组件调用其他组件,data属性标识只会添加到第一层HTML。比如引入一个element的table组件
    在这里插入图片描述
    我们可以明显看见,渲染后只有table组件最外层Html添加到了data属性标识。

我们可以根据vue-loader官方文档里了解到:

使用 scoped 后,父组件的样式将不会渗透到子组件中。不过一个子组件的根节点会同时受其父组件的 scoped CSS 和子组件的 scoped CSS 的影响。这样设计是为了让父组件可以从布局的角度出发,调整其子组件根元素的样式。

所以我们使用scoped属性后,就无法覆盖引入的子组件或者第三方组件样式。同时我们还需要注意的是,使用scoped属性后,会增加样式的权重。

解决方法

第一种方法:如果想要scoped里的样式能够作用于子组件内(覆盖第三方组件样式),可以使用深度选择器,对指定的样式使用操作符穿透scoped:

<div class='content'>
    <el-input v-model='value'></el-input>
</div>

<style lang='less'  scoped>
.content /deep/ .el-input__inner {
	background-color: red;
}
</style>

编译后:
.content[data-v-f3f3eg9] .el-input__inner { /* ... */ }

可以使用的操作符有:
.a >>> .b { color: #fff }
.a /deep/ .b { color: #fff }  ---------推荐
.a ::v-deep .b { color: #fff }

第二种方法:我们可以在一个组件里同时使用拥有scoped和没有scoped样式,将本组件里普通的样式写在scoped里面,需要覆盖的第三方组件样式写在没有scoped的

<style lang='less' scoped>
/* 本地样式 */ 作用域范围:仅限本组件
</style>

<style lang='less'> 
/* 全局样式 */  作用域范围:全局,没有了scoped就有污染全局的影响,所以我们可以给每个组件加一个根ID 将需要覆盖的第三方组件样式写在根ID下面;
</style>

// test.vue 案例
<template>
	<div id='testBox'> // 给每个组件添加根ID
        <div class='content'>
    		<el-input v-model='value'></el-input>
		</div>
    </div>
</template>

<style lang='less'  scoped>
    .content {
        width: 100%;
    }
</style>

<style lang='less'>
    // 将所有需要自定义设置的第三方组件样式都写在根ID选择器里面
    #testBox {
        .el-input__inner {
            background-color: red;
        }
    }
</style>

第三种方法:外部引入一个css文件,同时注意也需要在覆盖样式的前面添加一个父选择器(根ID选择器最好)。

// test.css
#testBox .el-input__inner {
    background-color: red;
}
// test.vue
<template>
	<div id='testBox'>
        <div class='content'>
    		<el-input v-model='value'></el-input>
		</div>
    </div>
</template>

<style lang='less' scoped> 
 	@import url("../assets/css/test.css");
</style>

注意:@import引入规则,我们以前很多vue项目都是用的@import引入的外部css文件,最近我在测试外部引入文件的时候发现@import引入的css样式也是全局作用域,也会存在样式污染。

<style lang='less' scoped> 
 	@import url("../assets/css/test.css");
</style>
<style lang='less'> 
 	@import url("../assets/css/test.css");
</style>
// 都是全局作用域

无论是在scoped里面引入还是在没有scoped里面引入,@import都是全局作用域,都有污染其他组件的风险。

而使用src引入的css文件,加了scoped就是局部作用域,不加就是全局作用域;

<style scoped src="../assets/css/test.css"><style>

所以: 想写在外部的css文件通过@import引入的,为了不影响其他组件,就必须在样式前面加上根ID的选择器。

想通过src引入的,就在style里面加上scoped属性。

最后总结:

vue设计scoped属性的目的就是私有化组件,所以我们还是推荐使用scoped属性。只是为了避免样式污染,我们可以采用灵活的解决办法。记录这次测试增加记忆,也希望能帮助一些朋友解决问题,如果有疑问的朋友可以评论留言。

Logo

前往低代码交流专区

更多推荐