背景

 项目使用Vue和ElementUi,想要覆盖ElementUi默认的输入框样式,新增自定义CSS样式,根据el-input编译后生成的class修改,但是样式并不生效。
后来发现修改

1
<style type="text/css" scoped></style>

标签里的scoped删除就生效了。

原因

scope可以做到样式私有化,及当前样式只作用于当前模块,不会影响全局。

 通过查看DOM结构发现:vue通过在DOM结构以及css样式上加唯一不重复的标记,以保证唯一,达到样式私有化模块化的目的。具体的渲染结果是怎样的,通过一个例子来说明。

举个栗子

 一个公共组件button,为了样式模块化,给其加上scoped属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//button.vue
<template>
<div class="button-warp">
<button class="button">text</button>
</div>
</template>
...
<style scoped>
.button-warp{
display:inline-block;
}
.button{
padding: 5px 10px;
font-size: 12px;
border-radus: 2px;
}
</style>

 浏览器渲染的button组件,其html和css结构如下:

1
2
3
<div data-v-2311c06a class="button-warp">
<button data-v-2311c06a class="button">text</button>
</div>
1
2
3
4
5
6
7
8
.button-warp[data-v-2311c06a]{
display:inline-block;
}
.button[data-v-2311c06a]{
padding: 5px 10px;
font-size: 12px;
border-radus: 2px;
}

 从上面的案例可以看出,添加了scoped属性的组件,为了达到组件样式模块化,做了两个处理:

  • HTMLDOM节点加一个不重复data属性(形如:data-v-2311c06a)来表示他的唯一性
  • 在每句 css选择器的末尾(编译后的生成的css语句)加一个当前组件的data属性选择器(如[data-v-2311c06a])来私有化样式

 但这势必影响到css的优先级,scoped虽然达到了组件样式模块化,但是会导致每个样式的权重加重。理论上我们要修改这个样式的话,需要更高的权重去修改,增加了复杂度。

外部引用

 栗子说明了单个组件渲染,当遇到组件相互调用,如果组件内部包含有其他组件,只会给其他组件的最外层标签加上当前组件的data属性。

解决方案

 vue-loader的深度作用选择器。

 在vue.js中,<style scoped>的作用域在本html页面,<style>样式的作用域则是全局(即所有html页面),由于我把其中一个html页面的<style scoped>scoped去掉,这个页面的样式就变成了全局样式,当其它页面和这些全局样式重class名时,样式会被覆盖(vue默认全局样式覆盖局部样式),这样就导致当前页面定义样式影响到全局样式。

 如何在<style scoped>局部样式中覆盖全局样式:父组件cssName+ /deep/+第三方库需要更改的cssName

1
2
3
比如修改classel-input__inner的元素,
它的父classinput_percent
则在style中写入input_percent /deep/ .el-input__inner {}即可