yuxino / blog

🎬 Life's a Movie
17 stars 2 forks source link

Vue scoped css VS css module #78

Closed yuxino closed 3 years ago

yuxino commented 6 years ago

我们可以看见Vue Loader里面有两种处理样式的方案。一种是Scoped CSS还有一种是CSS Modules。下面来做一些优缺点对比。

Scoped CSS的优点

开箱即用。能避免样式冲突(但是不完全)。

Scoped CSS的缺点

覆盖第三方利用了scoped的插件

引用包含scoped的第三方插件时如若需要修改样式则需要全局修改,而且要注意权重问题,迫不得已再使用!important。

冲突

如果样式和全局的样式重名,依然会导致样式冲突。

需要注意的问题

Scoped 样式不能代替 class。考虑到浏览器渲染各种 CSS 选择器的方式,当 p { color: red } 是 scoped 时 (即与特性选择器组合使用时) 会慢很多倍。 如果你使用 class 或者 id 取而代之,比如 .example { color: red },性能影响就会消除。

这句话的意思是可能有人会觉得有了Scoped就可以直接不写class或者id了。直接给标签写样式。虽然可以但是效率会很低,因为用到了特性选择器

举个例子:

在递归组件中小心使用后代选择器! 对选择器 .a .b 中的 CSS 规则来说,如果匹配 .a 的元素包含一个递归子组件,则所有的子组件中的 .b 都将被这个规则匹配。

选择器性能问题

Google 资深web开发工程师 Steve Souders 对 CSS 选择器的执行效率从高到低做了一个排序:

如何减少 CSS 选择器性能损耗? Google 资深web开发工程师 Steve Souders 对 CSS 选择器的执行效率从高到低做了一个排序:

1.id选择器(#myid)
2.类选择器(.myclassname)
3.标签选择器(div,h1,p)
4.相邻选择器(h1+p)
5.子选择器(ul < li)
6.后代选择器(li a)
7.通配符选择器(*)
8.属性选择器(a[rel="external"])
9.伪类选择器(a:hover, li:nth-child)

根据以上「选择器匹配」与「选择器执行效率」原则,我们可以通过避免不恰当的使用,提升 CSS 选择器性能。

CSS Modules 优点

不冲突

名字会有postcss生成转成hash。

性能更好一点

因为名字转成hash了,选择器也没有必要了。

多style

可以在.vue文件写很多个style,但是正常人会这样吗?

<style module="a">
  /* 注入标识符 a */
</style>

<style module="b">
  /* 注入标识符 b */
</style>

可以通过js直接访问样式名

<script>
export default {
  created () {
    console.log(this.$style.red)
    // -> "red_1VyoJ-uZ"
    // 一个基于文件名和类名生成的标识符
  }
}
</script>

Composition

可以组合其他的样式。

.className {
  color: green;
  background: red;
}

.otherClassName {
  composes: className;
  color: yellow;
}

composes可以写很多条。但是必须得写在其他CSS规则之前。

CSS Modules的缺点

并非开箱即用

需要手动配置在webpack加css-loader并传入modules:true

// webpack.config.js
{
  module: {
    rules: [
      // ... 其它规则省略
      {
        test: /\.css$/,
        use: [
          'vue-style-loader',
          {
            loader: 'css-loader',
            options: {
              // 开启 CSS Modules
              modules: true,
              // 自定义生成的类名
              localIdentName: '[local]_[hash:base64:8]'
            }
          }
        ]
      }
    ]
  }
}

然后才可以使用

<style module>
.red {
  color: red;
}
.bold {
  font-weight: bold;
}
</style>

新的写法

由于Scoped CSS在CSS Modules之前就出现了,会有更多的解决方案和项目用到。找资料的时候比较方便,CSS Modules是比较后面出来的,写法也不一样,需要一点点的学习成本。

必须使用:class

其实这个也还好,因为样式名是动态生成的,可以理解。和以前直接写class还是有区别的。最糟糕的可能是混着写,因为不免会用到一些三方的CSS。可能在一个组件里会有部分是:class部分直接写的class

使用css module在keyframes中的问题

使用CSS modules处理动画animation的关键帧keyframes,动画名称必须先写。

animation: ani 1s;能正常编译,而animation: 1s ani;则会编译的不符合预期,所以平时养成良好的css参数书写顺序也很重要。

css module父子组件问题

使用module的父组件会在没有使用module的子组件的所有根类上增加hash改变其类名,可能会造成子组件样式应用不上。

如下是没有开启css module子组件的样式:

<style lang="scss">
  .comp{
    color: palegoldenrod;
    p{
      color: black;
    }
  }
  .t {
    color: teal;
  }
  div {
    color: yellow;
  }
</style>

父组件开启css module后编译结果如下:

.comp_2tR6GNan {
  color: palegoldenrod;
}
.comp_2tR6GNan p {
  color: black;
}
.t_39GmF73s {
  color: teal;
}
div {
  color: yellow;
}

可以看到comp和t类都被修改了类名,如果根样式是标签选择器不会受影响。

所以在使用css module的父组件中使用的子组件也要开启css module。

总结

总的来看CSS Modules的坑会少一点。所以选型上我会比较偏向CSS Modules。

相关资料

glitchboyl commented 6 years ago

老哥 第一句话. ~Spoced CSS~ => Scoped CSS

yuxino commented 6 years ago

@LonelyLiaR ok

Indomite commented 3 years ago

覆盖第三方利用了scoped的插件,一般使用的还是穿透属性吧