淺談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>
開發(fā)實踐
除去單元測試中幾種基本的用法,在以下的 ParentDialog 組件中,主要有這幾個實際開發(fā)中要注意的點:
自定義組件和全局組件的寫法
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作用內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
vue中element-ui組件默認css樣式修改的四種方式
在前端項目中會運用各種組件,有時組件的默認樣式并不是你項目中所需要的,你需要更改樣式,下面這篇文章主要給大家介紹了關于vue中element-ui組件默認css樣式修改的四種方式,需要的朋友可以參考下2021-10-10