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 中似乎也不一樣
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章