Vue 3 中的 jsx 实践
一、起因
- 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 中似乎也不一样