淺談Vue3 defineComponent有什么作用
defineComponent函數(shù),只是對(duì)setup函數(shù)進(jìn)行封裝,返回options的對(duì)象;
export function defineComponent(options: unknown) { return isFunction(options) ? { setup: options } : options }
defineComponent最重要的是:在TypeScript下,給予了組件 正確的參數(shù)類型推斷 。
defineComponent重載函數(shù)
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>
開(kāi)發(fā)實(shí)踐
除去單元測(cè)試中幾種基本的用法,在以下的 ParentDialog 組件中,主要有這幾個(gè)實(shí)際開(kāi)發(fā)中要注意的點(diǎn):
自定義組件和全局組件的寫法
inject、ref 等的類型約束
setup 的寫法和相應(yīng) h 的注入問(wèn)題
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 中自定義組件依然要注冊(cè) components: { ChildTable }, props: { show: { type: Boolean, default: false }, specFn: { type: Function, default: noop } }, // note: setup 須用箭頭函數(shù) setup: (props, context) => { // 修正 tsx 中無(wú)法自動(dòng)注入 'h' 函數(shù)的問(wèn)題 // 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 = '請(qǐng)選擇部件'; return false; } for (let i = 0; i < model.length; i++) { const { brand_id, data } = model[i]; if (!brand_id) { err2.value = '請(qǐng)選擇品牌'; return false; } if ( formdata.value.model.some( (m, midx) => midx !== i && String(m.brand_id) === String(brand_id) ) ) { err2.value = '品牌重復(fù)'; 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 等全局注冊(cè)的組件依然要用 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>
全文總結(jié)
- 引入 defineComponent() 以正確推斷 setup() 組件的參數(shù)類型
- defineComponent 可以正確適配無(wú) props、數(shù)組 props 等形式
- defineComponent 可以接受顯式的自定義 props 接口或從屬性驗(yàn)證對(duì)象中自動(dòng)推斷
- 在 tsx 中,element-ui 等全局注冊(cè)的組件依然要用 kebab-case 形式
- 在 tsx 中,v-model 要用 model={{ value, callback }} 寫法
- 在 tsx 中,scoped slots 要用 scopedSlots={{ foo: (scope) => () }} 寫法
- defineComponent 并不適用于函數(shù)式組件,應(yīng)使用 RenderContext 解決
到此這篇關(guān)于淺談Vue3 defineComponent有什么作用的文章就介紹到這了,更多相關(guān)Vue3 defineComponent作用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue項(xiàng)目部署到nginx/tomcat服務(wù)器的實(shí)現(xiàn)
這篇文章主要介紹了vue項(xiàng)目部署到nginx/tomcat服務(wù)器的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-08-08Vue組件間的通信pubsub-js實(shí)現(xiàn)步驟解析
這篇文章主要介紹了Vue組件間的通信pubsub-js實(shí)現(xiàn)原理解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-03-03vue?parseHTML?函數(shù)源碼解析AST基本形成
這篇文章主要為大家介紹了vue?parseHTML?函數(shù)源碼解析AST基本形成,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-07-07Vue3實(shí)現(xiàn)簡(jiǎn)約型側(cè)邊欄的示例代碼
本文主要介紹了Vue3實(shí)現(xiàn)簡(jiǎn)約型側(cè)邊欄的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-07-07vue使用Split封裝通用拖拽滑動(dòng)分隔面板組件
這篇文章主要介紹了vue使用Split封裝通用拖拽滑動(dòng)分隔面板組件,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03vue中element-ui組件默認(rèn)css樣式修改的四種方式
在前端項(xiàng)目中會(huì)運(yùn)用各種組件,有時(shí)組件的默認(rèn)樣式并不是你項(xiàng)目中所需要的,你需要更改樣式,下面這篇文章主要給大家介紹了關(guān)于vue中element-ui組件默認(rèn)css樣式修改的四種方式,需要的朋友可以參考下2021-10-10VUE中使用滾動(dòng)組件-vueSeamlessScroll
這篇文章主要介紹了VUE中使用滾動(dòng)組件-vueSeamlessScroll,需要的朋友可以參考下2023-10-10vue監(jiān)聽(tīng)用戶輸入和點(diǎn)擊功能
這篇文章主要為大家詳細(xì)介紹了vue監(jiān)聽(tīng)用戶輸入和點(diǎn)擊功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-09-09