阅读 8768

如何在 Vue 中优雅地使用 CSS Modules?

Vue CSS Modules

CSS Modules:局部作用域 & 模块化

CSS Modules 为每一个局部类赋予全局唯一的类名,这样组件样式间就不会相互影响了。如:

/* button.css */
.button {
  font-size: 16px;
}
.mini {
  font-size: 12px;
}
复制代码

它会被转换为类似这样:

/* button.css */
.button__button--d8fj3 {
  font-size: 16px;
}
.button__mini--f90jc {
  font-size: 12px;
}
复制代码

当导入一个 CSS 模块文件时,它会将局部类名到全局类名的映射对象提供给我们。就像这样:

import styles from './button.css'
// styles = {
//   button: 'button__button--d8fj3',
//   mini: 'button__mini--f90jc'
// }

element.innerHTML = '<button class="' + styles.button + ' ' + styles.mini + '" />'
复制代码

vue-css-modules:简化类名映射

下面是一个使用了 CSS Modules 的按钮组件:

<template>
  <button :class="{
    'global-button-class-name': true,
    [styles.button]: true,
    [styles.mini]: mini
  }">点我</button>
</template>

<script>
  import styles from './button.css'

  export default {
    props: { mini: Boolean },
    data: () => ({ styles })
  }
</script>
复制代码

的确,CSS Modules 对于 Vue 组件是一个不错的选择。但也存在以下几点不足:

  • 你必须在 data 中传入 styles
  • 你必须使用 styles.localClassName 导入全局类名
  • 如果有其他全局类名,你必须将它们放在一起
  • 如果要和组件的属性值绑定,就算局部类名和属性名一样,也要显式指定

对于上面的按钮组件,使用 vue-css-modules 后:

<template>
  <button
    class="global-button-class-name"
    styleName="button :mini">
    点我
  </button>
</template>

<script>
  import CSSModules from 'vue-css-modules'
  import styles from './button.css'

  export default {
    mixins: [CSSModules(styles)],
    props: { mini: Boolean }
  }
</script>
复制代码

现在:

  • 你不必在 data 中传入 styles,但得在 mixins 中传入 styles 🌝
  • 你可以跟 styles.localClassName 说拜拜了
  • 将局部类名放在 styleName 属性,全局类名放在 class 属性,规整了许多
  • 局部类名绑定组件同名属性,只需在其前面加上 : 修饰符

修饰符

@button

<button styleName="@button">按钮</button>
复制代码

这等同于:

<button styleName="button" data-component-button="true">按钮</button>
复制代码

这让你能在外部重置组件的样式:

.form [data-component-button] {
  font-size: 20px;
}
复制代码

$type

<button styleName="$type">按钮</button>
复制代码

这等同于:

<button :styleName="type">按钮</button>
复制代码

:mini

<button styleName=":mini">按钮</button>
复制代码

这等同于:

<button :styleName="mini ? 'mini' : ''">按钮</button>
复制代码

disabled=isDisabled

<button styleName="disabled=isDisabled">按钮</button>
复制代码

这等同于:

<button :styleName="isDisabled ? 'disabled' : ''">按钮</button>
复制代码

使用方法

在 Vue 模板中使用

引入模板外部的 CSS 模块

<template>
  <button
    class="global-button-class-name"
    styleName="button :mini">
    点我
  </button>
</template>

<script>
  import CSSModules from 'vue-css-modules'
  import styles from './button.css'

  export default {
    mixins: [CSSModules(styles)],
    props: { mini: Boolean }
  }
</script>
复制代码

使用模板内部的 CSS 模块

<template>
  <button
    class="global-button-class-name"
    styleName="button :mini">
    点我
  </button>
</template>

<script>
  import CSSModules from 'vue-css-modules'

  export default {
    mixins: [CSSModules()],
    props: { mini: Boolean }
  }
</script>

<style module>
  .button {
    font-size: 16px;
  }
  .mini {
    font-size: 12px;
  }
</style>
复制代码

在 Vue JSX 中使用

import CSSModules from 'vue-css-modules'
import styles from './button.css'

export default {
  mixins: [CSSModules(styles)],
  props: { mini: Boolean },
  render() {
    return (
      <button styleName="@button :mini">点我</button>
    )
  }
}
复制代码

在 Vue 渲染函数中使用

import CSSModules from 'vue-css-modules'
import styles from './button.css'

export default {
  mixins: [CSSModules(styles)],
  props: { mini: Boolean },
  render(h) {
    return h('button', {
      styleName: '@button :mini'
    }, '点我')
  }
}
复制代码

实现原理

vue-css-modules 注册了 beforeCreate 钩子,在钩子中劫持了组件的渲染函数。对于传给渲染函数的参数,将会解析其 datadata.attrs 中的 styleName 属性生成全局类名字符串,并将它附着在 data.staticClass 值的后面。

Github:vue-css-modules

文章分类
前端