Vue:scoped与module的使用与利弊

clipboard.png

一个web应用是离不开html、css与js,其中css充斥的整个web项目中。css它有一个特定,它是全局的。这样的特性致使的结果是,一旦你在不一样的地方定义了相同的css命名,那么它们的样式就会相互覆盖,最终致使的style错乱,从而影响整个网页布局。css

我相信对于每个前端开发者都遇到过这种css样式覆盖的状况,值得庆幸的是,这些问题前辈都已经给出了解决方案。html

在Vue中咱们经过Scoped与Module来解决。下面我会分别对scoped与module解决方案进行说明,最后在分析它们的利弊与选择。若是你还未使用过或者说对它们之间的利弊与选择存在疑问的,相信这篇文章可以帮你解惑。前端

Scoped

假设咱们有以下一段代码:vue

index.vuegit

<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.vuegithub

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

clipboard.png

最终这屏幕上展现的是两行红色的文字,这就是父组件与子组件都定义了title-wrap的样式,致使子组件的样式被父组件所覆盖。web

遇到这种状况,能够在style标签中添加scoped属性布局

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

clipboard.png

scoped做用的阻止上层的css样式传递到下层,限制当前css做用域,使其只对当前组件生效。学习

知道了它的做用,下面咱们在开深刻看下它的实现。this

clipboard.png

clipboard.png

前面的是没有添加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: red;
  }
}
</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>

clipboard.png

clipboard.png

因为父组件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;
}

clipboard.png

经过 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>

clipboard.png

module的用法也很简单,只要在style中增长module属性便可。不一样之处是它在布局中的引用,都须要添加前缀$style。由于经过module做用的style都被保存到$style对象中。我能够经过console查看它的具体引用名。

mounted() {
  console.log(this.$style)
  console.log(this.$style['title-wrap'])
}

clipboard.png

经过观察,发现引用名有必定的规律。都是已index开头,后面再接着style中定义的命名,最后再接个后缀。这里的index是父组件的文件名index.vue。因此经过module做用的style将会从新命名为:文件名_原style名_不定后缀。

这么命名又有什么好处呢?咱们再来看下展现的效果

clipboard.png

当咱们在浏览的控制台查看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>

clipboard.png

clipboard.png

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>

clipboard.png

更多module相关操做能够 点击查看

总结

scoped与module都很是简单、易用,那么又该如何选择呢?

经过上面的使用对比,发现scoped不须要额外的知识,只要在style中定义scoped属性便可,使用很是简便。但它的局限性是适用于中小项目中。由于scoped做用的style对于咱们来讲不直观,对于快速查找定位,module更加合适,同时module对于style向下传递的控制权也很是灵活;额外的还有变量导出等便捷功能。

因此若是你是小项目中且低成本的使用,scoped更加适合;而对大项目module更加合适,虽然有一点学习成本,但对于用更好的控制权、可观性与定位速度来讲也就不值一提。

公众号

感受不错的能够来一波关注,扫描下方二维码,关注公众号:怪谈时间,及时获取最新知识技巧与互联网新动态。

clipboard.png

相关文章
相关标签/搜索