點燈坊

失くすものさえない今が強くなるチャンスよ

當 Component 遇見 Functional CSS

Sam Xiao's Avatar 2020-08-08

很多人對於 Functional CSS 直覺印象就是 HTML 會帶一堆 CSS Class,事實上當 Component 觀念出現後,由於 Prop 也能用在 CSS,因此 CSS 就有了新的用法。

Version

macOS Catalina 10.15.6
WebStorm 2020.2
Vue 2.6.11
Tailwind CSS 1.6.0

Root Component

component000

有兩個 button,各為 dark purple 與 light purple。

App.vue

<template>
  <div class="flex">
    <my-button color="darkPurple">Submit</my-button>
    <my-button color="lightPurple">Submit</my-button>
  </div>
</template>

<script>
import MyButton from '@/components/MyButton'

export default {
  name: 'App',
  components: {
    MyButton
  }
}
</script>

第 3 行

<my-button color="darkPurple">Submit</my-button>
<my-button color="lightPurple">Submit</my-button>

雖然看起來是兩個不同顏色 button,但實際上是同一個 MyButton component,只是對 color prop 指定了 darkPurplelightPurple 不同值。

MyButton Component

MyButton.vue

<template>
  <button type="button" :class="colorClass" class="inline-flex items-center px-4 py-2 border border-transparent text-sm leading-5 font-medium rounded-md focus:outline-none focus:shadow-outline-indigo transition ease-in-out duration-150">
    <slot></slot>
  </button>
</template>

<script>
let colorClass = function() {
  return {
    darkPurple: 'text-white bg-indigo-600 hover:bg-indigo-500 focus:border-indigo-700 active:bg-indigo-700',
    lightPurple: 'text-indigo-700 bg-indigo-100 hover:bg-indigo-50 focus:border-indigo-300 active:bg-indigo-200'
  }[this.color]
}

export default {
  name: "MyButton",
  props: [
    'color'
  ],
  computed: {
    colorClass
  }
}
</script>

第 2 行

<button type="button" :class="colorClass" class="inline-flex items-center px-4 py-2 border border-transparent text-sm leading-5 font-medium rounded-md focus:outline-none focus:shadow-outline-indigo transition ease-in-out duration-150">
  <slot></slot>
</button>

MyButton 骨子還是使用 HTML 的 <button>,將相同的 utility 留在 class attribute 內,不同的部分綁定到 colorClass computed。

第 8 行

let colorClass = function() {
  return {
    darkPurple: 'text-white bg-indigo-600 hover:bg-indigo-500 focus:border-indigo-700 active:bg-indigo-700',
    lightPurple: 'text-indigo-700 bg-indigo-100 hover:bg-indigo-50 focus:border-indigo-300 active:bg-indigo-200'
  }[this.color]
}

colorClass computed 的實作很精彩,將要傳入的 darkPurplelightPurple 放在 Object 的 key,將不同的 utility 放在其 value,直接使用 [this.color] 從 prop 比對出。

Conclusion

  • 可將 Tailwind CSS 的 utility 視為 function,藉由 Function Composition 組合出想要的 style,並將相同的部分保留在 class attribute,不同的部分則以 prop 傳入,最後使用 computed 選擇出不同的 utility 綁定
  • 以前 CSS 與 JavaScript 可謂井水不犯河水,CSS 負責 style,JavaScript 負責 logic,但透過 component 與 utility 整合之後,可藉由 component 重新組合 utility,解決 CSS 重複的老問題

Reference

Adam Wathan, CSS Utility Classes and “Separation of Concerns”