Vue3 defineComponent 有什么作用?

defineComponent函数,只是对setup函数进行封装,返回options的对象;

export function defineComponent(options: unknown) {
  return isFunction(options) ? { setup: options } : options
}

defineComponent最重要的是:在TypeScript下,给予了组件 正确的参数类型推断 。

在这里插入图片描述
defineComponent重载函数

1:direct setup function

// overload 1: direct setup function
// (uses user defined props interface)
export function defineComponent<Props, RawBindings = object>(
  setup: (
    props: Readonly<Props>,
    ctx: SetupContext
  ) => RawBindings | RenderFunction
): DefineComponent<Props, RawBindings>

在这里插入图片描述
2:object format with no props

// overload 2: object format with no props
// (uses user defined props interface)
// return type is for Vetur and TSX support
export function defineComponent<
  Props = {},
  RawBindings = {},
  D = {},
  C extends ComputedOptions = {},
  M extends MethodOptions = {},
  Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
  Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
  E extends EmitsOptions = EmitsOptions,
  EE extends string = string
>(
  options: ComponentOptionsWithoutProps<Props,RawBindings,D,C,M,Mixin,Extends,E,EE>
): DefineComponent<Props, RawBindings, D, C, M, Mixin, Extends, E, EE>

在这里插入图片描述
3:object format with array props declaration

// overload 3: object format with array props declaration
// props inferred as { [key in PropNames]?: any }
// return type is for Vetur and TSX support
export function defineComponent<
  PropNames extends string,
  RawBindings,
  D,
  C extends ComputedOptions = {},
  M extends MethodOptions = {},
  Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
  Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
  E extends EmitsOptions = Record<string, any>,
  EE extends string = string
>(
  options: ComponentOptionsWithArrayProps<
    PropNames,
    RawBindings,...>
): DefineComponent<
  Readonly<{ [key in PropNames]?: any }>,
  RawBindings,...>

在这里插入图片描述
4: object format with object props declaration

// overload 4: object format with object props declaration
// see `ExtractPropTypes` in ./componentProps.ts
export function defineComponent<
  // the Readonly constraint allows TS to treat the type of { required: true }
  // as constant instead of boolean.
  PropsOptions extends Readonly<ComponentPropsOptions>,
  RawBindings,
  D,
  C extends ComputedOptions = {},
  M extends MethodOptions = {},
  Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
  Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
  E extends EmitsOptions = Record<string, any>,
  EE extends string = string
>(
  options: ComponentOptionsWithObjectProps<
    PropsOptions, RawBindings, D, C, M, Mixin, Extends, E, EE>
): DefineComponent<PropsOptions, RawBindings, D, C, M, Mixin, Extends, E, EE>

在这里插入图片描述

开发实践

除去单元测试中几种基本的用法,在以下的 ParentDialog 组件中,主要有这几个实际开发中要注意的点:

自定义组件和全局组件的写法

inject、ref 等的类型约束

setup 的写法和相应 h 的注入问题

tsx 中 v-model 和 scopedSlots 的写法

ParentDialog.vue

<script lang="tsx">
import { noop, trim } from 'lodash';
import {
  inject, Ref, defineComponent, getCurrentInstance, ref
} from '@vue/composition-api';
import filters from '@/filters';
import CommonDialog from '@/components/CommonDialog';
import ChildTable, { getEmptyModelRow } from './ChildTable.vue';
 
export interface IParentDialog {
  show: boolean;
  specFn: (component_id: HostComponent['id']) => Promise<{ data: DictSpecs }>;
}
 
