一个web应用是离不开html、css与js,其中css充斥的整个web项目中。css它有一个特定,它是全局的。这样的特性导致的结果是,一旦你在不同的地方定义了相同的css命名,那么它们的样式就会相互覆盖,最终导致的style错乱,从而影响整个网页布局。
我相信对于每一个前端开发者都遇到过这种css样式覆盖的情况,值得庆幸的是,这些问题前辈都已经给出了解决方案。
在Vue中我们通过Scoped与Module来解决。下面我会分别对scoped与module解决方案进行说明,最后在分析它们的利弊与选择。如果你还未使用过或者说对它们之间的利弊与选择存在疑问的,相信这篇文章能够帮你解惑。
Scoped
假设我们有如下一段代码:
index.vue
GreenTitle.vue
最终这屏幕上展示的是两行红色的文字,这就是父组件与子组件都定义了title-wrap的样式,导致子组件的样式被父组件所覆盖。
遇到这种情况,可以在style标签中添加scoped属性
scoped作用的阻止上层的css样式传递到下层,限制当前css作用域,使其只对当前组件生效。
知道了它的作用,下面我们在开深入看下它的实现。
前面的是没有添加scoped的源码,后面是添加了scoped的源码。我们进行一一对比,发现前面的两个div标签都使用了title-wrap样式,自然导致样式覆盖;而后面的两个div标签,第一个增加了data-v-67e6b31f的前缀,这就是父组的style中增加scoped的效果,区别与第二个div中的title-wrap样式。
scoped的实现是借助了PostCSS实现的,一旦增加了scoped,他会将之前覆盖的样式转换成下面的样式
通过这种转换方式,间接的改变了原有的css命名。防止上层组件样式覆盖下层组件样式。
特性
细心的读者可能会发现上面的后一张源码图中第二个div的content中也有data-v-67e6b31f,可能会疑问,第二个content不是子组件中的css吗?子组件中未添加scoped,为什么还会添加data-v-67e6b31f前缀?
这是scoped的一个特性,使用 scoped 后,父组件的样式将不会渗透到子组件中。不过一个子组件的根节点会同时受其父组件有作用域的 CSS 和子组件有作用域的 CSS 的影响。这样设计是为了让父组件可以从布局的角度出发,调整其子组件根元素的样式。
所以如果我们将子组件做如下修改
由于父组件scoped特性,所以会影响到子组件的title-wrap,也会添加data-v-67e6b31f前缀
那么又有个疑问,增加了scoped是否就一定不能传递的下层组件呢?毕竟我们可能有需要个别样式传递到下层的需求。别急,接着看,这个也能很方便的解决。
深度作用
如果你希望scoped中的某个样式能够作用的更深,影响到子组件,你可以使用操作符
注意看我将style中的lang="scss"去掉了,因为加了预处理器后无法正确解析,这种情况可以使用/deep/代替,本质是的别名
将会编译成
通过 v-html 创建的 DOM 内容不受作用域内的样式影响,但是你仍然可以通过深度作用选择器来为他们设置样式
Module
针对上面的覆盖问题,还可以通过设置module来解决
module的用法也很简单,只要在style中增加属性即可。不同之处是它在布局中的引用,都需要添加前缀。因为通过module作用的style都被保存到对象中。我可以通过console查看它的具体引用名。
通过观察,发现引用名有一定的规律。都是已index开头,后面再接着style中定义的命名,最后再接个后缀。这里的index是父组件的文件名index.vue。所以通过module作用的style将会重新命名为:文件名原style名不定后缀。
这么命名又有什么好处呢?我们再来看下展示的效果
当我们在浏览的控制台查看Elements时,优点显而易见。相对于scoped的方式,module的方式能够一眼知道该元素时属于哪个文件组件中。在大型项目中能够帮助我们迅速定位到要查找的组件。
除了上述的快速定位,由于module会将所有的style都归入中,所以我们可以很灵活的将任意的父组件样式传递到任意深层的子组件中。例如,将父组件中的通过props传递到子组件中
module还有一个特性非常不错,它可以导出定义的变量,将变量归入中,例如:
领取 专属20元代金券
Get大咖技术交流圈