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

蕩麥發表於2019-03-03

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

相關文章