export default defineComponent<IParentDialog>({
  // tsx 中自定义组件依然要注册
  components: {
    ChildTable
  },
  props: {
    show: {
      type: Boolean,
      default: false
    },
    specFn: {
      type: Function,
      default: noop
    }
  },
 
  // note: setup 须用箭头函数
  setup: (props, context) => {
 
    // 修正 tsx 中无法自动注入 'h' 函数的问题
    // eslint-disable-next-line no-unused-vars
    const h = getCurrentInstance()!.$createElement;
 
    const { emit } = context;
    const { specFn, show } = props;
 
    // filter 的用法
    const { withColon } = filters;
 
    // inject 的用法
    const pageType = inject<CompSpecType>('pageType', 'foo');
    const dictComponents = inject<Ref<DictComp[]>>('dictComponents', ref([]));
    
    // ref的类型约束
    const dictSpecs = ref<DictSpecs>([]);
    const loading = ref(false);
 
    const _lookupSpecs = async (component_id: HostComponent['id']) => {
      loading.value = true;
      try {
        const json = await specFn(component_id);
        dictSpecs.value = json.data;
      } finally {
        loading.value = false;
      }
    };
 
    const formdata = ref<Spec>({
      component_id: '',
      specs_id: '',
      model: [getEmptyModelRow()]
    });
    const err1 = ref('');
    const err2 = ref('');
    
    const _doCheck = () => {
      err1.value = '';
      err2.value = '';
      
      const { component_id, specs_id, model } = formdata.value;
      if (!component_id) {
        err1.value = '请选择部件';
        return false;
      }
      for (let i = 0; i < model.length; i++) {
        const { brand_id, data } = model[i];
        if (!brand_id) {
          err2.value = '请选择品牌';
          return false;
        }
        if (
          formdata.value.model.some(
            (m, midx) => midx !== i && String(m.brand_id) === String(brand_id)
          )
        ) {
          err2.value = '品牌重复';
          return false;
        }
      }
      return true;
    };
 
    const onClose = () => {
      emit('update:show', false);
    };
    const onSubmit = async () => {
      const bool = _doCheck();
      if (!bool) return;
      const params = formdata.value;
      emit('submit', params);
      onClose();
    };
 
    // note: 在 tsx 中,element-ui 等全局注册的组件依然要用 kebab-case 形式 ????‍
    return () => (
      <CommonDialog
        class="comp"
        title="新建"
        width="1000px"
        labelCancel="取消"
        labelSubmit="确定"
        vLoading={loading.value}
        show={show}
        onClose={onClose}
        onSubmit={onSubmit}
      >
        <el-form labelWidth="140px" class="create-page">
         <el-form-item label={withColon('部件类型')} required={true} error={err1.value}>
            <el-select
              class="full-width"
              model={{
                value: formdata.value.component_id,
                callback: (v: string) => {
                  formdata.value.component_id = v;
                  _lookupSpecs(v);
                }
              }}
            >
              {dictComponents.value.map((dictComp: DictComp) => (
                <el-option key={dictComp.id} label={dictComp.component_name} value={dictComp.id} />
              ))}
            </el-select>
          </el-form-item>
          {formdata.value.component_id ? (
              <el-form-item labelWidth="0" label="" required={true} error={err2.value}>
                <child-table
                  list={formdata.value.model}
                  onChange={(v: Spec['model']) => {
                    formdata.value.model = v;
                  }}
                  onError={(err: string) => {
                    err3.value = err;
                  }}
                  scopedSlots={{
                      default: (scope: any) => (
                        <p>{ scope.foo }</p>
                      )
                  }}
                />
              </el-form-item>
          ) : null}
        </el-form>
      </CommonDialog>
    );
  }
});
</script>
 
<style lang="scss" scoped>
</style>

全文总结

引入 defineComponent() 以正确推断 setup() 组件的参数类型
defineComponent 可以正确适配无 props、数组 props 等形式
defineComponent 可以接受显式的自定义 props 接口或从属性验证对象中自动推断
在 tsx 中,element-ui 等全局注册的组件依然要用 kebab-case 形式
在 tsx 中,v-model 要用 model={{ value, callback }} 写法
在 tsx 中,scoped slots 要用 scopedSlots={{ foo: (scope) => () }} 写法
defineComponent 并不适用于函数式组件,应使用 RenderContext 解决

  • 3
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
`defineComponent` 是 Vue 3 的一个 API,它用于创建一个组件。与 Vue 2 中的 `Vue.component` 不同,`defineComponent` 是一个函数,它接收一个组件配置对象,并返回一个组件对象。 `defineComponent` 可以简化组件的编写过程,它会自动处理配置对象中的选项,并将它们转换为组件实例的属性和方法。同时,`defineComponent` 还可以提供类型检查和智能提示等功能,使得组件的开发更加高效和可靠。 在使用 `defineComponent` 创建组件时,需要传入一个配置对象,该对象包含组件的选项,比如 `data`、`methods`、`computed`、`props`、`watch` 等。此外,还可以通过 `setup` 选项来定义组件的逻辑,包括响应式数据、计算属性、事件处理函数等。 示例代码: ```javascript import { defineComponent } from 'vue' export default defineComponent({ name: 'MyComponent', props: { msg: String }, setup(props) { const count = ref(0) const handleClick = () => { count.value++ } return { count, handleClick } }, template: ` <div> <p>{{ msg }}</p> <p>Count: {{ count }}</p> <button @click="handleClick">Increment</button> </div> ` }) ``` 在上面的示例中,我们定义了一个名为 `MyComponent` 的组件,它有一个 `msg` 属性和一个 `count` 响应式数据。在 `setup` 函数中,我们定义了一个 `handleClick` 函数,用于处理按钮的点击事件。最后,我们将属性和方法绑定到模板中,以实现组件的渲染和交互。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值