iT邦幫忙

2021 iThome 鐵人賽

DAY 21
0
Modern Web

排版神器 Tailwind CSS~和兔兔一起快速上手漂亮的元件開發!系列 第 22

Day 21:「爸爸說,家裡要重新裝潢了」- 關於樣式的屬性綁定講解

Day21-Banner

兔女鵝:
「爸爸,兔兔昨天講的好難」
「有一大堆東西要記起來,要學不動了 QQ」

兔跋:
「一定是家裡的裝潢太壓抑,要換!」

兔女鵝:
「不是,爸爸...應該跟裝潢沒...」

兔跋:
「要換!」

兔女鵝:
「摁...要換。」 (苦笑

兔跋:
「要換! 既然你也同意了」
「那我就去跟你媽說一聲」

兔女鵝:
「可惡,被我爸算計了嗎...」
「裝潢哪有這麼輕易說換就換的啦...!」

有啦,還真的有!
而且還可以選擇性的換哦!
妳爸肯定是要用 Tailwind 來重新粉刷!

讓我們看看兔跋要怎麼做吧~
 

carrotPoint 屬性綁定

昨天有介紹到屬性綁定的部分,
就是透過 v-bind 可以簡單的實現。

我們再來看一次屬性綁定的用法:

<template>
  <div :id="id"> # {{id}}</div>
</template>

<script>
export default {
  data() {
    return {
      id: "test"
    }
  }
}
</script>

那之前也說過,幾乎所有的屬性都可以綁定,所以要綁定 styleclass動態改變樣式也是可行的!

就讓我們先從 style 綁定看起吧!
 

carrotPoint style 綁定

要綁定 style,必須要注意一下綁定的變數形式。

最基本,你可以直接給他字串,例如:

<template>
  <div :style="'color:red'">兔兔教</div>
</template>

但這樣其實跟你直接寫並沒有兩樣。

你也可以把它整理到 data( ) 中:

<template>
  <div :style="mystyle">兔兔教</div>
</template>

<script>
export default {
  data() {
    return {
      mystyle: "color:red"
    }
  }
}
</script>

所以若要在加上個藍色背景的話:

<template>
  <div :style="mystyle">兔兔教</div>
</template>

<script>
export default {
  data() {
    return {
      mystyle: "color:red;background-color:blue"
    }
  }
}
</script>

如果覺得這串太長太礙眼了,也可以用陣列包起來。

<template>
  <div :style="mystyle">兔兔教</div>
</template>

<script>
export default {
  data() {
    return {
      mystyle: [
        "color:red",
        "background-color:blue",
      ].join(';')
    }
  }
}
</script>

需要 .join(';') 是因為 inline style 每個屬性皆是用分號 ( ; ) 隔開。

不過其實上述的方法都不好,
因為它還是字串,不夠靈活。

如果要在裡面使用變數非常的麻煩,
所以最好的方式應該是使用物件:

<template>
  <div :style="mystyle">兔兔教</div>
</template>

<script>
export default {
  data() {
    return {
      mystyle: {
        "color": 'red',
        "background-color": 'blue',
      }
    }
  }
}
</script>

這樣我們就可以透過改變物件屬性的方式來改變樣式了!
比如按下按鈕之後,文字會變成黃色:

<template>
  <div :style="mystyle">兔兔教</div>
  <button @click="mystyle.['color']='yellow'">黃字</button>
</template>

<script>
export default {
  data() {
    return {
      mystyle: {
        "color": 'red',
        "background-color": 'blue',
      }
    }
  }
}
</script>

不知道你有沒有注意到按鈕上的寫法,
用陣列 key 值的方式太累了,
我們想要直接存取屬性的話,
我們必須把屬性以小駝峰命名方式呈現。

舉例,這樣就可以了:

<template>
  <div :style="mystyle">兔兔教</div>
  <button @click="mystyle.color='yellow'">黃字</button>
</template>

<script>
export default {
  data() {
    return {
      mystyle: {
        color: 'red',
        backgroundColor: 'blue',
      }
    }
  }
}
</script>

善用這個方法,我們就可以做到切換主題。

<template>
  <div :style="getStyle">兔兔教</div>
  <button @click="theme='red'">紅色主題</button>
  <button @click="theme='blue'">藍色主題</button>
  <button @click="theme='green'">綠色主題</button>
</template>

<script>
export default {
  data() {
    return {
      theme: "red",
      mystyle: {}
    }
  },
  computed: {
    getStyle(){
      if(this.theme==='red'){
        this.mystyle.color = 'red'
        this.mystyle.backgroundColor = 'pink'
      }
      else if(this.theme==='blue') {
        this.mystyle.color = 'blue'
        this.mystyle.backgroundColor = 'lightblue'
      }
      else {
        this.mystyle.color = 'green'
        this.mystyle.backgroundColor = 'lightgreen'
      }
      
      return this.mystyle
    }
  }
}
</script>

這份切換主題的範例也有實作出來版本的,放在文章尾部。

那 style 綁定屬性的方法和要注意的點就差不多這樣,有了這靈活綁定屬性的功能,就能簡單做到像是讀取進度條那種用數值變化位置或寬度的效果。

還有個小補充。

一般來說 html 不接受重複屬性,
比如說不能一個元素出現兩個 style 之類的。

但是因為綁定是會把運算完的結果加上去,所以可以做到重覆屬性的部分。而至於為何要重複屬性?

因為可能有些不會變動的樣式你不希望一起寫到變數中時,就可以這麼做!

舉例:

<template>
  <div style="line-height:2.5rem" :style="mystyle">兔兔教</div>
  <button @click="mystyle.color='yellow'">黃字</button>
</template>

<script>
export default {
  data() {
    return {
      mystyle: {
        color: 'red',
        backgroundColor: 'blue',
      }
    }
  }
}
</script>

接著趕快我們進入 class 的部分吧!
 

carrotPoint class 綁定

終於進入到重頭戲了啦!

既然是要使用 Tailwind,
那 class 的綁定絕對是必須要知道的!

畢竟 Tailwind 的語法有機會變很長,
還有為了要可以動態切換,
才能做出更多樣化的效果!

跟 style 的綁定方式差不多,
不過通常為了保持設計與視覺一致,
我們不會把他拉出來到變數之中存放。

拿之前的 Box 舉例:

<template>
  <div 
    :class="[
      [
        'w-20 h-20 bg-gray-500 rounded-md',
        'focus:ring-4 group-hover:text-gray-600',
        'font-bold text-3xl text-white',
        'flex justify-center items-center',
        'cursor-pointer outline-none'
      ],
      (color==='red') && 'bg-red-500 hover:bg-red-400 ring-red-300',
      (color==='blue') && 'bg-blue-500 hover:bg-blue-400 ring-blue-300',
      (color==='purple') && 'bg-purple-500 hover:bg-purple-400 ring-purple-300'
    ]"
    :tabindex="number"
  >
    {{ number }}
  </div>
