Vue:scoped与module的使用与利弊

7次阅读

共计 3963 个字符,预计需要花费 10 分钟才能阅读完成。

一个 web 应用是离不开 html、css 与 js,其中 css 充斥的整个 web 项目中。css 它有一个特定,它是全局的。这样的特性导致的结果是,一旦你在不同的地方定义了相同的 css 命名,那么它们的样式就会相互覆盖,最终导致的 style 错乱,从而影响整个网页布局。
我相信对于每一个前端开发者都遇到过这种 css 样式覆盖的情况,值得庆幸的是,这些问题前辈都已经给出了解决方案。
在 Vue 中我们通过 Scoped 与 Module 来解决。下面我会分别对 scoped 与 module 解决方案进行说明,最后在分析它们的利弊与选择。如果你还未使用过或者说对它们之间的利弊与选择存在疑问的,相信这篇文章能够帮你解惑。
Scoped
假设我们有如下一段代码:
index.vue
<template>
<div class=”content”>
<div class=”title-wrap”> 我是红色的 </div>
<green-title></green-title>
</div>
</template>

<style lang=”scss”>
.content {
.title-wrap {
font-size: 20px;
color: red;
}
}
</style>
GreenTitle.vue
<template>
<div class=”content”>
<div class=”title-wrap”> 我是绿色的 </div>
</div>
</template>

<style lang=”scss”>
.content {
.title-wrap {
font-size: 20px;
color: green;
}
}
</style>

最终这屏幕上展示的是两行红色的文字,这就是父组件与子组件都定义了 title-wrap 的样式,导致子组件的样式被父组件所覆盖。
遇到这种情况,可以在 style 标签中添加 scoped 属性
<style lang=”scss” scoped>
.content {
.title-wrap {
font-size: 20px;
color: green;
}
}
</style>

scoped 作用的阻止上层的 css 样式传递到下层,限制当前 css 作用域,使其只对当前组件生效。
知道了它的作用,下面我们在开深入看下它的实现。

前面的是没有添加 scoped 的源码,后面是添加了 scoped 的源码。我们进行一一对比,发现前面的两个 div 标签都使用了 title-wrap 样式,自然导致样式覆盖;而后面的两个 div 标签,第一个增加了 data-v-67e6b31f 的前缀,这就是父组的 style 中增加 scoped 的效果,区别与第二个 div 中的 title-wrap 样式。
scoped 的实现是借助了 PostCSS 实现的,一旦增加了 scoped,他会将之前覆盖的样式转换成下面的样式
<style lang=”scss”>
.content[data-v-67e6b31f] {
.title-wrap[data-v-67e6b31f] {
font-size: 20px;
color: green;
}
}
</style>
通过这种转换方式,间接的改变了原有的 css 命名。防止上层组件样式覆盖下层组件样式。
特性
细心的读者可能会发现上面的后一张源码图中第二个 div 的 content 中也有 data-v-67e6b31f,可能会疑问,第二个 content 不是子组件中的 css 吗?子组件中未添加 scoped,为什么还会添加 data-v-67e6b31f 前缀?
这是 scoped 的一个特性,使用 scoped 后,父组件的样式将不会渗透到子组件中。不过一个子组件的根节点会同时受其父组件有作用域的 CSS 和子组件有作用域的 CSS 的影响。这样设计是为了让父组件可以从布局的角度出发,调整其子组件根元素的样式。
所以如果我们将子组件做如下修改
<template>
<!– <div class=”content”> –>
<div class=”title-wrap”> 我是绿色的 </div>
<!– </div> –>
</template>

