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 给你们的回家作业:


关於兔兔们:


 


( # 兔兔小声说 )

你们知道广告上的这句

「心中若少软萌兔,编译再也无一物」

其实,
是有下一句的吗?


<<:  Day10 Collectionview小实作4

>>:  予焦啦!装置树(DTB)解析

Python Callback Function 回呼函式

Callback Function 回呼函式 Callback Function 其实描述的就是一个...

D6. 学习基础C、C++语言

D6: for回圈 最基本的for回圈样式是: for (变数初始值; 判断式; 递增式){ 陈述句...

Groovy 语言和你 SAY HELLO!!

第十八天 各位点进来的朋友,你们好阿 小的不才只能做这个系列的文章,但还是希望分享给点进来的朋友,知...

[Day 28] 社会对人工智慧的期待与影响 Society Concern and Impact

系列文章要到结尾了,今天想分享一些几篇关於「社会对人工智慧的期待与影响」的文章。 未来,您对人工智慧...

Day2-看看JDK内有些什麽好用的工具!

前言 工作了好一段时间後,直到那次处理了OOM(Out Of Memory)问题,才发现JDK内有很...