一、起因
- template 的寫法不太靈活,數據和模版都放在一起,不能拼接;
- 如果拆太碎,每個碎片組件都得寫一堆 props/emit 才能確保通信的完整性,太麻煩;
- 如果寫在一個文件中,模版上的判斷邏輯 (v-if) 又得一堆;
二、codes
- 碎片組件 1:
<script lang="jsx"> import { defineComponent } from 'vue'; export const useHook = () => { const field1 = ref(); const field2 = ref(); ... return [field1, field2, ...]; } export const renderTemplate = (field1, field2, field3...) => { return () => ( <> <van-field v-model={field1.value} label="內容:" placeholder="請輸入內容" label-align="top" autosize type="textarea" /> <van-field label="圖像:" label-align="top" > {{ input: () => ( <van-uploader v-model={field2.value} after-read={field3} /> ), default: () => ( <></> ) }} </van-field> </> ) }; export default defineComponent({ setup() { const [field1, field2, ...]; = useHook(); return renderTemplate(field1, field2, ...); } }); </script>
- 碎片組件 2:
<script lang="jsx"> import { defineComponent } from 'vue'; export const useHook = () => { const checked = ref(-1); return [checked]; } export const renderTemplate = (checked) => { return () => ( <> <van-field name="radio" label="結果:"> {{ input: () => ( <van-radio-group v-model={checked.value} direction="horizontal"> <van-radio name={1}>truth</van-radio> <van-radio name={0}>falsehood</van-radio> </van-radio-group> ) }} </van-field> </> ) }; export default defineComponent({ setup() { const [checked] = useResultRadio(); return renderTemplate(checked); } }); </script>
- 組合組件:
<script lang="jsx"> import { defineComponent, computed } from 'vue'; import { useHook as useHook1, renderTemplate as templateRender1 } from '碎片組件 1'; import { useHook as useHook2, renderTemplate as templateRender2 } from '碎片組件 2'; export default defineComponent({ emits: [...], props: {...}, setup(props, ctx) { const { emit } = ctx; const [field1, field2, ...] = useHook1(); const [checked] = useHook2(); const renders = [ templateRender1(field1, field2, ...), ]; if (xxx) { renders.push(templateRender2(checked)); } return () => { return ( { // html 模版 renders.map(r => r()) } ) } } }); </script>
三、解決的問題
- 根據業務需要隨時組合組件,並且如同上面顯示的那樣,組合出來的表單基本不會暴露無關項目
- 即使是碎片組件,也可以單獨拿出來使用
四、可能存在的問題
- 數據變化時 diff 算法以及對視圖的更新,所以上面的 demo 還是 vue 的響應式寫法,不知道能不能完全做到像 react 中 jsx 的寫法 - 即不依賴響應式寫法。不過想想 vue 的 h 函數的實現,感覺可能性不大;
- 碎片組件單獨使用時與外界的通信 - 不過已經使用了 Hook, 基本上數據已經通過 Hook 暴露了出去,應該不存在通信問題
- 表單必填項的驗證
五、需要注意的地方
- slot 的寫法,與 vue 2 中似乎也不一樣