這篇記錄一下專案上遇到的開發模式,情境是今天有一個 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 去做判定,但這次學到如果功能越趨複雜,需求越加繁多,在後面就會比較難去擴充功能跟修改樣式了。
參考資料