Vue3 tsx 泛型组件

979 阅读1分钟

包含以下内容

  1. tsx泛型
  2. defineComponent备用函数签名(Vue 3.3+)
  3. v-if v-for v-on v-model
  4. 作用域插槽
  5. 模板引用
  6. 自定义事件

直接上代码

父组件 Father.tsx

import { defineComponent, ref } from "vue";
import Child from "./Child";



/*
1.Vue 模板中 ref 变量是可以直接解构的,但是在 jsx 中不行,需要添加 .value
2.ctx 上下文对象是非响应式的,可以安全地解构:
3.
*/
const FatherTsx = defineComponent(
  (props, { attrs, slots, emit, expose }) => {
    const arr = ref<number[]>(Array.from(new Array(10).keys()));

    const fatherHandler = () => {
      console.log("father handler");
    };

    const child = ref()

    const triggerChildMethod =()=>{
      child.value?.childMethod()
    }

    const testAttrs = {
      name:'testAttrs'
    }

    const testVModel = ref('initial value')
    const updateVModel = (value:string)=>{
      testVModel.value = value
    }

    return () => (
      <>
        <Child arr={arr.value} ref={child} onFatherHandler={fatherHandler} {...testAttrs} onClick={triggerChildMethod} modelValue={testVModel.value} onUpdate:modelValue={updateVModel}>{{
          header: ({name}:{name:string}) => <div>{name}</div>,
          default: ({name}:{name:string}) => `${name} slot`,
          footer: ({name}:{name:string}) => [<div>{name} - one </div>, <div>{name} - two</div>]
        }}</Child>
        <button onClick={triggerChildMethod}>triggerChildMethod</button>
      </>
    );
  },
  {
  }
);

export default FatherTsx;

子组件 Child.tsx

import { defineComponent, SetupContext, SlotsType, PropType, ref } from "vue";

interface IProps<T> {
  arr: Array<T>;
  modelValue: string;
}
interface IEmits {
  fatherHandler(name: string): void;
  click(e: MouseEvent): any;
  "update:modelValue": (value: string) => void;
}
interface ISlots {
  default: { name: string };
  header: { name: string };
  footer: { name: string };
}

/*
1.泛型语法与tsx语法重复,因此不能单独写<T>,要么写 <T,> 要么写 <T extends xxx>
2.onClick需要在自定义组件上的emits中进行声明,否则类型校验有问题
*/
const Child = defineComponent(
   // Vue3.3+ defineComponent备用函数签名,可以使用泛型
  <T extends number | string>(
    props: IProps<T>,
    { attrs, slots, emit, expose }: SetupContext<IEmits, SlotsType<ISlots>>
  ) => {
    function childMethod() {
      console.log("childMethod");
    }

    function triggerVModel(e: any) {
      emit("update:modelValue", e.target.value);
    }

    const testVif = ref(true);

    expose({ childMethod });

    console.log({ attrs, props });

    return () => (
      <>
        {/* props, emitted events, event handlers, v-if v-model v-for*/}
        <div {...attrs}>
          {slots.header && slots.header({ name: "header" })}
          {slots.default && slots.default({ name: "default" })}
          {slots.footer && slots.footer({ name: "footer" })}

          <button onClick={() => emit("fatherHandler", "123")}>
            trigger emits
          </button>
          
          {testVif.value ? <div>v-if</div> : <div>v-else</div>}

          <ul>
            {props.arr.map((item: T) => {
              return <li>{item}</li>;
            })}
          </ul>

          <input
            type="text"
            value={props.modelValue}
            onInput={triggerVModel}
            placeholder="test V-Model"
          />
        </div>
      </>
    );
  },
  {
    props: {
      arr: Array as PropType<Array<number | string>>,
      modelValue: String,
    },
    slots: Object as SlotsType<ISlots>,
    emits: ["fatherHandler", "click", "update:modelValue"],
  }
);

export default Child;

如果对你有帮助的话就请点个赞吧