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 解决

关于浅谈Vue3 defineComponent有什么作用的文章就介绍至此,更多相关Vue3 defineComponent作用内容请搜索编程宝库以前的文章,希望以后支持编程宝库

本文为大家分享了微信小程序实现上传图片的具体代码,供大家参考。WXML<view class="img-wrap"><view class="txt">上传图片</view>< ...