VUE 如何覆盖element组件样式

      最近在用element UI开发一个toB系统时,发现设计稿和UI库有不小的出入,由于不是内部系统,所以这块的还原度没办法“得过且过”。我整理了一些覆盖UI库样式的“手段”

为什么UI库(这里用的是element UI)的组件不好直接覆盖?

         我们通常的vue工程都是用vue-cli自动生成出来的,不知道大家有没有发现一个细节——生成的*.vue文件上会默认带上“scoped”,如下图:

                                     1583646480_85_w518_h178.png

UI库不好覆盖的问题也基本从这里开始了。首先看“scoped”是什么?首先“scoped”并不是vue的专利,(“scoped”属性是HTML5的新特性,如果使用该属性,则样式仅仅应用到style元素的父元素及其子元素。)说人话就是vue用了scoped属性,导致当前*.vue文件里的style仅仅作用于当前组件的元素,而对部分element UI的组件无效(一些简单的组件,例如el-button这种简单替换的还是可以覆盖的)。

“scoped”在工程中是如何工作的?

        我们可以用自己的工程运行起来看一下。看看生成的页面是什么样的。

        1583648037_99_w718_h122.png1583648057_21_w1024_h246.png

        可以看到,在vue中引入了scoped这个概念,scoped的设计思想就是让当前组件的样式不会修改到其它地方的样式,使用了data-v-hash的方式来使css有了它对应模块的标识,这样写css的时候不需要加太多额外的选择器,方便很多。

  但是要注意scoped的作用域,因为权重的问题,如果是在子组件使用了scoped,那么在父组件中是不能直接修改子组件的样式的,需要在父组件中使用vue的深度作用选择器。

       问题来啦,我们在自己组件上用scoped初衷是好的。还拿上面的例子来说,我们data-v-a2a7b732是我们自己组件的模块的标识,这里element UI对“简单组件”并没有用data-v-hash管理,我们再举个“复杂组件”的例子,比如带浮层的例如el-select,我们想把【全部数据的按钮边框去掉】

1583649496_22_w558_h136.png

1583649419_4_w282_h274.png     1583649468_16_w1608_h314.png

                                                                  1583649541_27_w718_h504.png       

这样写出来发现在浏览器的选择器里并没有生效。对于“el-input__inner”生的只有库本身的css样式,我们看似“合理”的css继承关系为什么没有生效呢?我们来看一下我们自己的代码到底生成了什么:

首先父级长这样:1583650255_5_w628_h206.pngdata-type有了一个属性选择器确保唯一

工程生成的长这样: 1583650430_51_w1040_h550.png发现问题了吗?

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

也就是说scope的操作是这样的:我们的组件作为父级组件,调用其他组件(element UI)的场景下,scope仅仅作用于我们的当前组件,我们的组件每一级dom上多了一个data-v-hash,生成的css结尾加上了属性"[data-v-hash]"(注意是每个css规则的结尾),这样做的策略是保证css命中的叶子节点是在scope规则下的。那么我们在父级嵌套element ui时“el-input__inner”作为叶子节点即一条css规则的末尾被加上了后缀"[data-v-hash]",但是实际渲染DOM上element UI组件并不会加上属性"[data-v-hash]"。因此后缀"[data-v-hash]"的css无法匹配属性"[data-v-hash]"的DOM元素。也就不会生效。

为什么有的时候能覆盖?

刚才简单提到过,比较简单的elementUI组件能搞定,我们看看为什么。这里有一个按钮,我要附加一些样式:

1583654805_71_w372_h138.png   1583654944_33_w1642_h94.png

1583654969_68_w650_h224.png1583655012_95_w666_h546.png

注意这里我就是正常写了,给button加上一个自己的class:add-account,但是生效了。

        原因在于,elememt-ui中有一些组件,其实是单层级组件,比如:<el-button>替换成了<button class="el-button">而且会拷贝所有的class,因此element替换后仍是当前组件里的scoped控制,而上面提到的情况是组件内部深层元素不受scoped影响的情况。所以scoped场景下,仍可以控制其他组件的最外层dom,深层次规则就会出现前面的情况。而element-ui的“简单组件”基本是单层结构,也就是当前层仍能添加"[data-v-hash]"。

下面我们来看一下几种解决方案:

1、去掉scoped

刚才提到css不能覆盖的原因是属性"[data-v-hash]"导致的,那么最简单的方法就是去掉“scoped”,但是一旦这样做,当前组件中的css就可能污染组件外的空间,vue工程本来就比较庞大复杂,一个页面很可能会加载很多的组件,这些组件页有可能多人维护,难免名字相同,除非你的css有比较好的“命名空间管理”就像element-ui一样。我个人建议还是不要轻易去掉。

2、多个<style></style>

一个vue文件可以写多个<style>标签,我们可以把大部分代码写在<style scoped>里,少数需要覆盖子组件的写在普通<style>中。但是这样只是减小了污染,并没有解决

3、/deep/ 或者 >>> 深度作用选择器

还用最开始的el-select举例,我添加了/deep/如下图:

1583657025_83_w714_h504.png     1583657047_38_w474_h210.png生效了!没有border了

1583657166_11_w2320_h304.png

某些预处理器(如Sass)可能无法>>>正确解析。在这些情况下,您可以使用/deep/组合器 - 它是别名>>>并且工作完全相同。这种方法,我比较推荐,页很好用,但是并不是万能的。

4、css import 

有一些element-ui组件会产生脱离当前从属结构的DOM元素,比如el-dialog会在body中插入一段html,这个dom就不符合当前组件的从属关系了,并不是当前组件的子元素。第一种方法是css import,这种元素仍可以产生一些特征来减少污染,element-ui对这类组件提供了一个css的命名权,即,你可以对子组件的某个结构单独命名。例如el-dialog和el-table都有这样的属性:

1583657687_39_w932_h204.png1583657714_50_w1068_h102.png

这样在body中插入的html就有了一个和组件名相关的class。我们可以在组件路径下封一个单独的css处理(不推荐写到common里,不好维护)。

5、style-function

这也是我发现element独特支持的方法,还用刚才的el-table举例。

1583657949_1_w926_h210.png 我们可以传入一个函数,return你要的样式。

1583658187_32_w1224_h70.png

1583658279_92_w692_h244.png 1583658312_71_w956_h334.png

这种方法相当于是向特定dom上加上style。完全不污染全局,但是依赖ui库自身提供接口。但是可以根据具体参数灵活计算。

猜你喜欢

转载自www.cnblogs.com/webARM/p/12443518.html