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>
    View Code
  • 碎片组件 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>
    View Code
  • 组合组件:
    <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>
    View Code

三、解决的问题

  • 根据业务需要随时组合组件,并且如同上面显示的那样,组合出来的表单基本不会暴露无关项目
  • 即使是碎片组件,也可以单独拿出来使用

四、可能存在的问题

  • 数据变化时 diff 算法以及对视图的更新,所以上面的 demo 还是 vue 的响应式写法,不知道能不能完全做到像 react 中 jsx 的写法 - 即不依赖响应式写法。不过想想 vue 的 h 函数的实现,感觉可能性不大;
  • 碎片组件单独使用时与外界的通信 - 不过已经使用了 Hook, 基本上数据已经通过 Hook 暴露了出去,应该不存在通信问题
  • 表单必填项的验证

五、需要注意的地方

  • slot 的写法,与 vue 2 中似乎也不一样
posted @ 2024-01-05 13:49  shiweiqianju  阅读(30)  评论(0编辑  收藏  举报