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

Vue CSS Modules

CSS Modules:局部做用域 & 模塊化

CSS Modules 爲每個局部類賦予全局惟一的類名,這樣組件樣式間就不會相互影響了。如:javascript

/* button.css */
.button {
  font-size: 16px;
}
.mini {
  font-size: 12px;
}
複製代碼

它會被轉換爲相似這樣:css

/* button.css */
.button__button--d8fj3 {
  font-size: 16px;
}
.button__mini--f90jc {
  font-size: 12px;
}
複製代碼

當導入一個 CSS 模塊文件時,它會將局部類名到全局類名的映射對象提供給咱們。就像這樣:html

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 的按鈕組件:vue

<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 組件是一個不錯的選擇。但也存在如下幾點不足:java

  • 你必須在 data 中傳入 styles
  • 你必須使用 styles.localClassName 導入全局類名
  • 若是有其餘全局類名,你必須將它們放在一塊兒
  • 若是要和組件的屬性值綁定,就算局部類名和屬性名同樣,也要顯式指定

對於上面的按鈕組件,使用 vue-css-modules 後:git

<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>
複製代碼

如今:github

  • 你沒必要在 data 中傳入 styles,但得在 mixins 中傳入 styles 🌝
  • 你能夠跟 styles.localClassName 說拜拜了
  • 將局部類名放在 styleName 屬性,全局類名放在 class 屬性,規整了許多
  • 局部類名綁定組件同名屬性,只需在其前面加上 : 修飾符

修飾符

@button

<button styleName="@button">按鈕</button>
複製代碼

這等同於:api

<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

相關文章
相關標籤/搜索