A brief discussion on the role of Vue3 defineComponent

A brief discussion on the role of Vue3 defineComponent

The defineComponent function simply encapsulates the setup function and returns an options object;

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

The most important thing about defineComponent is that in TypeScript, it gives the component the correct parameter type inference.

insert image description here

defineComponent overload function

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>

insert image description here

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>

insert image description here

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

insert image description here

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>

insert image description here

Development Practice

Apart from the basic usage in unit testing, there are several points to note in the following ParentDialog component:

How to write custom components and global components

Type constraints for inject, ref, etc.

How to write setup and the corresponding h injection problem

How to write v-model and scopedSlots in tsx

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>({
  // Custom components in tsx still need to register components: {
    ChildTable
  },
  props: {
    show: {
      type: Boolean,
      default: false
    },
    specFn: {
      type: Function,
      default: noop
    }
  },
 
  // note: setup must use arrow function setup: (props, context) => {
 
    // Fix the problem that 'h' function cannot be automatically injected in tsx // eslint-disable-next-line no-unused-vars
    const h = getCurrentInstance()!.$createElement;
 
    const { emit } = context;
    const { specFn, show } = props;
 
    // Usage of filter const { withColon } = filters;
 
    // inject usage const pageType = inject<CompSpecType>('pageType', 'foo');
    const dictComponents = inject<Ref<DictComp[]>>('dictComponents', ref([]));
    
    // ref type constraint 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 = 'Please select a component';
        return false;
      }
      for (let i = 0; i < model.length; i++) {
        const { brand_id, data } = model[i];
        if (!brand_id) {
          err2.value = 'Please select a brand';
          return false;
        }
        if (
          formdata.value.model.some(
            (m, midx) => midx !== i && String(m.brand_id) === String(brand_id)
          )
        ) {
          err2.value = 'Brand duplicate';
          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: In tsx, globally registered components such as element-ui still need to use kebab-case format????‍
    return () => (
      <CommonDialog
        class="comp"
        title="New"
        width="1000px"
        labelCancel="Cancel"
        labelSubmit="OK"
        vLoading={loading.value}
        show={show}
        onClose={onClose}
        onSubmit={onSubmit}
      >
        <el-form labelWidth="140px" class="create-page">
         <el-form-item label={withColon('Component type')} 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>

Summary

  • Introduced defineComponent() to correctly infer parameter types of setup() components
  • defineComponent can correctly adapt to forms such as no props, array props, etc.
  • defineComponent can accept an explicit custom props interface or be automatically inferred from a property validation object
  • In tsx, globally registered components such as element-ui still need to use kebab-case format
  • In tsx, v-model should use model={ { value, callback }} syntax
  • In tsx, scoped slots should use scopedSlots={ { foo: (scope) => () }} syntax
  • defineComponent does not apply to functional components, use RenderContext instead

This is the end of this article about the role of Vue3 defineComponent. For more information about the role of Vue3 defineComponent, please search 123WORDPRESS.COM's previous articles or continue to browse the following related articles. I hope everyone will support 123WORDPRESS.COM in the future!

You may also be interested in:
  • Thinking about vue3 binding all onSomething as v-on events by default
  • This article will show you how to use Vue 3.0 responsive
  • Implementation of vue3.0+vant3.0 rapid project construction
  • Use Vue3 to implement a component that can be called with js
  • Detailed explanation of the underlying principle of defineCustomElement added in vue3.2
  • Vue3 Vue Event Handling Guide

<<:  Using MySQL database in docker to achieve LAN access

>>:  MySQL 8.0.18 installation and configuration graphic tutorial

Recommend

jQuery plugin to achieve carousel effect

A jQuery plugin every day - jQuery plugin to impl...

Implementation example of Docker rocketmq deployment

Table of contents Preparation Deployment process ...

How to disable web page styles using Firefox's web developer

Prerequisite: The web developer plugin has been in...

Detailed explanation of the use of custom parameters in MySQL

MySQL variables include system variables and syst...

Vue custom encapsulated button component

The custom encapsulation code of the vue button c...

Basic tutorial on using explain statement in MySQL

Table of contents 1. Overview 1. Explain statemen...

WeChat applet implements video player sending bullet screen

This article shares the specific code for WeChat ...

CSS uses calc() to obtain the current visible screen height

First, let's take a look at the relative leng...

HTML+CSS to achieve drop-down menu

1. Drop-down list example The code is as follows:...

JQuery implements hiding and displaying animation effects

This article shares the specific code of JQuery t...

What are the new features of Apache Spark 2.4, which will be released in 2018?

This article is from the Apache Spark Meetup held...

Use Element+vue to implement start and end time limits

This article example shares the specific code for...

25 Vue Tips You Must Know

Table of contents 1. Limit props to type lists 2....