1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

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

        共 15894字,需瀏覽 32分鐘

         ·

        2021-11-15 20:24


        來源:Tz

        https://juejin.cn/post/6979394726927532068

        不知不覺Vue-next[1]的版本已經(jīng)來到了3.1.2,最近對照著源碼學(xué)習(xí)Vue3的全局Api,邊學(xué)習(xí)邊整理了下來,希望可以和大家一起進(jìn)步。

        我們以官方定義、用法、源碼淺析三個維度來一起看看它們。

        下文是關(guān)于Vue3全局Api的內(nèi)容,大家如果有更好的理解和想法,可以在評論區(qū)留言,每條我都會回復(fù)~

        全局API

        全局API是直接在Vue上掛載方法,在Vue中,全局API一共有13個。分別是:

        • createapp返回一個提供應(yīng)用上下文的應(yīng)用實(shí)例;
        • h返回一個”虛擬節(jié)點(diǎn);
        • definecomponent返回options的對象,在TS下,會給予組件正確的參數(shù)類型推斷;
        • defineasynccomponent創(chuàng)建一個只有在需要時才會加載的異步組件;
        • resolvecomponent按傳入的組件名稱解析 component;
        • resolvedynamiccomponent返回已解析的Component或新建的VNode;
        • resolvedirective通過其名稱解析一個 directive;
        • withdirectives返回一個包含應(yīng)用指令的 VNode;
        • createrenderer跨平臺自定義渲染;
        • nexttick是將回調(diào)函數(shù)延遲在下一次dom更新數(shù)據(jù)后調(diào)用;
        • mergeprops將包含 VNode prop 的多個對象合并為一個單獨(dú)的對象;
        • usecssmodule訪問 CSS 模塊;
        • version查看已安裝的 Vue 的版本號;

        createApp

        官方定義:返回一個提供應(yīng)用上下文的應(yīng)用實(shí)例。應(yīng)用實(shí)例掛載的整個組件樹共享同一個上下文。

        顧名思義,CreateApp 作為 vue 的啟動函數(shù),返回一個應(yīng)用實(shí)例,每個 Vue 應(yīng)用程序都首先使用以下函數(shù)創(chuàng)建一個新的應(yīng)用程序?qū)嵗?/strong>,應(yīng)用程序?qū)嵗_的大多數(shù)方法都返回相同的實(shí)例,可以鏈?zhǔn)秸{(diào)用。例如:

        Vue.createApp({}).component('SearchInput',?SearchInputComponent)
        復(fù)制代碼

        用法

        • 第一個參數(shù): 接收一個根組件選項(xiàng)

          • 第二個參數(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()?來創(chuàng)建?app?對象
        ????//?源碼位置上方[2]
        ????//?->?ensureRenderer方法調(diào)用了來自runtime-core的createRenderer
        ????//?源碼位置上方[3]
        ????//?-> createRenderer(HostNode, HostElement),兩個通用參數(shù)HostNode(主機(jī)環(huán)境中的節(jié)點(diǎn))和HostElement(宿主環(huán)境中的元素),對應(yīng)于宿主環(huán)境。
        ????//?-> reateRenderer(使用(可選的)選項(xiàng)創(chuàng)建一個 Renderer 實(shí)例。),該方法返回了 baseCreateRenderer
        ????//?源碼位置上方[4]
        ????//?-> baseCreateRenderer方法最終返回 render hydrate createApp三個函數(shù),生成的 render 傳給 createAppAPI ,hydrate 為可選參數(shù),ssr 的場景下會用到;
        ??const?app?=?ensureRenderer().createApp(...args)

        ??if?(__DEV__)?{
        ?????//?DEV環(huán)境下,用于組件名稱驗(yàn)證是否是原生標(biāo)簽或者svg屬性標(biāo)簽
        ????injectNativeTagCheck(app)
        ?????//?DEV環(huán)境下,檢查CompilerOptions如果有已棄用的屬性,顯示警告
        ????injectCompilerOptionsCheck(app)
        ??}

        ??const?{?mount?}?=?app
        ??//?從創(chuàng)建的app對象中解構(gòu)獲取mount,改寫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的第一個參數(shù),源碼位置在上方[5]
        ????const?component?=?app._component
        ????//?component不是函數(shù)?并且?沒有不包含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)?并且屬性名稱包含v-、:、@?,會給出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ù)用[此處個人理解,僅供參考],是否為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

        官方定義:返回一個”虛擬節(jié)點(diǎn)“,通常縮寫為VNode:一個普通對象,其中包含向 Vue 描述它應(yīng)在頁面上渲染哪種節(jié)點(diǎn)的信息,包括所有子節(jié)點(diǎn)的描述。它的目的是用于手動編寫的渲染函數(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".

        它來自術(shù)語“hyperscript”,該術(shù)語常用于許多虛擬 dom 實(shí)現(xiàn)。“Hyperscript”本身代表“生成 HTML 結(jié)構(gòu)的腳本”,因?yàn)?HTML 是“超文本標(biāo)記語言”的首字母縮寫詞。

        回復(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)的速度也更快。

        用法

        • 第一個參數(shù):HTML 標(biāo)簽名、組件、異步組件或函數(shù)式組件。使用返回 null 的函數(shù)將渲染一個注釋。此參數(shù)是必需的。

        • 第二個參數(shù):一個對象,與我們將在模板中使用的 attribute、prop、class 和、style和事件相對應(yīng)??蛇x。

        • 第三個參數(shù):子代 VNode,使用h()生成,或者使用字符串來獲取“文本 VNode”,或帶有插槽的對象??蛇x。

          //?用法示例
          h('div',?{},?[
          'Some?text?comes?first.',
          h('h1',?'A?headline'),
          h(MyComponent,?{
          ??someProp:?'foobar'
          })
          ])
          復(fù)制代碼

        源碼淺析

        GitHub地址:

        • h:174行 - 196行 \[6\][8]
        //?源碼位置見上方[6]
        export?function?h(type:?any,?propsOrChildren?:?any,?children?:?any):?VNode?{
        ??const?l?=?arguments.length
        ??//?如果參數(shù)是兩個
        ??if?(l?===?2)?{
        ??????//?判斷是否是對象,并且不為數(shù)組
        ????if?(isObject(propsOrChildren)?&&?!isArray(propsOrChildren))?{
        ??????//?所有VNode對象都有一個?__v_isVNode 屬性,isVNode 方法也是根據(jù)這個屬性來判斷是否為VNode對象。
        ??????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),這句話的意思就是說把調(diào)用方法的參數(shù)截取出來,可以理解成是讓arguments轉(zhuǎn)換成一個數(shù)組對象,讓arguments具有slice()方法
        ??????children?=?Array.prototype.slice.call(arguments,?2)
        ????}?else?if?(l?===?3?&&?isVNode(children))?{
        ??????//?如果參數(shù)長度等于3,并且第三個參數(shù)為VNode對象
        ??????children?=?[children]
        ????}
        ????//?h?函數(shù)內(nèi)部的主要處理邏輯就是根據(jù)參數(shù)個數(shù)和參數(shù)類型,執(zhí)行相應(yīng)處理操作,但最終都是通過調(diào)用?createVNode?函數(shù)來創(chuàng)建?VNode?對象
        ????return?createVNode(type,?propsOrChildren,?children)
        ??}
        }
        復(fù)制代碼

        defineComponent

        官方定義:defineComponent只返回傳遞給它的對象。但是,就類型而言,返回的值有一個合成類型的構(gòu)造函數(shù),用于手動渲染函數(shù)、TSX 和 IDE 工具支持

        definComponent主要是用來幫助Vue在TS下正確推斷出setup()組件的參數(shù)類型

        引入 defineComponent() 以正確推斷 setup() 組件的參數(shù)類型;

        defineComponent 可以正確適配無 props、數(shù)組 props 等形式;

        用法

        • **參數(shù):**具有組件選項(xiàng)的對象或者是一個setup函數(shù),函數(shù)名稱將作為組件名稱來使用

          //?之前寫Ts + vue,需要聲明相關(guān)的數(shù)據(jù)類型。如下
          //?聲明props和return的數(shù)據(jù)類型
          interface?Data?{
          [key:?string]:?unknown
          }
          //?使用的時候入?yún)⒁由下暶鳎瑀eturn也要加上聲明
          export?default?{
          setup(props:?Data):?Data?{
          ??//?...
          ??return?{
          ????//?...
          ??}
          }
          }
          //?非常的繁瑣,使用defineComponent 之后,就可以省略這些類型定義,defineComponent 可以接受顯式的自定義props接口或從屬性驗(yàn)證對象中自動推斷;

          //?用法示例1:
          import?{?defineComponent?}?from?'vue'

          const?MyComponent?=?defineComponent({
          data()?{
          ??return?{?count:?1?}
          },
          methods:?{
          ??increment()?{
          ????this.count++
          ??}
          }
          })

          //?用法示例2:
          //?不只適用于 setup,只要是 Vue 本身的 API ,defineComponent 都可以自動幫你推導(dǎo)。
          import?{?defineComponent?}?from?'vue'
          export?default?defineComponent({
          setup?(props,?context)?{
          ??//?...
          ??
          ??return?{
          ????//?...
          ??}
          }
          })
          復(fù)制代碼

        源碼淺析

        GitHub地址:源碼文件位置[9]

        ...
        ...
        ...
        //??實(shí)際上這個 api 只是直接 return 傳進(jìn)來的 options,export default defineComponent({})?是有點(diǎn)等價于export default {},目前看來這樣做的最大作用只是限制 type, setup 必須是函數(shù),props 必須是 undefined 或者?對象。
        export?function?defineComponent(options:?unknown)?{
        ??return?isFunction(options)???{?setup:?options,?name:?options.name?}?:?options
        }
        復(fù)制代碼

        defineAsyncComponent

        官方定義:創(chuàng)建一個只有在需要時才會加載的異步組件。

        用法

        參數(shù):接受一個返回Promise的工廠函數(shù)。Promise 的resolve回調(diào)應(yīng)該在服務(wù)端返回組件定義后被調(diào)用。

        //?在?Vue?2.x?中,聲明一個異步組件只需這樣
        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ù),因此異步組件的定義需要通過將其包裹在新的 defineAsyncComponent 助手方法中來顯式地定義:
        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)的異步組件,對 2.x 所做的另一個更改是,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]

        //?源碼位置見上方
        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()
        ???????//?失敗場景處理
        ????????.catch(err?=>?{
        ??????????err?=?err?instanceof?Error???err?:?new?Error(String(err))
        ??????????if?(userOnError)?{
        ????????????//?對應(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)?=>?{
        ??????????//?個人理解:在thisRequest = pendingRequest = loader(),loader()最開始屬于等待狀態(tài),賦值給pendingRequest、在thisRequest此刻他們是相等的等待狀態(tài),當(dāng)進(jìn)入then的時候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.
        ??????//?對應(yīng)文檔中如果父組件是一個?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)致組件更新?重新渲染組件?只不過此時我們已經(jīng)得到組件內(nèi)容
        ??????????loaded.value?=?true
        ????????})
        ????????.catch(err?=>?{
        ??????????onError(err)
        ??????????error.value?=?err
        ????????})

        ??????//?返回的函數(shù)會被當(dāng)做組件實(shí)例的?render?函數(shù)
        ??????return?()?=>?{
        ????????//?render初始執(zhí)行觸發(fā)?loaded的依賴收集?
        ????????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í)例中可用,則允許按名稱解析component,返回一個Component。如果沒有找到,則返回接收的參數(shù)name。

        用法

        參數(shù):已加載的組件的名稱

        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]
        //?接收一個name參數(shù),主要還是在resolveAsset方法中做了處理,源碼位置見上方[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

        ????//?自我名稱具有最高的優(yōu)先級
        ????if?(type?===?COMPONENTS)?{
        ??????//?getComponentName?首先判斷傳入的Component參數(shù)是不是函數(shù),如果是函數(shù)優(yōu)先使用.displayName屬性,其次使用.name
        ??????const?selfName?=?getComponentName(Component)
        ??????if?(
        ????????//?camelize?使用replace方法,正則/-(\w)/gname,匹配后toUpperCase()?轉(zhuǎn)換成大寫
        ????????// capitalize函數(shù):str.charAt(0).toUpperCase()?+ str.slice(1)?首字母大寫?+?處理后的字符
        ????????selfName?&&
        ????????(selfName?===?name?||
        ??????????selfName?===?camelize(name)?||
        ??????????selfName?===?capitalize(camelize(name)))
        ??????)?{
        ????????return?Component
        ??????}
        ????}

        ????const?res?=
        ??????//?注冊
        ??????//?首先檢查實(shí)例[type],它被解析為選項(xiàng)API
        ??????resolve(instance[type]?||?(Component?as?ComponentOptions)[type],?name)?||
        ??????//?全局注冊
        ??????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,其中組件名稱作為節(jié)點(diǎn)標(biāo)簽。如果找不到Component,將發(fā)出警告。

        用法

        參數(shù):接受一個參數(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ù)的名稱,我們可以知道它用于解析動態(tài)組件,在?resolveDynamicComponent?函數(shù)內(nèi)部,若?component?參數(shù)是字符串類型,則會調(diào)用前面介紹的?resolveAsset?方法來解析組件,
        //?如果 resolveAsset 函數(shù)獲取不到對應(yīng)的組件,則會返回當(dāng)前 component 參數(shù)的值。比如 resolveDynamicComponent('div')?將返回?'div'?字符串
        //?源碼見上方[1]地址
        export?function?resolveDynamicComponent(component:?unknown):?VNodeTypes?{
        ??if?(isString(component))?{
        ????return?resolveAsset(COMPONENTS,?component,?false)?||?component
        ??}?else?{
        ????//?無效類型將引發(fā)警告,如果 component 參數(shù)非字符串類型,則會返回 component || NULL_DYNAMIC_COMPONENT 這行語句的執(zhí)行結(jié)果,其中 NULL_DYNAMIC_COMPONENT 的值是一個 Symbol 對象。
        ????return?(component?||?NULL_DYNAMIC_COMPONENT)?as?any
        ??}
        }

        //??resolveAsset函數(shù)解析見上方[8]位置處
        復(fù)制代碼

        resolveDirective

        如果在當(dāng)前應(yīng)用實(shí)例中可用,則允許通過其名稱解析一個directive。返回一個Directive。如果沒有找到,則返回undefined。

        用法

        • 第一個參數(shù):已加載的指令的名稱。

        源碼淺析

        GitHub地址:

        • resolveDirective()43行 - 48行內(nèi)容 \[10\][15]
        • resolveAsset():62行- 123行[16]
        /**
        ?*?源碼位置見上方[10]位置處
        ?*/

        export?function?resolveDirective(name:?string):?Directive?|?undefined?{
        ??//?然后調(diào)用前面介紹的?resolveAsset?方法來解析組件,resolveAsset函數(shù)解析見上方[8]位置處
        ??return?resolveAsset(DIRECTIVES,?name)
        }
        復(fù)制代碼

        withDirectives

        官方定義:允許將指令應(yīng)用于VNode。返回一個包含應(yīng)用指令的 VNode。

        用法

        • 第一個參數(shù):一個虛擬節(jié)點(diǎn),通常使用h()創(chuàng)建

          • 第二個參數(shù):一個指令數(shù)組,每個指令本身都是一個數(shù)組,最多可以定義 4 個索引。
        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()?則會拋出異常:
        ????__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ù)接受兩個泛型參數(shù):HostNodeHostElement,對應(yīng)于宿主環(huán)境中的 Node 和 Element 類型。

        用法

        • 第一個參數(shù):HostNode宿主環(huán)境中的節(jié)點(diǎn)。
        • 第二個參數(shù):Element宿主環(huán)境中的元素。
        //?對于 runtime-dom,HostNode 將是 DOM Node 接口,HostElement 將是 DOM Element 接口。
        //?自定義渲染器可以傳入特定于平臺的類型,如下所示:

        // createRenderer(HostNode, HostElement),兩個通用參數(shù)HostNode(主機(jī)環(huán)境中的節(jié)點(diǎn))和HostElement(宿主環(huán)境中的元素),對應(yīng)于宿主環(huán)境。
        // reateRenderer(使用(可選的)選項(xiàng)創(chuàng)建一個 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這個放2000行的左右的代碼量,這里就完整不貼過來了,里面是渲染的核心代碼,從平臺特性 options 取出相關(guān) API,實(shí)現(xiàn)了 patch、處理節(jié)點(diǎn)、處理組件、更新組件、安裝組件實(shí)例等等方法,最終返回了一個renderer對象。
        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三個函數(shù),生成的 render 傳給 createAppAPI ,hydrate 為可選參數(shù),ssr 的場景下會用到;
        ??return?{
        ????render,
        ????hydrate,
        ????createApp:?createAppAPI(render,?hydrate)
        ??}
        }
        復(fù)制代碼

        nextTick

        官方定義:將回調(diào)推遲到下一個 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)建一個異步任務(wù),但是改變dom屬性也是異步策略,怎么保證dom加載完成
        //?Vue2.x是?會判斷瀏覽器是否支持promise屬性?->?是否支持MutationObserver?->?是否支持setImmediate??->?都不支持使用setTimeout,Vue3不再支持IE11,所以nextTick直接使用Promise

        // Vue 異步執(zhí)行 DOM 更新。只要觀察到數(shù)據(jù)變化,Vue 將開啟一個隊(duì)列,并緩沖在同一事件循環(huán)中發(fā)生的所有數(shù)據(jù)改變。如果同一個 watcher 被多次觸發(fā),只會被推入到隊(duì)列中一次。這種在緩沖時去除重復(fù)數(shù)據(jù)對于避免不必要的計(jì)算和 DOM 操作上非常重要。然后,在下一個的事件循環(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',該組件不會立即重新渲染。當(dāng)刷新隊(duì)列時,組件會在事件循環(huán)隊(duì)列清空時的下一個“tick”更新。如果你想在 DOM 狀態(tài)更新后做點(diǎn)什?,可以在數(shù)據(jù)變化之后立即使用Vue.nextTick(callback)?。
        復(fù)制代碼

        mergeProps

        官方定義:將包含 VNode prop 的多個對象合并為一個單獨(dú)的對象。其返回的是一個新創(chuàng)建的對象,而作為參數(shù)傳遞的對象則不會被修改。

        用法

        參數(shù):可以傳遞不限數(shù)量的對象

        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合并第一個參數(shù)為對象
        ??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開頭的
        ????????const?existing?=?ret[key]
        ????????const?incoming?=?toMerge[key]
        ????????if?(existing?!==?incoming)?{
        ??????????//?如果第一個參數(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ù)中訪問 CSS 模塊。

        用法

        • 參數(shù):CSS 模塊的名稱。默認(rèn)為'$style'
        // useCssModule 只能在 render 或 setup 函數(shù)中使用。
        //?這里的name不止可以填寫$style,
        /*
        **?...
        *
        */

        //?這樣就可以使用?const?style?=?useCssModule(‘a(chǎn)aa'),來獲取相應(yīng)內(nèi)容

        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

          <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            女人28片毛片60分钟 | 久热精品视频在线播放 | 男女www视频 | 国产第21页 | 色视频www在线播放国产人成 | 8x8x国产精品 | 日韩三级中文乱码 | 成人看片免费视频在线观看 | 强干少妇小说 | 77777亚洲和欧洲 |