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

Tutorial on installing MySQL 5.7.18 using RPM package

system: CentOS 7 RPM packages: mysql-community-cl...

Installation and configuration method of vue-route routing management

introduce Vue Router is the official routing mana...

Detailed explanation of docker-machine usage

Docker-machine is a Docker management tool offici...

MySQL 8.0.14 installation and configuration method graphic tutorial (general)

MySQL service 8.0.14 installation (general), for ...

Some common mistakes with MySQL null

According to null-values, the value of null in My...

How to solve the Docker container startup failure

Question: After the computer restarts, the mysql ...

HTML tag meta summary, HTML5 head meta attribute summary

Preface meta is an auxiliary tag in the head area...

Solve the problem of running hello-world after docker installation

Installed Docker V1.13.1 on centos7.3 using yum B...

MySQL Optimization Summary - Total Number of Query Entries

1. COUNT(*) and COUNT(COL) COUNT(*) usually perfo...

How to install and deploy ftp image server in linux

Refer to the tutorial on setting up FTP server in...

Detailed explanation of Nginx timeout configuration

I recently used nginx in a project, and used Java...

Detailed explanation of views in MySQL

view: Views in MySQL have many similarities with ...

Summary of MySQL development standards and usage skills

1. Naming conventions 1. Database names, table na...

CSS cleverly uses gradients to achieve advanced background light animation

accomplish This effect is difficult to replicate ...