问题描述
遇到一个需求,改变vue项目中某个element组件的样式,但是正常的在style中写css样式不起作用
例如:我要改变下面输入框的的背景色以及字体颜色
<el-dialog :title="title" :visible.sync="open" width="700px">
<el-form ref="form" :model="form" label-width="140px">
<el-form-item label="员工姓名" prop="name">
<el-input v-model="form.name" disabled></el-input>
</el-form-item>
</el-form>
</el-dialog>
解决问题
第一步
那么按照常理来说css代码应该会是这样的:
.el-dialog .el-form .el-input{
background-color: white;
color: rgb(8, 234, 49);
}
第二步
首先明确,在vue项目中的element组件中直接看到的组件名是不能直接用来写css代码来改变其样式的。因为组件名并不是它的最低一层的层级,也就是说直接用组件名写css样式,就等同于是没有选中元素,因此css样式是不会起作用的。正确的做法是,通过浏览器中的控制台,选中指定元素后,去看其最低一层的层级是什么,然后通过最低一层的层级选中目标元素之后写css代码。
但是事实上该样式不起作用,原因就是没有获取到最低一层的层级,获取其最低的层级如下图所示:
当层级确定之后,css的代码应该是这样的:
.el-dialog .el-form .el-input .el-input__inner{
background-color: white;
color: rgb(8, 234, 49);
}
第三步
但是,当层级已经正确且css代码正确之后,依旧没有起作用,这是因为父组件的style样式无法渗透到子组件中起作用,这时就需要考虑一下是不是通过 deep 穿透一下,让样式可以渗透到其内部组件,添加穿透之后的css代码应该如下:
::v-deep .el-dialog .el-form .el-input .el-input__inner {
background-color: white;
color: rgb(8, 234, 49);
}
到此为止,问题已经解决了。
第四步
但是,有时候样式仍然会不起作用,这时候就需要考虑是不是选择器权重的问题,是不是权重没有达到要求,为了确保设置的样式权重最高,可以给代码再添加 ! important 属性(但是一般不建议使用该属性,因为它会破坏整个页面的权重结构)
::v-deep .el-dialog .el-form .el-input .el-input__inner {
background-color: white !important;
color: rgb(8, 234, 49) !important;
}
到此为止,算是已经是一个完美体了,正确的选中了元素、css代码书写正确、添加了渗透、添加了最高级别的权重;基本上可以解决绝大部分问题。
拓展
查看element组件最低层级
查看element组件的最低一层的层级,上面已经讲过方法,在控制台查看即可。
关于vue中deep的用法
vue中的scoped属性
我们都知道,Vue组件的style标签里,有scoped
属性,这是为了使其内部所写的样式只会作用于当前组件,而不会污染到其他组件的样式,避免了样式的全局污染;不仅仅是可以避免不同的.vue文件之间是隔离的,而且在一个.vue的内部,父组件和子组件之间样式也是隔离的。极大的避免了样式污染的发生。
因此对于vue项目来说,组件中style的scoped
一定要写,且必不可少。
scoped存在导致的问题
但是这样就会出现一个问题:使用了scoped属性之后,父组件的style样式将不会渗透到子组件中。
而对于上文中的问题而言,el-dialog是一个弹窗组件,el-form是一个表单组件,el-input又是一个输入框组件,也就是说,我们刚才所写的看似是一层一层的css层级,实际上却是一层层的组件,由外到内。
如果直接写成:.el-dialog .el-form .el-input .el-input__inner
,那么css样式仅仅只停留在el-dialog这一个父组件中,而不会作用到el-form乃至其更内层的组件中去,就会导致css样式不起作用。
此时一定大脑中有一个疑问,既然作用不到子组件中,那我直接只写最低一层的子组件这一层不就行了,就比如这样:.el-input__inner
;哈哈,如果在当前的.vue文件中有且仅有这一个输入框、又或者是该页面的所有输入框都要修改样式,那么这样写是没有问题的,因为这样只写最低一级的层级,会选中该.vue文件中的所有输入框。
但是如果该.vue文件中代码很多,而你想选中且改变样式的输入框,仅仅只是众多输入框中的一个或者是几个呢?那么阁下又该如何应对呢?
我们还是要回到最初的问题上来,通过父组件一层一层的去筛选出我们想要改变样式的输入框,然后改变样式最好不过了,那么如何解决样式无法渗透到子级组件的问题呢?
deep
如果想对设置了scoped的子组件里的元素进行控制可以使用 ’>>>’ 或者 ’deep’,但是一些预处理程序,例如sass不能解析>>>属性,因此最终权衡之后都用deep是最好的选择了。
deep的写法,目前已知的有:/deep/
、::deep
,其中兼容性最好的就是::deep
,所以建议使用第二种。
使用方法:在想要影响子组件的那个元素的选择器的前边加上::v-deep
经过测试,deep是可以穿透多层的,只要组件写在deep之后,那么就可以穿透到最低一层,就比如:::deep .el-dialog .el-form .el-input .el-input__inner
,样式不是穿透到el-dialog就不能再穿透了,而是可以一直穿透到最低一级:el-input__inner
deep精准定位
如果子组件里有很多个li,或者li嵌套li,那只在外边说深入里边的li不准确。怎么精确的定位到子组件里的某个元素呢?
可以给子组件传一个prop,是classPrefix。在子组件里判断,如果外边给传了这个classPrefix,那某个元素就拥有了一个class,叫做"xxx-item"
,xxx是classPrefix变量的值。
然后在父组件的css里,就可以deep 这个加了前缀的选择器,就可以精准找到这个元素了。
给元素绑定class
给一个元素添加class可以使用&&
,也可以使用对象形式。
<li :class="type==='-' && 'selected'" @click="selectType('-')">支出</li>
如果前边的表达式为True,这个元素就拥有了后边的class。如果我这时再给它添加一个class绑定,会出错,不能同时有两个:class
,所以升级使用对象
<li :class="{selected:type==='-',[classPrefix+'-item']:classPrefix}" @click="selectType('-')">支出</li>
绑定的class是一个对象,里边是key-value。表示如果value表达式为真,我就拥有了key这个class。因为对象可以有很多个键值对,所以使用对象可以同时动态绑定多个class。
我要绑定的第二个class,是xxx-item
,xxx是父组件要给我传的一个prop。如果给传了,即如果classPrefix
这个外部属性给传了,我就拥有了以它为前缀的一个class。可是这个classPrefix是一个变量,在这里使用${}插值不合法。有ES6的新语法:如果一个key里边有变量,就用[]
把这个key包起来,里边用字符串相加的方式表达。
总结
在子组件的特定元素上绑定了classPrefix的类之后,在父组件的css里就可以deep 这个新加的class找到这个元素了。
<template>
<Layout>
<Type :type.sync="yyy" class-prefix="xxx"/>
</Layout>
</template>
<style lang="scss" scoped>
::v-deep .xxx-item{
border:1px solid black;
}
::v-deep ul{
border:1px solid red;
}
</style>
使用classPrefix在以下场景非常好用:一个父组件使用了多个一样的子组件,但是又需要不同的样式,就可以给不同的子组件传不同的classPrefix,那么里边的具体某个元素在不同的使用下就有独特的class。这样每个组件的样式都不互相影响。