1. 程式人生 > 程式設計 >淺談Vue3 defineComponent有什麼作用

淺談Vue3 defineComponent有什麼作用

目錄
  • 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,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,E extends EmitsOptions = Record<string,any>,EE extends string = string
>(
  options: ComponentOptionsWithArrayProps<
    PropNames,...>
): DefineComponent<
  Readonly<{ [key in PropNames]?: any }>http://www.cppcns.com
;,...>

在這裡插入圖片描述

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>,EE extends string = string
>(
  options: ComponentOptionsWithObjectProps<
    PropsOptions,EE>
): DefineComponent<PropsOptions,EE>

在這裡插入圖片描述

開發實踐

除去單元測試中幾種基本的用法,在以下的 ParentDialog 元件中,主要有這幾個實際開發中要注意的點:

自定義元件和全域性元件的寫法

inject、ref 等的型別約束

setup 的寫法和相應 h 的注入問題

tsx 中 v-model 和 scopedSlots 的寫法

ParentDialog.

<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 on = 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;
        www.cppcns.com}
      }
      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>
                      )
                  }}
                />
              </www.cppcns.comel-form-item>
          ) : null}
        </el-form>
      </CommonDialog>
    );
  }
});
</script>
 
<style lang="s" 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作用內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!