</template>

<script>
export default {
  name: "Box",
  props: ["number", "color"]
}
</script>

上面這樣是有經過整理的,不然會超~長一串。

而且這樣整理的好處就是,
還可以把要吃變數的樣式分離出來,

而 class 不需要 join(';')
因為 vue 預設會把他們用空白字元串接在一起。

上面得範例可能太複雜了,
我們換一個範例來解釋。

然後順便用 vue 官方上寫的用物件的方式。

以前面的 style 切換主題為例,用 class 寫而且是 Tailwind 的話,複雜度可以降低很多

<template>
  <div
    :class="{
      'leading-10':true,
      'text-red-600 bg-red-300': theme==='red',
      'text-blue-600 bg-blue-300': theme==='blue',
      'text-green-600 bg-green-300': theme==='green',
    }"
  >
    兔兔教
  </div>
  <button @click="theme='red'" class="p-1 m-1 bg-gray-200 rounded">
    紅色主題
  </button>
  <button @click="theme='blue'" class="p-1 m-1 bg-gray-200 rounded">
    藍色主題
  </button>
  <button @click="theme='green'" class="p-1 m-1 bg-gray-200 rounded">
    綠色主題
  </button>
</template>

<script>
export default {
  data() {
    return {
      theme: "red",
    }
  },
}
</script>

有發現嗎,下面整個 computed 都不用寫了,簡單很多。

我們把最上面那塊抽出來講解一下:

<template>
  <div 
    :class="{
      'leading-10':true,
      'text-red-600 bg-red-300': theme==='red',
      'text-blue-600 bg-blue-300': theme==='blue',
      'text-green-600 bg-green-300': theme==='green',
    }"
  >
    兔兔教
  </div>

有發現什麼玄機嗎?

仔細看應該會發現都是以 {'classNames': boolean} 的方式呈現,也就是說 Vue 在幫元素加上這些 class 時,只會加上 vaule 是 true 的 class,如果是 false 則會直接被省略掉。

但這寫法並不優。 為什麼?

不優的原因就在於第一個的樣式 leading-10,它是一個並不會變動的 class,但是為了讓它可以被加在元素上,你仍然要給一個 true 的 boolean 值。

所以我們要活用 js 中的 Truthy!

若是字串不為 ""
那它就會是 true!

運用這個道理,我們把它以物件和陣列的混寫的方式來達成:

<template>
  <div 
    :class="[
      'leading-10',
      {
        'text-red-600 bg-red-300': theme==='red',
        'text-blue-600 bg-blue-300': theme==='blue',
        'text-green-600 bg-green-300': theme==='green',
      }
    ]"
  >
    兔兔教
  </div>

