浅谈Vue3 defineComponent有什么作用

作者:飞啊飞我的骄傲放纵-未来全栈工程师 时间:2024-05-05 09:25:03 

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


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

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

浅谈Vue3 defineComponent有什么作用

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>

浅谈Vue3 defineComponent有什么作用

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>

浅谈Vue3 defineComponent有什么作用

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,...>

浅谈Vue3 defineComponent有什么作用

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>

浅谈Vue3 defineComponent有什么作用

开发实践

除去单元测试中几种基本的用法,在以下的 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 解决

来源:https://blog.csdn.net/weixin_44378370/article/details/111475625

标签:Vue3,defineComponent
0
投稿

猜你喜欢

  • vue实现带过渡效果的下拉菜单功能

    2024-05-09 15:18:47
  • Python+Opencv实现图像匹配功能(模板匹配)

    2022-06-28 08:56:35
  • vue中provide和inject的用法及说明(vue组件爷孙传值)

    2024-05-21 10:15:26
  • 你知道怎么在淘宝里进行投诉吗?

    2008-06-04 12:00:00
  • MySQL的root帐户密码重置方法

    2007-08-24 15:53:00
  • Python代码实现http/https代理服务器的脚本

    2022-04-12 10:19:06
  • asp数组使用特法

    2009-05-11 12:43:00
  • 用python3读取python2的pickle数据方式

    2023-06-05 09:45:48
  • 实例讲解MySQL统计库表大小

    2024-01-19 09:21:53
  • python 实现在tkinter中动态显示label图片的方法

    2022-07-17 11:10:15
  • Python import自己的模块报错问题及解决

    2023-11-09 15:57:08
  • Python自动化办公之群发邮件案例详解

    2023-09-08 17:10:36
  • python操作xml文件示例

    2022-06-17 22:38:28
  • Python中22个万用公式的小结

    2022-12-15 21:06:38
  • python zip,lambda,map函数代码实例

    2023-08-25 05:04:49
  • MySQL学习第三天 Windows 64位操作系统下验证MySQL

    2024-01-22 20:19:39
  • python with (as)语句实例详解

    2023-04-12 04:15:09
  • OpenCV学习之图像加噪与滤波的实现详解

    2022-09-20 04:40:57
  • django echarts饼图数据动态加载的实例

    2023-02-13 21:12:43
  • Python脚本实现格式化css文件

    2023-01-09 19:00:37
  • asp之家 网络编程 m.aspxhome.com