0%

Vue 筆記 - 展示性元件 VS 容器式元件

container

這篇記錄一下專案上遇到的開發模式,情境是今天有一個 input 的共用元件,會依照不同的條件去修改基礎樣式,在同事的解說下把觀念釐清並筆記。

展示性元件 Presentation Component

主要用途是 UI 的呈現,一般來說都是接收容器式元件或是父層元件的 props 資料,使用展示性元件最大的優勢就是可以重複的使用。

像是本次範例使用的 input 元件。

容器式元件 Container Component

主要是用來接收資料用,並且大部分的情境會有自己的 state 來存放或接收的資料,並且把這些資料傳給展示性元件。

像是本次的範例為許多 input 組成的表單。

實作

建立展示性元件

有上方概念後,在建立一個展示性的 input 元件,此元件的概念式可以再重複使用的,所以不會特別綁定商業邏輯的規則,建立好基本的樣式後,也確認在父層引入後顯示無誤,便建立以下共用屬性。

CustomInput.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
<template>
<div>
<div v-if="type === 'radio' || type === 'checkbox'">
<div class="flex items-center w-full">
<input
v-model="currentValue"
:type="type"
:name="customName"
class="px-5 py-2 rounded-md border border-blue-400 active:ring-offset-2 active:ring outline-none"
:placeholder="customPlaceholder"
/>
<p class="text-base pl-4" :class="{ ...customWidth }">
{{ customTitle }}
</p>
</div>
</div>
<div v-else-if="type === 'date'">
<input
:type="type"
:name="customName"
class="lg:px-5 py-2 rounded-md border border-blue-400 active:ring-offset-2 active:ring outline-none"
:class="{ ...customWidth }"
:disabled="(currentValue = 'false')"
/>
</div>
<div v-else>
<label>
<h3 class="text-left text-lg pb-2 pt-4" :class="{ ...customWidth }">
{{ customTitle }}
</h3>
<input
v-model="currentValue"
:type="type"
:name="customName"
class="lg:px-5 py-2 rounded-md border border-blue-400 active:ring-offset-2 active:ring outline-none"
:class="{ ...customWidth }"
:placeholder="customPlaceholder"
/>
</label>
</div>
</div>
</template>

<script>
export default {
name: "CustomInput",
data() {
return {
currentValue: this.customValue,
};
},
props: {
type: {
type: String,
default: "text",
},
customWidth: {
type: Object,
default: () => ({ "w-full": true }),
},
customTitle: {
type: String,
default: "",
},
customPlaceholder: {
type: String,
default: "",
},
customName: {
type: String,
default: "",
},
customValue: {
type: String,
default: "",
},
},
};
</script>

容器式元件中引入展示性元件

也就是引入子元件,要注意的是除了在 template 引入後,也要在 script 引入,

ComponentView.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
<template>
<div>
<custom-input :custom-title="'姓名'" :custom-placeholder="'EX:王小明'">
</custom-input>
</div>
</template>

<script>
import CustomInput from "@/components/CustomInput.vue";
export default {
components: { CustomInput },
};
</script>

完成後應該會出現下圖:

之後就可以在容器式元件中改變 type 的類型,逐步客製出想要的表單,並且加上自己想要的樣式,透過基本元件化的方式就可以快速完成表單的設定囉!

展示性元件要保持高彈性

這次情境出現兩個元件要合併的呈現的畫面,此時就可以用 props 在展示性元件使用物件的方式把父層的直傳進來,

CustomInput.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<template>
<div>
<label>
<h3 class="text-left text-lg pb-2 pt-4" :class="{ ...customWidth }">
{{ customTitle }}
</h3>
<input
v-model="currentValue"
:type="type"
:name="customName"
class="lg:px-5 py-2 rounded-md border border-blue-400 active:ring-offset-2 active:ring outline-none"
:class="{ ...customWidth }"
:placeholder="customPlaceholder"
/>
</label>
</div>
</template>

<script>
export default{
props:{
customWidth: {
type: Object,
default: () => ({ "w-full": true }),
},
}
}
</scirpt>

ComponentView.vue

這邊有一個是否指定日期的功能,透過自訂 :class 的方式,傳到子元件,在樣式上可以透過物件的方式做更高彈性的修改,像這邊範例就是可以自由地修改欄位的寬度。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<div class="lg:grid lg:grid-cols-3 lg:gap-4 py-5">
<div class="lg:flex lg:items-center">
<custom-input
:type="'radio'"
name="unOrder"
value="'false'"
:custom-title="'不指定日期'"
:custom-width="{ 'w-32': true }"
v-model="form.radio"
:customValue="form.radio"
></custom-input>
</div>

<div class="lg:flex lg:items-center">
<custom-input
:type="'radio'"
name="isOrder"
value="'true'"
:custom-title="'不指定日期'"
:custom-width="{ 'w-32': true }"
v-model="form.radio"
:customValue="form.radio"
></custom-input>

<custom-input
:type="'date'"
:custom-width="{ 'w-full': true, 'pt-0': true }"
></custom-input>
</div>
</div>

基礎範例

連結:https://stackblitz.com/edit/vue-dyzdwr?file=src%2Fcomponents%2FCustomInput.vue

結語

過去開發會依照商業邏輯去設定變數去判定是否為這個需求,大多是用 Boolean 去做判定,但這次學到如果功能越趨複雜,需求越加繁多,在後面就會比較難去擴充功能跟修改樣式了。

參考資料