浅谈Vue3 defineComponent有什么作用

 更新时间:2021年08月13日 15:53:01   作者:飞啊飞我的骄傲放纵-未来全栈工程师  
本文主要介绍了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作用内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:

相关文章

  • vue中循环表格数据出现数据联动现象(示例代码)

    vue中循环表格数据出现数据联动现象(示例代码)

    在Vue中循环生成表格数据时,可能会遇到数据联动的现象,即修改一个表格中的数据后,其他表格的数据也会跟着变化,这种现象通常是因为所有表格的数据引用了同一个对象或数组导致的,本文介绍vue中循环表格数据出现数据联动现象,感兴趣的朋友一起看看吧
    2024-11-11
  • 解决vue router使用 history 模式刷新后404问题

    解决vue router使用 history 模式刷新后404问题

    这篇文章主要介绍了解决vue router使用 history 模式刷新后404问题,需要的朋友可以参考下
    2017-07-07
  • 详解Vue之计算属性

    详解Vue之计算属性

    这篇文章主要介绍了Vue之计算属性的相关知识,文中讲解非常细致,代码帮助大家更好的理解和学习,感兴趣的朋友可以了解下
    2020-06-06
  • Vue实现自定义组件改变组件背景色(示例代码)

    Vue实现自定义组件改变组件背景色(示例代码)

    要实现 Vue 自定义组件改变组件背景色,你可以通过 props 将背景色作为组件的一个属性传递给组件,在组件内部监听这个属性的变化,并将其应用到组件的样式中,下面通过示例代码介绍Vue如何实现自定义组件改变组件背景色,感兴趣的朋友一起看看吧
    2024-03-03
  • VUE中使用MUI方法

    VUE中使用MUI方法

    在本篇文章里小编给大家分享了关于VUE中使用MUI方法和步骤,有需要的朋友们可以学习参考下。
    2019-02-02
  • vue自定义可选时间的日历组件

    vue自定义可选时间的日历组件

    这篇文章主要为大家详细介绍了vue自定义可选时间的日历组件,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-08-08
  • vue3如何定义变量及ref、reactive、toRefs特性说明

    vue3如何定义变量及ref、reactive、toRefs特性说明

    这篇文章主要介绍了vue3如何定义变量及ref、reactive、toRefs特性说明,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-06-06
  • Vue.js获取手机系统型号、版本、浏览器类型的示例代码

    Vue.js获取手机系统型号、版本、浏览器类型的示例代码

    这篇文章主要介绍了vue js获取手机系统型号、版本、浏览器类型的示例代码,代码简单易懂,非常不错具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-05-05
  • vue中Form 表单的 resetFields() 失效原因及问题解决

    vue中Form 表单的 resetFields() 失效原因及问题解决

    在Vue项目中,使用formRef.value.resetFields()方法重置表单时可能遇到不起作用的问题,下面就来介绍一下如何解决,感兴趣的可以了解一下
    2024-09-09
  • vue中v-model动态生成的实例详解

    vue中v-model动态生成的实例详解

    这篇文章主要介绍了vue中v-model动态生成的实例详解的相关资料,希望通过本文能帮助到大家,让大家理解掌握这部分内容,需要的朋友可以参考下
    2017-10-10

最新评论