<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          摸透Vue3中的13個(gè)全局Api,讓你的代碼更優(yōu)雅!

          共 15894字,需瀏覽 32分鐘

           ·

          2021-11-15 20:24


          來(lái)源:Tz

          https://juejin.cn/post/6979394726927532068

          不知不覺(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ù):HostNodeHostElement,對(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)容

          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                    <th id="afajh"><progress id="afajh"></progress></th>
                    俺去听听婷婷 | 亚洲色婷婷视频 | 黄色成人视频免费看 | 亚洲天堂视频网 | 午夜福利无码视频 |