由于父组件 scoped 特性,所以会影响到子组件的 title-wrap,也会添加 data-v-67e6b31f 前缀
那么又有个疑问,增加了 scoped 是否就一定不能传递的下层组件呢?毕竟我们可能有需要个别样式传递到下层的需求。别急,接着看,这个也能很方便的解决。
深度作用
如果你希望 scoped 中的某个样式能够作用的更深,影响到子组件,你可以使用 >>> 操作符
<style scoped>
.content >>> .title-wrap {
font-size: 20px;
color: red;
}
</style>
注意看我将 style 中的 lang=”scss” 去掉了,因为加了预处理器后无法正确解析 >>>,这种情况可以使用 /deep/ 代替,本质是 >>> 的别名
<style lang=”scss” scoped>
.content {
/deep/ {
.title-wrap {
font-size: 20px;
color: red;
}
}
}
</style>
将会编译成
.content[data-v-67e6b31f] .title-wrap {
font-size: 20px;
color: red;
}

通过 v-html 创建的 DOM 内容不受作用域内的样式影响,但是你仍然可以通过深度作用选择器来为他们设置样式
Module
针对上面的覆盖问题,还可以通过设置 module 来解决
<template>
<div :class=”$style.content”>
<div :class=”$style[‘title-wrap’]”> 我是红色的 </div>
<green-title></green-title>
</div>
</template>

<style lang=”scss” module>
.content {
.title-wrap {
font-size: 20px;
color: red;
}
}
</style>

module 的用法也很简单,只要在 style 中增加 module 属性即可。不同之处是它在布局中的引用,都需要添加前缀 $style。因为通过 module 作用的 style 都被保存到 $style 对象中。我可以通过 console 查看它的具体引用名。
mounted() {
console.log(this.$style)
console.log(this.$style[‘title-wrap’])
}

通过观察,发现引用名有一定的规律。都是已 index 开头,后面再接着 style 中定义的命名,最后再接个后缀。这里的 index 是父组件的文件名 index.vue。所以通过 module 作用的 style 将会重新命名为:文件名_原 style 名_不定后缀。
这么命名又有什么好处呢?我们再来看下展示的效果

当我们在浏览的控制台查看 Elements 时,优点显而易见。相对于 scoped 的方式,module 的方式能够一眼知道该元素时属于哪个文件组件中。在大型项目中能够帮助我们迅速定位到要查找的组件。
除了上述的快速定位,由于 module 会将所有的 style 都归入 $style 中,所以我们可以很灵活的将任意的父组件样式传递到任意深层的子组件中。例如,将父组件中的 title-wrap 通过 props 传递到子组件中
<template>
<div :class=”$style.content”>
<div :class=”$style[‘title-wrap’]”> 我是红色的 </div>
<green-title :styleTitle=”$style[‘title-wrap’]”></green-title>
</div>
</template>
<template>
<div class=”content”>
<div :class=”styleTitle”> 我是绿色的 </div>
</div>
</template>
<script>

export default {
props: {
styleTitle: String,
},
}
</script>

module 还有一个特性非常不错,它可以导出定义的变量,将变量归入 $style 中,例如:
<template>
<div :class=”$style.content”>
<div :class=”$style[‘title-wrap’]”> 我是红色的 </div>
<green-title :styleTitle=”$style[‘title-wrap’]”></green-title>
<div>{{$style.titleColor}}</div>
</div>
</template>

<style lang=”scss” module>
$title-color: red;
:export {
titleColor: $title-color
}
.content {
.title-wrap {
font-size: 20px;
color: $title-color;
}
}
</style>

更多 module 相关操作可以点击查看

总结
scoped 与 module 都非常简单、易用,那么又该如何选择呢?
通过上面的使用对比,发现 scoped 不需要额外的知识,只要在 style 中定义 scoped 属性即可,使用非常简便。但它的局限性是适用于中小项目中。因为 scoped 作用的 style 对于我们来说不直观,对于快速查找定位,module 更加合适,同时 module 对于 style 向下传递的控制权也非常灵活;额外的还有变量导出等便捷功能。
所以如果你是小项目中且低成本的使用,scoped 更加适合;而对大项目 module 更加合适,虽然有一点学习成本,但对于用更好的控制权、可观性与定位速度来说也就不值一提。
公众号
感觉不错的可以来一波关注,扫描下方二维码,关注公众号: 怪谈时间,及时获取最新知识技巧与互联网新动态。

正文完
 0