這樣是不是好多了呢?

固定的樣式我們就直接是字串就好了,
底下需要變動的才寫成物件。

不過...其實這還是不好。
(乾,兔兔又覺得不好了!)

應該要像我們前面一樣,
全部都用陣列解決就好!

那再改寫一下:

<template>
  <div 
    :class="[
      'leading-10',
      theme==='red'? 'text-red-600 bg-red-300': '',
      theme==='blue'? 'text-blue-600 bg-blue-300': '',
      theme==='green'? 'text-green-600 bg-green-300': '',
    ]"
  >
    兔兔教
  </div>

欸?

三元運算子變成字串,
然後空字串就是 false 嘛?

哦!
不錯哦!這想法 ...!
還可以再更好一些 XDDD

我們可以試著運用這個方法啦!

const result1 = false && "123" // false
const result2 = true && "123" // "123"

這樣看懂了嗎?

沒有錯! 用 AND!

如果今天前方的條件為 false
會直接回傳 false

但如果今天前方條件是 true
則會往後運算,
最後返回的是後面的字串

要是不這麼做啊,
我們還得再多寫一個空字串,
我不要!我不要!

所以,最後我們這樣寫:

<div
  :class="[
    'leading-10',
    theme==='red' && 'text-red-600 bg-red-300',
    theme==='blue' && 'text-blue-600 bg-blue-300',
    theme==='green' && 'text-green-600 bg-green-300',
  ]"
>
  兔兔教
</div>

這樣的寫法就跟之前的 box 一樣了!

那所謂的分類你可能沒感受到,我們就再來多加一個功能。

我們用 transition-all 來加上過渡效果,並再多加一個 duration-500 讓過度效果不要太快,不然就看不出來了。

加上去之後:

<div
  :class="[
    'leading-10',
    'transition-all duration-500',
    theme==='red' && 'text-red-600 bg-red-300',
    theme==='blue' && 'text-blue-600 bg-blue-300',
    theme==='green' && 'text-green-600 bg-green-300',
  ]"
>
  兔兔教
</div>

這樣一行一行的,就可以相同用途的功能放在一起,不但撰寫起來輕鬆,邏輯也很清晰哦,要維護時就不會這麼困難了!

那這邊就放上完整的元件內容:

<template>
  <div
    :class="[
      'leading-10',
      'transition-all duration-500',
      theme==='red' && 'text-red-600 bg-red-300',
      theme==='blue' && 'text-blue-600 bg-blue-300',
      theme==='green' && 'text-green-600 bg-green-300',
    ]"
  >
    兔兔教
  </div>
  <button @click="theme='red'" class="p-1 m-1 bg-gray-200 rounded">
    紅色主題
  </button>
  <button @click="theme='blue'" class="p-1 m-1 bg-gray-200 rounded">
    藍色主題
  </button>
  <button @click="theme='green'" class="p-1 m-1 bg-gray-200 rounded">
    綠色主題
  </button>
</template>

<script>
export default {
  data() {
    return {
      theme: "red",
    }
  },
}
</script>

這個範例的連結也會放在文章尾部!

 
最後來補充一下,同樣的版搬到 React 也幾乎可以馬上使用! 要靠一個叫做 clsx 的 npm 套件,然後修改一下直接用:

<div
  className="clsx(
    'leading-10',
    'transition-all duration-500',
    theme==='red' && 'text-red-600 bg-red-300',
    theme==='blue' && 'text-blue-600 bg-blue-300',
    theme==='green' && 'text-green-600 bg-green-300',
  )"
>
  兔兔教
</div>

摁,只要你變數名稱一模一樣,上面這段就可以直接拉去 React 用囉!

那 class 的部分就這樣詳解完啦~~
 

跟前幾天的相比,
今天有沒有比較簡單呢? (笑)

不過就是範例的量可能比較多啦,
慢慢看一定沒有問題的!

聽說只要說要考試,
學生們的學習能力就會大增
那我是不是下次來出個考... (被摀嘴)

兔女鵝:
「爸爸,我把老師的嘴摀住了,心裡不煩了,可以求你不要重新換我房間的裝潢嗎...!」

兔跋:
「 ... 」

「 要換!」
 

carrotPoint 給你們的回家作業:


關於兔兔們:


 


( # 兔兔小聲說 )

你們知道廣告上的這句

「心中若少軟萌兔,編譯再也無一物」

其實,
是有下一句的嗎?


上一篇
Day 20:「資料拿來我就幫你改」- Vue 基礎觀念及常用語法
下一篇
Day 22:「您好,歡迎登入 Vuta 奇幻世界」- 事件處理
系列文
排版神器 Tailwind CSS~和兔兔一起快速上手漂亮的元件開發!32

尚未有邦友留言

立即登入留言