摸透Vue3中的13個(gè)全局Api,讓你的代碼更優(yōu)雅!
來(lái)源:Tz
不知不覺(jué)Vue-next[1]的版本已經(jīng)來(lái)到了3.1.2,最近對(duì)照著源碼學(xué)習(xí)Vue3的全局Api,邊學(xué)習(xí)邊整理了下來(lái),希望可以和大家一起進(jìn)步。
我們以官方定義、用法、源碼淺析三個(gè)維度來(lái)一起看看它們。
下文是關(guān)于Vue3全局Api的內(nèi)容,大家如果有更好的理解和想法,可以在評(píng)論區(qū)留言,每條我都會(huì)回復(fù)~
全局API
全局API是直接在Vue上掛載方法,在Vue中,全局API一共有13個(gè)。分別是:
createapp返回一個(gè)提供應(yīng)用上下文的應(yīng)用實(shí)例; h返回一個(gè)”虛擬節(jié)點(diǎn); definecomponent返回options的對(duì)象,在TS下,會(huì)給予組件正確的參數(shù)類(lèi)型推斷; defineasynccomponent創(chuàng)建一個(gè)只有在需要時(shí)才會(huì)加載的異步組件; resolvecomponent按傳入的組件名稱(chēng)解析 component; resolvedynamiccomponent返回已解析的Component或新建的VNode; resolvedirective通過(guò)其名稱(chēng)解析一個(gè) directive; withdirectives返回一個(gè)包含應(yīng)用指令的 VNode; createrenderer跨平臺(tái)自定義渲染; nexttick是將回調(diào)函數(shù)延遲在下一次dom更新數(shù)據(jù)后調(diào)用; mergeprops將包含 VNode prop 的多個(gè)對(duì)象合并為一個(gè)單獨(dú)的對(duì)象; usecssmodule訪問(wèn) CSS 模塊; version查看已安裝的 Vue 的版本號(hào);
createApp
官方定義:返回一個(gè)提供應(yīng)用上下文的應(yīng)用實(shí)例。應(yīng)用實(shí)例掛載的整個(gè)組件樹(shù)共享同一個(gè)上下文。
顧名思義,CreateApp 作為 vue 的啟動(dòng)函數(shù),返回一個(gè)應(yīng)用實(shí)例,每個(gè) Vue 應(yīng)用程序都首先使用以下函數(shù)創(chuàng)建一個(gè)新的應(yīng)用程序?qū)嵗?/strong>,應(yīng)用程序?qū)嵗_(kāi)的大多數(shù)方法都返回相同的實(shí)例,可以鏈?zhǔn)秸{(diào)用。例如:
Vue.createApp({}).component('SearchInput',?SearchInputComponent)
復(fù)制代碼
用法
第一個(gè)參數(shù): 接收一個(gè)根組件選項(xiàng)
第二個(gè)參數(shù): 將根 prop 傳遞給應(yīng)用程序
//?用法示例
import?{?createApp,?h,?nextTick?}?from?'vue'
const?app?=?createApp({
??data()?{
????return?{
??????...
????}
??},
??methods:?{...},
??computed:?{...}
??...
},
????{?username:?'Evan'?})
復(fù)制代碼
源碼淺析
GitHub地址:
createApp():56行 - 102行內(nèi)容 \[1\][2] ensureRenderer():35 行- 37行內(nèi)容 \[2\][3] createRenderer():419 行- 424行內(nèi)容 \[3\][4] baseCreateRenderer():448 行- 2418行 \[4\][5] app._component:174行\(zhòng)[5\][6]
//?源碼位置上方[1]
export?const?createApp?=?((...args)?=>?{
????//?使用ensureRenderer().createApp()?來(lái)創(chuàng)建?app?對(duì)象
????//?源碼位置上方[2]
????//?->?ensureRenderer方法調(diào)用了來(lái)自runtime-core的createRenderer
????//?源碼位置上方[3]
????//?-> createRenderer(HostNode, HostElement),兩個(gè)通用參數(shù)HostNode(主機(jī)環(huán)境中的節(jié)點(diǎn))和HostElement(宿主環(huán)境中的元素),對(duì)應(yīng)于宿主環(huán)境。
????//?-> reateRenderer(使用(可選的)選項(xiàng)創(chuàng)建一個(gè) Renderer 實(shí)例。),該方法返回了 baseCreateRenderer
????//?源碼位置上方[4]
????//?-> baseCreateRenderer方法最終返回 render hydrate createApp三個(gè)函數(shù),生成的 render 傳給 createAppAPI ,hydrate 為可選參數(shù),ssr 的場(chǎng)景下會(huì)用到;
??const?app?=?ensureRenderer().createApp(...args)
??if?(__DEV__)?{
?????//?DEV環(huán)境下,用于組件名稱(chēng)驗(yàn)證是否是原生標(biāo)簽或者svg屬性標(biāo)簽
????injectNativeTagCheck(app)
?????//?DEV環(huán)境下,檢查CompilerOptions如果有已棄用的屬性,顯示警告
????injectCompilerOptionsCheck(app)
??}
??const?{?mount?}?=?app
??//?從創(chuàng)建的app對(duì)象中解構(gòu)獲取mount,改寫(xiě)mount方法后?返回app實(shí)例
??app.mount?=?(containerOrSelector:?Element?|?ShadowRoot?|?string):?any?=>?{
????//?container?是真實(shí)的?DOM?元素,normalizeContainer方法使用document.querySelector處理傳入的參數(shù),如果在DEV環(huán)境下元素不存在?或者?元素為影子DOM并且mode狀態(tài)為closed,則返回相應(yīng)的警告?
????const?container?=?normalizeContainer(containerOrSelector)
????//?如果不是真實(shí)的DOM元素則?return
????if?(!container)?return
?
?????//?這里的app._component?其實(shí)就是全局API的createApp的第一個(gè)參數(shù),源碼位置在上方[5]
????const?component?=?app._component
????//?component不是函數(shù)?并且?沒(méi)有不包含render、template
????if?(!isFunction(component)?&&?!component.render?&&?!component.template)?{
??????//?不安全的情況
??????//?原因:可能在dom模板中執(zhí)行JS表達(dá)式。
??????//?用戶必須確保內(nèi)dom模板是可信的。如果它是
??????//?模板不應(yīng)該包含任何用戶數(shù)據(jù)。
????????
???????//??使用?DOM的innerHTML作為component.template?內(nèi)容
??????component.template?=?container.innerHTML
??????//?2.掛載前檢查,獲得元素屬性的集合遍歷如果name不是v-cloak狀態(tài)?并且屬性名稱(chēng)包含v-、:、@?,會(huì)給出vue文檔鏈接提示
??????if?(__COMPAT__?&&?__DEV__)?{
????????for?(let?i?=?0;?i???????????const?attr?=?container.attributes[i]
??????????if?(attr.name?!==?'v-cloak'?&&?/^(v-|:|@)/.test(attr.name))?{
????????????compatUtils.warnDeprecation(
??????????????DeprecationTypes.GLOBAL_MOUNT_CONTAINER,
??????????????null
????????????)
????????????break
??????????}
????????}
??????}
????}
????//?掛載前清除內(nèi)容
????container.innerHTML?=?''
????//?真正的掛載?(元素,?是否復(fù)用[此處個(gè)人理解,僅供參考],是否為SVG元素)
????const?proxy?=?mount(container,?false,?container?instanceof?SVGElement)
????if?(container?instanceof?Element)?{
??????//?刪除元素上的?v-cloak?指令
??????container.removeAttribute('v-cloak')
??????//?設(shè)置data-v-app屬性
??????container.setAttribute('data-v-app',?'')
????}
????return?proxy
??}
??return?app
})?as?CreateAppFunction
復(fù)制代碼
h
官方定義:返回一個(gè)”虛擬節(jié)點(diǎn)“,通常縮寫(xiě)為VNode:一個(gè)普通對(duì)象,其中包含向 Vue 描述它應(yīng)在頁(yè)面上渲染哪種節(jié)點(diǎn)的信息,包括所有子節(jié)點(diǎn)的描述。它的目的是用于手動(dòng)編寫(xiě)的渲染函數(shù);
h是什么意思?根據(jù)祖師爺?shù)幕貜?fù),h 的含義如下:
It comes from the term "hyperscript", which is commonly used in many virtual-dom implementations. "Hyperscript" itself stands for "script that generates HTML structures" because HTML is the acronym for "hyper-text markup language".
它來(lái)自術(shù)語(yǔ)“hyperscript”,該術(shù)語(yǔ)常用于許多虛擬 dom 實(shí)現(xiàn)。“Hyperscript”本身代表“生成 HTML 結(jié)構(gòu)的腳本”,因?yàn)?HTML 是“超文本標(biāo)記語(yǔ)言”的首字母縮寫(xiě)詞。
回復(fù)出處:github.com/vuejs/babel…[7]
其實(shí)h()函數(shù)和createVNode()函數(shù)都是創(chuàng)建dom節(jié)點(diǎn),他們的作用是一樣的,但是在VUE3中createVNode()函數(shù)的功能比h()函數(shù)要多且做了性能優(yōu)化,渲染節(jié)點(diǎn)的速度也更快。
用法
第一個(gè)參數(shù):HTML 標(biāo)簽名、組件、異步組件或函數(shù)式組件。使用返回 null 的函數(shù)將渲染一個(gè)注釋。此參數(shù)是必需的。
第二個(gè)參數(shù):一個(gè)對(duì)象,與我們將在模板中使用的 attribute、prop、class 和、style和事件相對(duì)應(yīng)。可選。
第三個(gè)參數(shù):子代 VNode,使用
h()生成,或者使用字符串來(lái)獲取“文本 VNode”,或帶有插槽的對(duì)象。可選。//?用法示例
h('div',?{},?[
'Some?text?comes?first.',
h('h1',?'A?headline'),
h(MyComponent,?{
??someProp:?'foobar'
})
])
復(fù)制代碼
源碼淺析
GitHub地址:
h:174行 - 196行 \[6\][8]
//?源碼位置見(jiàn)上方[6]
export?function?h(type:?any,?propsOrChildren?:?any,?children?:?any):?VNode?{
??const?l?=?arguments.length
??//?如果參數(shù)是兩個(gè)
??if?(l?===?2)?{
??????//?判斷是否是對(duì)象,并且不為數(shù)組
????if?(isObject(propsOrChildren)?&&?!isArray(propsOrChildren))?{
??????//?所有VNode對(duì)象都有一個(gè)?__v_isVNode 屬性,isVNode 方法也是根據(jù)這個(gè)屬性來(lái)判斷是否為VNode對(duì)象。
??????if?(isVNode(propsOrChildren))?{
????????return?createVNode(type,?null,?[propsOrChildren])
??????}
??????//?只包含屬性不含有子元素??
??????return?createVNode(type,?propsOrChildren)
????}?else?{
??????//?忽略props屬性?
??????return?createVNode(type,?null,?propsOrChildren)
????}
??}?else?{
????if?(l?>?3)?{
??????//?Array.prototype.slice.call(arguments,?2),這句話的意思就是說(shuō)把調(diào)用方法的參數(shù)截取出來(lái),可以理解成是讓arguments轉(zhuǎn)換成一個(gè)數(shù)組對(duì)象,讓arguments具有slice()方法
??????children?=?Array.prototype.slice.call(arguments,?2)
????}?else?if?(l?===?3?&&?isVNode(children))?{
??????//?如果參數(shù)長(zhǎng)度等于3,并且第三個(gè)參數(shù)為VNode對(duì)象
??????children?=?[children]
????}
????//?h?函數(shù)內(nèi)部的主要處理邏輯就是根據(jù)參數(shù)個(gè)數(shù)和參數(shù)類(lèi)型,執(zhí)行相應(yīng)處理操作,但最終都是通過(guò)調(diào)用?createVNode?函數(shù)來(lái)創(chuàng)建?VNode?對(duì)象
????return?createVNode(type,?propsOrChildren,?children)
??}
}
復(fù)制代碼
defineComponent
官方定義:defineComponent只返回傳遞給它的對(duì)象。但是,就類(lèi)型而言,返回的值有一個(gè)合成類(lèi)型的構(gòu)造函數(shù),用于手動(dòng)渲染函數(shù)、TSX 和 IDE 工具支持
definComponent主要是用來(lái)幫助Vue在TS下正確推斷出setup()組件的參數(shù)類(lèi)型
引入 defineComponent() 以正確推斷 setup() 組件的參數(shù)類(lèi)型;
defineComponent 可以正確適配無(wú) props、數(shù)組 props 等形式;
用法
**參數(shù):**具有組件選項(xiàng)的對(duì)象或者是一個(gè)
setup函數(shù),函數(shù)名稱(chēng)將作為組件名稱(chēng)來(lái)使用//?之前寫(xiě)Ts + vue,需要聲明相關(guān)的數(shù)據(jù)類(lèi)型。如下
//?聲明props和return的數(shù)據(jù)類(lèi)型
interface?Data?{
[key:?string]:?unknown
}
//?使用的時(shí)候入?yún)⒁由下暶鳎瑀eturn也要加上聲明
export?default?{
setup(props:?Data):?Data?{
??//?...
??return?{
????//?...
??}
}
}
//?非常的繁瑣,使用defineComponent 之后,就可以省略這些類(lèi)型定義,defineComponent 可以接受顯式的自定義props接口或從屬性驗(yàn)證對(duì)象中自動(dòng)推斷;
//?用法示例1:
import?{?defineComponent?}?from?'vue'
const?MyComponent?=?defineComponent({
data()?{
??return?{?count:?1?}
},
methods:?{
??increment()?{
????this.count++
??}
}
})
//?用法示例2:
//?不只適用于 setup,只要是 Vue 本身的 API ,defineComponent 都可以自動(dòng)幫你推導(dǎo)。
import?{?defineComponent?}?from?'vue'
export?default?defineComponent({
setup?(props,?context)?{
??//?...
??
??return?{
????//?...
??}
}
})
復(fù)制代碼
源碼淺析
GitHub地址:源碼文件位置[9]
...
...
...
//??實(shí)際上這個(gè) api 只是直接 return 傳進(jìn)來(lái)的 options,export default defineComponent({})?是有點(diǎn)等價(jià)于export default {},目前看來(lái)這樣做的最大作用只是限制 type, setup 必須是函數(shù),props 必須是 undefined 或者?對(duì)象。
export?function?defineComponent(options:?unknown)?{
??return?isFunction(options)???{?setup:?options,?name:?options.name?}?:?options
}
復(fù)制代碼
defineAsyncComponent
官方定義:創(chuàng)建一個(gè)只有在需要時(shí)才會(huì)加載的異步組件。
用法
參數(shù):接受一個(gè)返回Promise的工廠函數(shù)。Promise 的resolve回調(diào)應(yīng)該在服務(wù)端返回組件定義后被調(diào)用。
//?在?Vue?2.x?中,聲明一個(gè)異步組件只需這樣
const?asyncModal?=?()?=>?import('./Modal.vue')
//?或者
const?asyncModal?=?{
??component:?()?=>?import('./Modal.vue'),
??delay:?200,
??timeout:?3000,
??error:?ErrorComponent,
??loading:?LoadingComponent
}
//?現(xiàn)在,在 Vue 3 中,由于函數(shù)式組件被定義為純函數(shù),因此異步組件的定義需要通過(guò)將其包裹在新的 defineAsyncComponent 助手方法中來(lái)顯式地定義:
import?{?defineAsyncComponent?}?from?'vue'
import?ErrorComponent?from?'./components/ErrorComponent.vue'
import?LoadingComponent?from?'./components/LoadingComponent.vue'
//?不帶選項(xiàng)的異步組件
const?asyncModal?=?defineAsyncComponent(()?=>?import('./Modal.vue'))
//?帶選項(xiàng)的異步組件,對(duì) 2.x 所做的另一個(gè)更改是,component 選項(xiàng)現(xiàn)在被重命名為loader,以便準(zhǔn)確地傳達(dá)不能直接提供組件定義的信息。注意:defineAsyncComponent不能使用在Vue Router上!
const?asyncModalWithOptions?=?defineAsyncComponent({
??loader:?()?=>?import('./Modal.vue'),
??delay:?200,
??timeout:?3000,
??errorComponent:?ErrorComponent,
??loadingComponent:?LoadingComponent
})
復(fù)制代碼
源碼淺析
GitHub地址:41行- 196行[10]
//?源碼位置見(jiàn)上方
export?function?defineAsyncComponent<
??T?extends?Component?=?{?new?():?ComponentPublicInstance?}
>(source:?AsyncComponentLoader?|?AsyncComponentOptions):?T?{
??????
??if?(isFunction(source))?{
????source?=?{?loader:?source?}
??}
?//?異步組件的參數(shù)
??const?{
????loader,
????loadingComponent,
????errorComponent,
????delay?=?200,
????timeout,?//?undefined?=?never?times?out
????suspensible?=?true,
????onError:?userOnError
??}?=?source
??let?pendingRequest:?Promise?|?null?=?null
??let?resolvedComp:?ConcreteComponent?|?undefined
??let?retries?=?0
??//?重新嘗試load得到組件內(nèi)容
??const?retry?=?()?=>?{
????retries++
????pendingRequest?=?null
????return?load()
??}
??const?load?=?():?Promise?=>?{
????let?thisRequest:?Promise
????return?(
??????//?如果pendingRequest?存在就return,否則實(shí)行l(wèi)oader()
??????pendingRequest?||
??????(thisRequest?=?pendingRequest?=?loader()
???????//?失敗場(chǎng)景處理
????????.catch(err?=>?{
??????????err?=?err?instanceof?Error???err?:?new?Error(String(err))
??????????if?(userOnError)?{
????????????//?對(duì)應(yīng)文檔中的?失敗捕獲回調(diào)函數(shù)?用戶使用
????????????return?new?Promise((resolve,?reject)?=>?{
??????????????const?userRetry?=?()?=>?resolve(retry())
??????????????const?userFail?=?()?=>?reject(err)
??????????????userOnError(err,?userRetry,?userFail,?retries?+?1)
????????????})
??????????}?else?{
????????????throw?err
??????????}
????????})
????????.then((comp:?any)?=>?{
??????????//?個(gè)人理解:在thisRequest = pendingRequest = loader(),loader()最開(kāi)始屬于等待狀態(tài),賦值給pendingRequest、在thisRequest此刻他們是相等的等待狀態(tài),當(dāng)進(jìn)入then的時(shí)候pendingRequest已經(jīng)發(fā)生了改變,所以返回pendingRequest
??????????if?(thisRequest?!==?pendingRequest?&&?pendingRequest)?{
????????????return?pendingRequest
??????????}
??????????//?如果在DEV環(huán)境則警告
??????????if?(__DEV__?&&?!comp)?{
????????????warn(
??????????????`Async?component?loader?resolved?to?undefined.?`?+
????????????????`If?you?are?using?retry(),?make?sure?to?return?its?return?value.`
????????????)
??????????}
??????????//?interop?module?default
??????????if?(
????????????comp?&&
????????????(comp.__esModule?||?comp[Symbol.toStringTag]?===?'Module')
??????????)?{
????????????comp?=?comp.default
??????????}
??????????//?如果在DEV環(huán)境則警告
??????????if?(__DEV__?&&?comp?&&?!isObject(comp)?&&?!isFunction(comp))?{
????????????throw?new?Error(`Invalid?async?component?load?result:?${comp}`)
??????????}
??????????resolvedComp?=?comp
??????????return?comp
????????}))
????)
??}
??return?defineComponent({
????__asyncLoader:?load,
????//?異步組件統(tǒng)一名字
????name:?'AsyncComponentWrapper',
????//?組件有setup方法的走setup邏輯
????setup()?{
??????const?instance?=?currentInstance!
??????//?already?resolved
??????if?(resolvedComp)?{
????????return?()?=>?createInnerComp(resolvedComp!,?instance)
??????}
??????const?onError?=?(err:?Error)?=>?{
????????pendingRequest?=?null
????????handleError(
??????????err,
??????????instance,
??????????ErrorCodes.ASYNC_COMPONENT_LOADER,
??????????!errorComponent?/*?do?not?throw?in?dev?if?user?provided?error?component?*/
????????)
??????}
??????//?suspense-controlled?or?SSR.
??????//?對(duì)應(yīng)文檔中如果父組件是一個(gè)?suspense?那么只返回promise結(jié)果?其余的控制交給?suspense?處理即可
??????if?(
????????(__FEATURE_SUSPENSE__?&&?suspensible?&&?instance.suspense)?||
????????(__NODE_JS__?&&?isInSSRComponentSetup)
??????)?{
????????return?load()
??????????.then(comp?=>?{
????????????return?()?=>?createInnerComp(comp,?instance)
??????????})
??????????.catch(err?=>?{
????????????onError(err)
????????????return?()?=>
??????????????errorComponent
??????????????????createVNode(errorComponent?as?ConcreteComponent,?{
????????????????????error:?err
??????????????????})
????????????????:?null
??????????})
??????}
??????const?loaded?=?ref(false)
??????const?error?=?ref()
??????const?delayed?=?ref(!!delay)
??????if?(delay)?{
????????setTimeout(()?=>?{
??????????delayed.value?=?false
????????},?delay)
??????}
??????if?(timeout?!=?null)?{
????????setTimeout(()?=>?{
??????????if?(!loaded.value?&&?!error.value)?{
????????????const?err?=?new?Error(
??????????????`Async?component?timed?out?after?${timeout}ms.`
????????????)
????????????onError(err)
????????????error.value?=?err
??????????}
????????},?timeout)
??????}
??????load()
????????.then(()?=>?{
??????????//?promise成功返回后觸發(fā)trigger導(dǎo)致組件更新?重新渲染組件?只不過(guò)此時(shí)我們已經(jīng)得到組件內(nèi)容
??????????loaded.value?=?true
????????})
????????.catch(err?=>?{
??????????onError(err)
??????????error.value?=?err
????????})
??????//?返回的函數(shù)會(huì)被當(dāng)做組件實(shí)例的?render?函數(shù)
??????return?()?=>?{
????????//?render初始執(zhí)行觸發(fā)?loaded的依賴(lài)收集?
????????if?(loaded.value?&&?resolvedComp)?{
??????????return?createInnerComp(resolvedComp,?instance)
????????}?else?if?(error.value?&&?errorComponent)?{
??????????return?createVNode(errorComponent?as?ConcreteComponent,?{
????????????error:?error.value
??????????})
????????}?else?if?(loadingComponent?&&?!delayed.value)?{
??????????return?createVNode(loadingComponent?as?ConcreteComponent)
????????}
??????}
????}
??})?as?any
}
復(fù)制代碼
resolveComponent
官方定義:如果在當(dāng)前應(yīng)用實(shí)例中可用,則允許按名稱(chēng)解析component,返回一個(gè)Component。如果沒(méi)有找到,則返回接收的參數(shù)name。
用法
參數(shù):已加載的組件的名稱(chēng)
const?app?=?createApp({})
app.component('MyComponent',?{
??/*?...?*/
})
import?{?resolveComponent?}?from?'vue'
render()?{
??const?MyComponent?=?resolveComponent('MyComponent')
}
復(fù)制代碼
源碼淺析
GitHub地址:
resolveComponent():21行- 27行 \[7\][11] resolveAsset():62行- 123行 \[8\][12]
//?接收一個(gè)name參數(shù),主要還是在resolveAsset方法中做了處理,源碼位置見(jiàn)上方[7]
export?function?resolveComponent(
??name:?string,
??maybeSelfReference?:?boolean
):?ConcreteComponent?|?string?{
??return?resolveAsset(COMPONENTS,?name,?true,?maybeSelfReference)?||?name
}
//?resolveAsset源碼在上方地址[8]
function?resolveAsset(
??type:?AssetTypes,
??name:?string,
??warnMissing?=?true,
??maybeSelfReference?=?false
)?{
??//?尋找當(dāng)前渲染實(shí)例,不存在則為當(dāng)前實(shí)例
??const?instance?=?currentRenderingInstance?||?currentInstance
??if?(instance)?{
????const?Component?=?instance.type
????//?自我名稱(chēng)具有最高的優(yōu)先級(jí)
????if?(type?===?COMPONENTS)?{
??????//?getComponentName?首先判斷傳入的Component參數(shù)是不是函數(shù),如果是函數(shù)優(yōu)先使用.displayName屬性,其次使用.name
??????const?selfName?=?getComponentName(Component)
??????if?(
????????//?camelize?使用replace方法,正則/-(\w)/gname,匹配后toUpperCase()?轉(zhuǎn)換成大寫(xiě)
????????// capitalize函數(shù):str.charAt(0).toUpperCase()?+ str.slice(1)?首字母大寫(xiě)?+?處理后的字符
????????selfName?&&
????????(selfName?===?name?||
??????????selfName?===?camelize(name)?||
??????????selfName?===?capitalize(camelize(name)))
??????)?{
????????return?Component
??????}
????}
????const?res?=
??????//?注冊(cè)
??????//?首先檢查實(shí)例[type],它被解析為選項(xiàng)API
??????resolve(instance[type]?||?(Component?as?ComponentOptions)[type],?name)?||
??????//?全局注冊(cè)
??????resolve(instance.appContext[type],?name)
????if?(!res?&&?maybeSelfReference)?{
??????return?Component
????}
????if?(__DEV__?&&?warnMissing?&&?!res)?{
??????warn(`Failed?to?resolve?${type.slice(0,?-1)}:?${name}`)
????}
????return?res
??}?else?if?(__DEV__)?{
????//?如果實(shí)例不存在,并且在DEV環(huán)境警告:can only be used in render() or setup()
????warn(
??????`resolve${capitalize(type.slice(0,?-1))}?`?+
????????`can?only?be?used?in?render()?or?setup().`
????)
??}
}
復(fù)制代碼
resolveDynamicComponent
官方定義:返回已解析的Component或新創(chuàng)建的VNode,其中組件名稱(chēng)作為節(jié)點(diǎn)標(biāo)簽。如果找不到Component,將發(fā)出警告。
用法
參數(shù):接受一個(gè)參數(shù):component
import?{?resolveDynamicComponent?}?from?'vue'
render?()?{
??const?MyComponent?=?resolveDynamicComponent('MyComponent')
}
復(fù)制代碼
源碼淺析
GitHub地址:
resolveDirective():43行 - 48行內(nèi)容 \[9\][13] resolveAsset():62行- 123行[14]
//?源碼位置位于上方[9]位置處
//?根據(jù)該函數(shù)的名稱(chēng),我們可以知道它用于解析動(dòng)態(tài)組件,在?resolveDynamicComponent?函數(shù)內(nèi)部,若?component?參數(shù)是字符串類(lèi)型,則會(huì)調(diào)用前面介紹的?resolveAsset?方法來(lái)解析組件,
//?如果 resolveAsset 函數(shù)獲取不到對(duì)應(yīng)的組件,則會(huì)返回當(dāng)前 component 參數(shù)的值。比如 resolveDynamicComponent('div')?將返回?'div'?字符串
//?源碼見(jiàn)上方[1]地址
export?function?resolveDynamicComponent(component:?unknown):?VNodeTypes?{
??if?(isString(component))?{
????return?resolveAsset(COMPONENTS,?component,?false)?||?component
??}?else?{
????//?無(wú)效類(lèi)型將引發(fā)警告,如果 component 參數(shù)非字符串類(lèi)型,則會(huì)返回 component || NULL_DYNAMIC_COMPONENT 這行語(yǔ)句的執(zhí)行結(jié)果,其中 NULL_DYNAMIC_COMPONENT 的值是一個(gè) Symbol 對(duì)象。
????return?(component?||?NULL_DYNAMIC_COMPONENT)?as?any
??}
}
//??resolveAsset函數(shù)解析見(jiàn)上方[8]位置處
復(fù)制代碼
resolveDirective
如果在當(dāng)前應(yīng)用實(shí)例中可用,則允許通過(guò)其名稱(chēng)解析一個(gè)directive。返回一個(gè)Directive。如果沒(méi)有找到,則返回undefined。
用法
第一個(gè)參數(shù):已加載的指令的名稱(chēng)。
源碼淺析
GitHub地址:
resolveDirective():43行 - 48行內(nèi)容 \[10\][15] resolveAsset():62行- 123行[16]
/**
?*?源碼位置見(jiàn)上方[10]位置處
?*/
export?function?resolveDirective(name:?string):?Directive?|?undefined?{
??//?然后調(diào)用前面介紹的?resolveAsset?方法來(lái)解析組件,resolveAsset函數(shù)解析見(jiàn)上方[8]位置處
??return?resolveAsset(DIRECTIVES,?name)
}
復(fù)制代碼
withDirectives
官方定義:允許將指令應(yīng)用于VNode。返回一個(gè)包含應(yīng)用指令的 VNode。
用法
第一個(gè)參數(shù):一個(gè)虛擬節(jié)點(diǎn),通常使用
h()創(chuàng)建第二個(gè)參數(shù):一個(gè)指令數(shù)組,每個(gè)指令本身都是一個(gè)數(shù)組,最多可以定義 4 個(gè)索引。
import?{?withDirectives,?resolveDirective?}?from?'vue'
const?foo?=?resolveDirective('foo')
const?bar?=?resolveDirective('bar')
return?withDirectives(h('div'),?[
??[foo,?this.x],
??[bar,?this.y]
])
復(fù)制代碼
源碼淺析
GitHub地址:
resolveDirective():85行 - 114內(nèi)容 \[11\][17]
//?源碼鏈接在上方[11]位置處
export?function?withDirectives<T?extends?VNode>(
??vnode:?T,
??directives:?DirectiveArguments
):?T?{
??//?獲取當(dāng)前實(shí)例
??const?internalInstance?=?currentRenderingInstance
??if?(internalInstance?===?null)?{
????//?如果在 render 函數(shù)外面使用 withDirectives()?則會(huì)拋出異常:
????__DEV__?&&?warn(`withDirectives?can?only?be?used?inside?render?functions.`)
????return?vnode
??}
??const?instance?=?internalInstance.proxy
??//?在?vnode?上綁定?dirs?屬性,并且遍歷傳入的?directives?數(shù)組
??const?bindings:?DirectiveBinding[]?=?vnode.dirs?||?(vnode.dirs?=?[])
??for?(let?i?=?0;?i?????let?[dir,?value,?arg,?modifiers?=?EMPTY_OBJ]?=?directives[i]
????if?(isFunction(dir))?{
??????dir?=?{
????????mounted:?dir,
????????updated:?dir
??????}?as?ObjectDirective
????}
????bindings.push({
??????dir,
??????instance,
??????value,
??????oldValue:?void?0,
??????arg,
??????modifiers
????})
??}
??return?vnode
}
復(fù)制代碼
createRenderer
官方定義:createRenderer 函數(shù)接受兩個(gè)泛型參數(shù):HostNode和HostElement,對(duì)應(yīng)于宿主環(huán)境中的 Node 和 Element 類(lèi)型。
用法
第一個(gè)參數(shù):HostNode宿主環(huán)境中的節(jié)點(diǎn)。 第二個(gè)參數(shù):Element宿主環(huán)境中的元素。
//?對(duì)于 runtime-dom,HostNode 將是 DOM Node 接口,HostElement 將是 DOM Element 接口。
//?自定義渲染器可以傳入特定于平臺(tái)的類(lèi)型,如下所示:
// createRenderer(HostNode, HostElement),兩個(gè)通用參數(shù)HostNode(主機(jī)環(huán)境中的節(jié)點(diǎn))和HostElement(宿主環(huán)境中的元素),對(duì)應(yīng)于宿主環(huán)境。
// reateRenderer(使用(可選的)選項(xiàng)創(chuàng)建一個(gè) Renderer 實(shí)例。),該方法返回了 baseCreateRenderer
export?function?createRenderer<
??HostNode?=?RendererNode,
??HostElement?=?RendererElement
>(options:?RendererOptions )?{
??return?baseCreateRenderer(options)
}
復(fù)制代碼
源碼解析
createRenderer():419 行- 424行內(nèi)容 \[3\][18] baseCreateRenderer():448 行- 2418行 \[4\][19]
export?function?createRenderer<
??HostNode?=?RendererNode,
??HostElement?=?RendererElement
>(options:?RendererOptions )?{
??return?baseCreateRenderer(options)
}
// baseCreateRenderer這個(gè)放2000行的左右的代碼量,這里就完整不貼過(guò)來(lái)了,里面是渲染的核心代碼,從平臺(tái)特性 options 取出相關(guān) API,實(shí)現(xiàn)了 patch、處理節(jié)點(diǎn)、處理組件、更新組件、安裝組件實(shí)例等等方法,最終返回了一個(gè)renderer對(duì)象。
function?baseCreateRenderer(
??options:?RendererOptions,
??createHydrationFns?:?typeof?createHydrationFunctions
):?any?{
??//?compile-time?feature?flags?check
??if?(__ESM_BUNDLER__?&&?!__TEST__)?{
????initFeatureFlags()
??}
??if?(__DEV__?||?__FEATURE_PROD_DEVTOOLS__)?{
????const?target?=?getGlobalThis()
????target.__VUE__?=?true
????setDevtoolsHook(target.__VUE_DEVTOOLS_GLOBAL_HOOK__)
??}
??const?{
????insert:?hostInsert,
????remove:?hostRemove,
????patchProp:?hostPatchProp,
????forcePatchProp:?hostForcePatchProp,
????createElement:?hostCreateElement,
????createText:?hostCreateText,
????createComment:?hostCreateComment,
????setText:?hostSetText,
????setElementText:?hostSetElementText,
????parentNode:?hostParentNode,
????nextSibling:?hostNextSibling,
????setScopeId:?hostSetScopeId?=?NOOP,
????cloneNode:?hostCloneNode,
????insertStaticContent:?hostInsertStaticContent
??}?=?options
?...
?...
????...
??//?返回 render hydrate createApp三個(gè)函數(shù),生成的 render 傳給 createAppAPI ,hydrate 為可選參數(shù),ssr 的場(chǎng)景下會(huì)用到;
??return?{
????render,
????hydrate,
????createApp:?createAppAPI(render,?hydrate)
??}
}
復(fù)制代碼
nextTick
官方定義:將回調(diào)推遲到下一個(gè) DOM 更新周期之后執(zhí)行。在更改了一些數(shù)據(jù)以等待 DOM 更新后立即使用它。
import?{?createApp,?nextTick?}?from?'vue'
const?app?=?createApp({
??setup()?{
????const?message?=?ref('Hello!')
????const?changeMessage?=?async?newMessage?=>?{
??????message.value?=?newMessage
??????await?nextTick()
??????console.log('Now?DOM?is?updated')
????}
??}
})
復(fù)制代碼
源碼淺析
GitHub地址:
nextTick():42行 - 48行內(nèi)容[20]
//?源碼位置在上方
//?這里直接創(chuàng)建一個(gè)異步任務(wù),但是改變dom屬性也是異步策略,怎么保證dom加載完成
//?Vue2.x是?會(huì)判斷瀏覽器是否支持promise屬性?->?是否支持MutationObserver?->?是否支持setImmediate??->?都不支持使用setTimeout,Vue3不再支持IE11,所以nextTick直接使用Promise
// Vue 異步執(zhí)行 DOM 更新。只要觀察到數(shù)據(jù)變化,Vue 將開(kāi)啟一個(gè)隊(duì)列,并緩沖在同一事件循環(huán)中發(fā)生的所有數(shù)據(jù)改變。如果同一個(gè) watcher 被多次觸發(fā),只會(huì)被推入到隊(duì)列中一次。這種在緩沖時(shí)去除重復(fù)數(shù)據(jù)對(duì)于避免不必要的計(jì)算和 DOM 操作上非常重要。然后,在下一個(gè)的事件循環(huán)“tick”中,Vue 刷新隊(duì)列并執(zhí)行實(shí)際?(已去重的)?工作。
export?function?nextTick(
??this:?ComponentPublicInstance?|?void,
??fn?:?()?=>?void
):?Promise<void>?{
??const?p?=?currentFlushPromise?||?resolvedPromise
??return?fn???p.then(this???fn.bind(this)?:?fn)?:?p
}
//?你設(shè)置vm.someData =?'new value',該組件不會(huì)立即重新渲染。當(dāng)刷新隊(duì)列時(shí),組件會(huì)在事件循環(huán)隊(duì)列清空時(shí)的下一個(gè)“tick”更新。如果你想在 DOM 狀態(tài)更新后做點(diǎn)什?,可以在數(shù)據(jù)變化之后立即使用Vue.nextTick(callback)?。
復(fù)制代碼
mergeProps
官方定義:將包含 VNode prop 的多個(gè)對(duì)象合并為一個(gè)單獨(dú)的對(duì)象。其返回的是一個(gè)新創(chuàng)建的對(duì)象,而作為參數(shù)傳遞的對(duì)象則不會(huì)被修改。
用法
參數(shù):可以傳遞不限數(shù)量的對(duì)象
import?{?h,?mergeProps?}?from?'vue'
export?default?{
??inheritAttrs:?false,
??render()?{
????const?props?=?mergeProps({
??????//?該 class 將與?$attrs 中的其他 class 合并。
??????class:?'active'
????},?this.$attrs)
????return?h('div',?props)
??}
}
復(fù)制代碼
源碼淺析
GitHub地址:
mergeProps():687行 - 712行[21]
export?function?mergeProps(...args:?(Data?&?VNodeProps)[])?{
??//?extend就是Object.assign方法,?ret合并第一個(gè)參數(shù)為對(duì)象
??const?ret?=?extend({},?args[0])
??//?遍歷args參數(shù)
??for?(let?i?=?1;?i?????const?toMerge?=?args[i]
????for?(const?key?in?toMerge)?{
??????if?(key?===?'class')?{
????????//?合并class
????????if?(ret.class?!==?toMerge.class)?{
??????????ret.class?=?normalizeClass([ret.class,?toMerge.class])
????????}
??????}?else?if?(key?===?'style')?{
????????//?合并style
????????ret.style?=?normalizeStyle([ret.style,?toMerge.style])
??????}?else?if?(isOn(key))?{、
???????//?判斷是不是以?on開(kāi)頭的
????????const?existing?=?ret[key]
????????const?incoming?=?toMerge[key]
????????if?(existing?!==?incoming)?{
??????????//?如果第一個(gè)參數(shù)中不存在,則合并,否則新增
??????????ret[key]?=?existing
??????????????[].concat(existing?as?any,?incoming?as?any)
????????????:?incoming
????????}
??????}?else?if?(key?!==?'')?{
????????//?key不為空則添加屬性
????????ret[key]?=?toMerge[key]
??????}
????}
??}
??return?ret
}
復(fù)制代碼
useCssModule
官方定義:允許在`setup`[22]的單文件組件[23]函數(shù)中訪問(wèn) CSS 模塊。
用法
參數(shù):CSS 模塊的名稱(chēng)。默認(rèn)為 '$style'
// useCssModule 只能在 render 或 setup 函數(shù)中使用。
//?這里的name不止可以填寫(xiě)$style,
/*
*
*/
//?這樣就可以使用?const?style?=?useCssModule(‘a(chǎn)aa'),來(lái)獲取相應(yīng)內(nèi)容
俺去听听婷婷
|
亚洲色婷婷视频
|
黄色成人视频免费看
|
亚洲天堂视频网
|
午夜福利无码视频
|
