Skip to content
On this page

vueSFC 中如何书写 css

在日常的业务开发中,很多都是在 vue 文件中的 style 中使用 scoped 字段来实现 css 样式的。比如本人使用的 vscode 扩展,其默认就是设置成 scoped 格式。

但是,scoped 的原理是属性选择器,直观的结果就是当前组件一些 dom 元素会被加上 data-v-hashId 这样的属性,且生成的 css 的类名,也是类选择器和属性选择器的组合,即 .xxx[data-v-hashId] { color: red },且不谈属性选择器的性能劣势,单就直观而看,这样也是不优雅的。

所以我介绍下另一个与 scoped 同级的参数 module,即使得当前的 css 通过 css modules 解析,且默认会在组件实例上生成一个计算属性 $style,此时再通过 $style.xxx 进行元素类名的双向绑定即可,即 <div :class="$style.xxx"></div>

但是,如果一个组件的 css 类名很多,全部通过 module 去双向绑定,也是个麻烦事,那是不是有什么办法简化?

办法是有的,比如将 $style 中的所有类名,都绑定到当前组件上(与组件内部数据字段不冲突时),此时写法上就简化成了 <div :class="red"></div>。或者可以根据一定规则生成新的类名,比如统一用相同前缀 cls,即 <div :class="cls_red"></div>,可以解决与数据字段命名空间冲突的问题,但是还是存在多次双向绑定的情况。那是否有办法减少双向绑定次数?

有,也是此文档的推荐方式。即,在有作用范围的元素(一般就是根元素)绑定 module 上的一个类名,其内部的类名,通过 css module:global 关键字进行包裹,则不会生成被 css modules 处理后的类名,而是与定义的类名一致,也是全局类名。那么生成全局类名,不是会互相影响吗?会,但如果所有的业务和页面级的 vue 组件,都遵循该规范,理论上就不会相互影响,或影响到的极少。类名的命名冲突这种情况就不谈了。这里会生成至少两层的 css,即 .hashId .red { color: red },应该比类加属性选择器性能更优,可以做个 benchmarnk

所以,推荐的写法如下:

html
<template>
  <!-- * 只需要这里双向绑定一次 -->
  <div :class="$style.root">
    <!-- * 这里仍然使用常规的类名 -->
    <div class="red">red</div>
    <div class="blue">blue</div>
  </div>
</template>
<script>
export default {}
</script>
<style lang="scss" module>
.root {
  :global {
    /* * 在global下,不会hash化,但又进行了scss嵌套,即可生成满足的css了 */
    .red {
      color: red;
    }
    .blue {
      color: blue;
    }
  }
}
</style>

实际效果如下,可以通过 element 面板查看细节

red
blue