Vue.js Basic Learning 12 — Component Exercise

再做一組 component 的練習。這次要做出的是 message alert bar。可以依照 alert type 做不同樣式的呈現,並且 click “X” 可以關掉此列訊息。

樣式的部份講者是使用 stylus 來編寫,我是照 key 後再使用 online converter 轉換成 css。因為本人 css 程度低劣,所以就不勉強多做介紹了。

stylus:

.Alert
position: relative
background: #ddd
border: 1px solid darken(#ddd, 10%)
padding: 1em
.Alert--Success
background: lighten(green, 70%)
border: 1px solid lighten(green, 30%)
.Alert--Error
background: lighten(red, 70%)
border: 1px solid lighten(red, 30%)
.Alert__close
position: absolute
top: 1em
right: 1em
font-weight: bold
cursor: pointer

css:

.Alert {
position: relative;
background: #ddd;
border: 1px solid #c7c7c7;
padding: 1em;
}
.Alert--Success {
background: #8cff8c;
border: 1px solid #00f300;
}
.Alert--Error {
background: #ffb3b3;
border: 1px solid #ff4d4d;
}
.Alert__close {
position: absolute;
top: 1em;
right: 1em;
font-weight: bold;
cursor: pointer;
}

基本的 component 和 template 組成大概會是:

<div id="app">
<alert type=""><strong>Normal!</strong> my message</alert>
<alert type="success"><strong>Success!</strong> my message</alert>
<alert type="error"><strong>Error!</strong> my message</alert>
</div>
<template id="alert-template">
<div class="Alert">
<slot></slot>
<span class="Alert__close">X</span>
</div>
</template>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script type="text/javascript">
Vue.component('alert', {
template: '#alert-template'
}
});
new Vue({
el: '#app'
});
</script>

一個 alert 元件就是一條 message bar。對 alert 來說他並不知道該顯示什麼文字,message 內容應該是要由使用 alert 的人自由填入的,和 alert type 沒有直接關係,所以不放在 template 中。

這邊使用 <slot> 來將 <alert> 區塊中要呈現的文字轉移到 template 內。slot 還有個好用的特性是如果 parent component 中並沒有指定文字,那就可以呈現 slot 中的內容(有設置的話)。例如:

<alert type=""><alert>
<!-- 以下為 template -->
<slot>default message</slot>

官方詳細的 slot 說明可以參考這邊:


接著 close event 我們只要在 click 時去操作 v-show 就好。增加 data 變數 show 預設為 true,click 時再調整為 false 即可:

<span class="Alert__close" @click="show = false">X</span>
<!-- 以下為 js -->
// ...
template: '#alert-template',
props: ['type'],
data: function() {
return {
show: true
};
}

最後剩下根據不同的 alert type 套上不同的樣式。每個 alert 都要有基本的 class=“Alert”,如果 type=“success” 那要變成 class=“Alert Alert--Success”。

根據官方文件,在做 v-bind 時若對象是 class 或 style 會有特殊功能:

Vue provides special enhancements when v-bind is used with class and style. In addition to strings, the expressions can also evaluate to objects or arrays.
  • Object Syntax

:class 內可以直接傳物件格式或是物件變數。以下的語法會當 isActive 為 true 時,增加一個 active class:

<div v-bind:class="{ active: isActive }"></div>

如果需要根據條件決定的 class 比較多,也可以先把 default 的固定寫好,剩下的全部放在另外的 :class 內一起判斷:

<div class="static" v-bind:class="{ active: isActive, 'text-danger': hasError }"></div>
  • Array Syntax

同樣在 :class 的情況下,可以直接傳陣列進去,也可做 inline 的判斷:

<div v-bind:class="[activeClass, errorClass]">
<div v-bind:class="[isActive ? activeClass : '', errorClass]">

閱讀過官方的介紹後來看這個需求。基本一定要有 Alert,再根據 type 是 success 的話就增加 Alert- -Success,error 的話就增加 Alert- -Error,類似:

<div :class="['Alert', 'Alert--Success']" v-show="show">

稍微分組一下,再加入判斷條件可以得到:

<div class="Alert" 
:class="[
this.type == 'success' ? 'Alert--Success' : '',
this.type == 'error' ? 'Alert--Error' : ''
]"
v-show="show">

但是這樣寫有點亂,改用 computed 和 Object Syntax,一口氣把每個 class 要不要呈現的邏輯都寫好,最後直接把物件 alertClasses 傳入 :class 中即可:

<div :class="alertClasses" v-show="show">
<!-- 以下為 js -->
computed: {
alertClasses: function() {
return {
'Alert': true,
'Alert--Success': (this.type == 'success'),
'Alert--Error': (this.type == 'error')
};
}
}

這樣就打完收工了,實際寫起來跟 jQuery 相比,因為 Vue 已經做好了雙向的 data binding,使得邏輯較為收斂,而 component 和 template 能讓 html 達到重用的效果。