日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区

您的位置:首頁技術文章
文章詳情頁

vue3的動態組件是如何工作的

瀏覽:30日期:2022-10-02 13:45:40

在這篇文章中,阿寶哥將介紹 Vue 3 中的內置組件 —— component,該組件的作用是渲染一個 “元組件” 為動態組件。如果你對動態組件還不了解的話也沒關系,文中阿寶哥會通過具體的示例,來介紹動態組件的應用。由于動態組件內部與組件注冊之間有一定的聯系,所以為了讓大家能夠更好地了解動態組件的內部原理,阿寶哥會先介紹組件注冊的相關知識。

一、組件注冊1.1 全局注冊

在 Vue 3.0 中,通過使用 app 對象的 component 方法,可以很容易地注冊或檢索全局組件。component 方法支持兩個參數:

name:組件名稱; component:組件定義對象。

接下來,我們來看一個簡單的示例:

<div id='app'> <component-a></component-a> <component-b></component-b> <component-c></component-c></div><script> const { createApp } = Vue const app = createApp({}); // ① app.component(’component-a’, { // ② template: '<p>我是組件A</p>' }); app.component(’component-b’, { template: '<p>我是組件B</p>' }); app.component(’component-c’, { template: '<p>我是組件C</p>' }); app.mount(’#app’) // ③</script>

在以上代碼中,我們通過 app.component 方法注冊了 3 個組件,這些組件都是全局注冊的 。也就是說它們在注冊之后可以用在任何新創建的組件實例的模板中。該示例的代碼比較簡單,主要包含 3 個步驟:創建 App 對象、注冊全局組件和應用掛載。其中創建 App 對象的細節,阿寶哥會在后續的文章中單獨介紹,下面我們將重點分析其他 2 個步驟,首先我們先來分析注冊全局組件的過程。

1.2 注冊全局組件的過程

在以上示例中,我們使用 app 對象的 component 方法來注冊全局組件:

app.component(’component-a’, { template: '<p>我是組件A</p>'});

當然,除了注冊全局組件之外,我們也可以注冊局部組件,因為組件中也接受一個 components 的選項:

const app = Vue.createApp({ components: { ’component-a’: ComponentA, ’component-b’: ComponentB }})

需要注意的是,局部注冊的組件在其子組件中是不可用的。接下來,我們來繼續介紹注冊全局組件的過程。對于前面的示例來說,我們使用的 app.component 方法被定義在 runtime-core/src/apiCreateApp.ts 文件中:

export function createAppAPI<HostElement>( render: RootRenderFunction, hydrate?: RootHydrateFunction): CreateAppFunction<HostElement> { return function createApp(rootComponent, rootProps = null) { const context = createAppContext() const installedPlugins = new Set() let isMounted = false const app: App = (context.app = { // 省略部分代碼 _context: context, // 注冊或檢索全局組件 component(name: string, component?: Component): any { if (__DEV__) { validateComponentName(name, context.config) } if (!component) { // 獲取name對應的組件 return context.components[name] } if (__DEV__ && context.components[name]) { // 重復注冊提示 warn(`Component '${name}' has already been registered in target app.`) } context.components[name] = component // 注冊全局組件 return app }, }) return app }}

當所有的組件都注冊成功之后,它們會被保存到 context 對象的 components 屬性中,具體如下圖所示:

vue3的動態組件是如何工作的

而 createAppContext 函數被定義在 runtime-core/src/apiCreateApp.ts 文件中:

// packages/runtime-core/src/apiCreateApp.tsexport function createAppContext(): AppContext { return { app: null as any, config: { // 應用的配置對象 isNativeTag: NO, performance: false, globalProperties: {}, optionMergeStrategies: {}, isCustomElement: NO, errorHandler: undefined, warnHandler: undefined }, mixins: [], // 保存應用內的混入 components: {}, // 保存全局組件的信息 directives: {}, // 保存全局指令的信息 provides: Object.create(null) }}

分析完 app.component 方法之后,是不是覺得組件注冊的過程還是挺簡單的。那么對于已注冊的組件,何時會被使用呢?要回答這個問題,我們就需要分析另一個步驟 —— 應用掛載。

1.3 應用掛載的過程

為了更加直觀地了解應用掛載的過程,阿寶哥利用 Chrome 開發者工具的 Performance 標簽欄,記錄了應用掛載的主要過程:

vue3的動態組件是如何工作的

在上圖中我們發現了一個與組件相關的函數 resolveComponent。很明顯,該函數用于解析組件,且該函數在 render 方法中會被調用。在源碼中,我們找到了該函數的定義:

// packages/runtime-core/src/helpers/resolveAssets.tsconst COMPONENTS = ’components’export function resolveComponent(name: string): ConcreteComponent | string { return resolveAsset(COMPONENTS, name) || name}

由以上代碼可知,在 resolveComponent 函數內部,會繼續調用 resolveAsset 函數來執行具體的解析操作。在分析 resolveAsset 函數的具體實現之前,我們在 resolveComponent 函數內部加個斷點,來一睹 render 方法的 “芳容”:

vue3的動態組件是如何工作的

在上圖中,我們看到了解析組件的操作,比如 _resolveComponent('component-a')。前面我們已經知道在 resolveComponent 函數內部會繼續調用 resolveAsset 函數,該函數的具體實現如下:

// packages/runtime-core/src/helpers/resolveAssets.tsfunction resolveAsset( type: typeof COMPONENTS | typeof DIRECTIVES, name: string, warnMissing = true) { const instance = currentRenderingInstance || currentInstance if (instance) { const Component = instance.type // 省略大部分處理邏輯 const res = // 局部注冊 // check instance[type] first for components with mixin or extends. resolve(instance[type] || (Component as ComponentOptions)[type], name) || // 全局注冊 resolve(instance.appContext[type], name) return res } else if (__DEV__) { warn( `resolve${capitalize(type.slice(0, -1))} ` + `can only be used in render() or setup().` ) }}

因為注冊組件時,使用的是全局注冊的方式,所以解析的過程會執行 resolve(instance.appContext[type], name) 該語句,其中 resolve 方法的定義如下:

// packages/runtime-core/src/helpers/resolveAssets.tsfunction resolve(registry: Record<string, any> | undefined, name: string) { return ( registry && (registry[name] || registry[camelize(name)] || registry[capitalize(camelize(name))]) )}

分析完以上的處理流程,我們在解析全局注冊的組件時,會通過 resolve 函數從應用的上下文對象中獲取已注冊的組件對象。

(function anonymous() { const _Vue = Vue return function render(_ctx, _cache) { with (_ctx) { const {resolveComponent: _resolveComponent, createVNode: _createVNode, Fragment: _Fragment, openBlock: _openBlock, createBlock: _createBlock} = _Vue const _component_component_a = _resolveComponent('component-a') const _component_component_b = _resolveComponent('component-b') const _component_component_c = _resolveComponent('component-c') return (_openBlock(), _createBlock(_Fragment, null, [ _createVNode(_component_component_a), _createVNode(_component_component_b), _createVNode(_component_component_c)], 64)) } }})

在獲取到組件之后,會通過 _createVNode 函數創建 VNode 節點。然而,關于 VNode 是如何被渲染成真實的 DOM 元素這個過程,阿寶哥就不繼續往下介紹了,后續會寫專門的文章來單獨介紹這塊的內容,接下來我們將介紹動態組件的相關內容。

二、動態組件

在 Vue 3 中為我們提供了一個 component 內置組件,該組件可以渲染一個 “元組件” 為動態組件。根據 is 的值,來決定哪個組件被渲染。如果 is 的值是一個字符串,它既可以是 HTML 標簽名稱也可以是組件名稱。對應的使用示例如下:

<!-- 動態組件由 vm 實例的 `componentId` property 控制 --><component :is='componentId'></component><!-- 也能夠渲染注冊過的組件或 prop 傳入的組件--><component :is='$options.components.child'></component><!-- 可以通過字符串引用組件 --><component :is='condition ? ’FooComponent’ : ’BarComponent’'></component><!-- 可以用來渲染原生 HTML 元素 --><component :is='href ? ’a’ : ’span’'></component>2.1 綁定字符串類型

介紹完 component 內置組件,我們來舉個簡單的示例:

<div id='app'> <button v-for='tab in tabs' :key='tab' @click='currentTab = ’tab-’ + tab.toLowerCase()'> {{ tab }} </button> <component :is='currentTab'></component></div><script> const { createApp } = Vue const tabs = [’Home’, ’My’] const app = createApp({ data() { return { tabs, currentTab: ’tab-’ + tabs[0].toLowerCase() } }, }); app.component(’tab-home’, { template: `<div style='border: 1px solid;'>Home component</div>` }) app.component(’tab-my’, { template: `<div style='border: 1px solid;'>My component</div>` }) app.mount(’#app’)</script>

在以上代碼中,我們通過 app.component 方法全局注冊了 tab-home 和 tab-my 2 個組件。此外,在模板中,我們使用了 component 內置組件,該組件的 is 屬性綁定了 data 對象的 currentTab 屬性,該屬性的類型是字符串。當用戶點擊 Tab 按鈕時,會動態更新 currentTab 的值,從而實現動態切換組件的功能。以上示例成功運行后的結果如下圖所示:

vue3的動態組件是如何工作的

看到這里你會不會覺得 component 內置組件挺神奇的,感興趣的小伙伴繼續跟阿寶哥一起,來揭開它背后的秘密。下面我們利用 Vue 3 Template Explorer 在線工具,看一下 <component :is='currentTab'></component> 模板編譯的結果:

const _Vue = Vuereturn function render(_ctx, _cache, $props, $setup, $data, $options) { with (_ctx) { const { resolveDynamicComponent: _resolveDynamicComponent, openBlock: _openBlock, createBlock: _createBlock } = _Vue return (_openBlock(), _createBlock(_resolveDynamicComponent(currentTab))) }}

通過觀察生成的渲染函數,我們發現了一個 resolveDynamicComponent 的函數,根據該函數的名稱,我們可以知道它用于解析動態組件,它被定義在 runtime-core/src/helpers/resolveAssets.ts 文件中,具體實現如下所示:

// packages/runtime-core/src/helpers/resolveAssets.tsexport function resolveDynamicComponent(component: unknown): VNodeTypes { if (isString(component)) { return resolveAsset(COMPONENTS, component, false) || component } else { // invalid types will fallthrough to createVNode and raise warning return (component || NULL_DYNAMIC_COMPONENT) as any }}

在 resolveDynamicComponent 函數內部,若 component 參數是字符串類型,則會調用前面介紹的 resolveAsset 方法來解析組件:

// packages/runtime-core/src/helpers/resolveAssets.tsfunction resolveAsset( type: typeof COMPONENTS | typeof DIRECTIVES, name: string, warnMissing = true) { const instance = currentRenderingInstance || currentInstance if (instance) { const Component = instance.type // 省略大部分處理邏輯 const res = // 局部注冊 // check instance[type] first for components with mixin or extends. resolve(instance[type] || (Component as ComponentOptions)[type], name) || // 全局注冊 resolve(instance.appContext[type], name) return res }}

對于前面的示例來說,組件是全局注冊的,所以解析過程中會從 app.context 上下文對象的 components 屬性中獲取對應的組件。當 currentTab 發生變化時,resolveAsset 函數就會返回不同的組件,從而實現動態組件的功能。此外,如果 resolveAsset 函數獲取不到對應的組件,則會返回當前 component 參數的值。比如 resolveDynamicComponent(’div’) 將返回 ’div’ 字符串。

// packages/runtime-core/src/helpers/resolveAssets.tsexport const NULL_DYNAMIC_COMPONENT = Symbol()export function resolveDynamicComponent(component: unknown): VNodeTypes { if (isString(component)) { return resolveAsset(COMPONENTS, component, false) || component } else { return (component || NULL_DYNAMIC_COMPONENT) as any }}

細心的小伙伴可能也注意到了,在 resolveDynamicComponent 函數內部,如果 component 參數非字符串類型,則會返回 component || NULL_DYNAMIC_COMPONENT 這行語句的執行結果,其中 NULL_DYNAMIC_COMPONENT 的值是一個 Symbol 對象。

2.2 綁定對象類型

了解完上述的內容之后,我們來重新實現一下前面動態 Tab 的功能:

<div id='app'> <button v-for='tab in tabs' :key='tab' @click='currentTab = tab'> {{ tab.name }} </button> <component :is='currentTab.component'></component></div><script> const { createApp } = Vue const tabs = [ { name: ’Home’, component: { template: `<div style='border: 1px solid;'>Home component</div>` } }, { name: ’My’, component: { template: `<div style='border: 1px solid;'>My component</div>` } }] const app = createApp({ data() { return { tabs, currentTab: tabs[0] } }, }); app.mount(’#app’)</script>

在以上示例中,component 內置組件的 is 屬性綁定了 currentTab 對象的 component 屬性,該屬性的值是一個對象。當用戶點擊 Tab 按鈕時,會動態更新 currentTab 的值,導致 currentTab.component 的值也發生變化,從而實現動態切換組件的功能。需要注意的是,每次切換的時候,都會重新創建動態組件。但在某些場景下,你會希望保持這些組件的狀態,以避免反復重渲染導致的性能問題。

對于這個問題,我們可以使用 Vue 3 的另一個內置組件 —— keep-alive,將動態組件包裹起來。比如:

<keep-alive> <component :is='currentTab'></component></keep-alive>

keep-alive 內置組件的主要作用是用于保留組件狀態或避免重新渲染,使用它包裹動態組件時,會緩存不活動的組件實例,而不是銷毀它們。關于 keep-alive 組件的內部工作原理,阿寶哥后面會寫專門的文章來分析它,對它感興趣的小伙伴記得關注 Vue 3.0 進階 系列喲。

三、阿寶哥有話說3.1 除了 component 內置組件外,還有哪些內置組件?

在 Vue 3 中除了本文介紹的 component 和 keep-alive 內置組件之外,還提供了 transition、transition-group 、slot 和 teleport 內置組件。

3.2 注冊全局組件與局部組件有什么區別?注冊全局組件

const { createApp, h } = Vueconst app = createApp({});app.component(’component-a’, { template: '<p>我是組件A</p>'});

使用 app.component 方法注冊的全局的組件,被保存到 app 應用對象的上下文對象中。而通過組件對象 components 屬性注冊的局部組件是保存在組件實例中。

注冊局部組件

const { createApp, h } = Vueconst app = createApp({});const componentA = () => h(’div’, ’我是組件A’);app.component(’component-b’, { components: { ’component-a’: componentA }, template: `<div> 我是組件B,內部使用了組件A <component-a></component-a> </div>`})解析全局注冊和局部注冊的組件

// packages/runtime-core/src/helpers/resolveAssets.tsfunction resolveAsset( type: typeof COMPONENTS | typeof DIRECTIVES, name: string, warnMissing = true) { const instance = currentRenderingInstance || currentInstance if (instance) { const Component = instance.type // 省略大部分處理邏輯 const res = // 局部注冊 // check instance[type] first for components with mixin or extends. resolve(instance[type] || (Component as ComponentOptions)[type], name) || // 全局注冊 resolve(instance.appContext[type], name) return res }}3.3 動態組件能否綁定其他屬性?

component 內置組件除了支持 is 綁定之外,也支持其他屬性綁定和事件綁定:

<component :is='currentTab.component' :name='name' @click='sayHi'></component>

這里阿寶哥使用 Vue 3 Template Explorer 這個在線工具,來編譯上述的模板:

const _Vue = Vuereturn function render(_ctx, _cache, $props, $setup, $data, $options) { with (_ctx) { const { resolveDynamicComponent: _resolveDynamicComponent, openBlock: _openBlock, createBlock: _createBlock } = _Vue return (_openBlock(), _createBlock(_resolveDynamicComponent(currentTab.component), { name: name, onClick: sayHi }, null, 8 /* PROPS */, ['name', 'onClick'])) }}

觀察以上的渲染函數可知,除了 is 綁定會被轉換為 _resolveDynamicComponent 函數調用之外,其他的屬性綁定都會被正常解析為 props 對象。

以上就是vue3的動態組件是如何工作的的詳細內容,更多關于vue3動態組件的資料請關注好吧啦網其它相關文章!

標簽: Vue
相關文章:
日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区
国产成人精品一区二区免费看京 | 中文一区在线| 亚洲欧洲一区| 日韩一区二区三区四区五区| 精品色999| 亚洲免费激情| 国产免费播放一区二区| 日本精品影院| 日韩va亚洲va欧美va久久| 成人在线黄色| 亚洲欧洲美洲国产香蕉| 美女视频黄免费的久久| 亚洲欧美日韩高清在线| 18国产精品| 欧美一级精品| 欧美激情99| 美女精品在线| 国产黄大片在线观看| 亚洲色图综合| 91精品婷婷色在线观看| 国产精品美女在线观看直播| 在线观看免费一区二区| 精品国产亚洲一区二区三区在线| 天堂va蜜桃一区二区三区| 激情黄产视频在线免费观看| 日本不卡中文字幕| 99在线精品视频在线观看| 久久精品国产99| 亚洲三级观看| 免费国产自久久久久三四区久久| 欧美激情91| 亚洲日韩中文字幕一区| 91精品久久久久久久久久不卡| 国产欧美二区| 视频精品一区二区| 天堂中文av在线资源库| 欧美亚洲综合视频| 欧美在线亚洲| 国产一区二区三区四区五区| 日本不卡不码高清免费观看| 亚洲国产专区| 国产精品99一区二区三| 国产美女视频一区二区| 一区二区国产在线| 九一精品国产| av免费不卡国产观看| 国产日韩三级| 偷拍亚洲精品| 免费人成精品欧美精品| 国产综合婷婷| 美女福利一区二区三区| 国产乱人伦丫前精品视频 | 91精品啪在线观看国产18| 国产一区二区三区四区二区| 国产情侣一区| 天堂俺去俺来也www久久婷婷| 免费久久精品| 久久中文字幕二区| 中文字幕系列一区| 韩日一区二区| 久久99蜜桃| 7m精品国产导航在线| 香蕉久久久久久久av网站| 日韩一区二区三区在线免费观看| 久久不卡日韩美女| 91嫩草精品| 亚洲精品国产精品粉嫩| 亚洲一区二区三区高清不卡| 久久精品1区| 欧洲精品一区二区三区| 97精品国产福利一区二区三区| 国产日本亚洲| 日韩不卡免费视频| **爰片久久毛片| 日本欧美在线| 久久国内精品视频| 国产午夜久久av| 色8久久久久| 日韩高清在线一区| 亚洲另类av| 免费不卡在线观看| 免费一级片91| 亚洲我射av| 美女被久久久| 蘑菇福利视频一区播放| 宅男噜噜噜66国产日韩在线观看| 精品一区毛片| 香蕉久久久久久久av网站| 在线一区视频| 日av在线不卡| 亚洲午夜久久| 日韩美女国产精品| 国产亚洲观看| 国产欧美日本| 国内精品伊人| 日韩理论视频| 国产精品av久久久久久麻豆网| 久久国产毛片| 欧美日韩四区| 首页欧美精品中文字幕| 日韩精选在线| 国产欧美日韩免费观看| 成人在线免费观看网站| 精精国产xxxx视频在线野外| 99日韩精品| 日韩和欧美的一区| 国产精品亚洲四区在线观看| 麻豆视频观看网址久久| 国产欧洲在线| 国产精品av一区二区| 亚洲综合三区| 欧美一级一区| 黑森林国产精品av| 亚洲欧美日韩国产一区二区| 亚洲1区在线| 久久99影视| 婷婷成人在线| 亚洲三级网址| 精品国产一区二| 欧美理论视频| 日本aⅴ免费视频一区二区三区| 国产精品va视频| 日韩在线免费| 日韩三区四区| 国产一区二区三区精品在线观看| 99久久精品网| 三级一区在线视频先锋| 91精品国产自产在线丝袜啪| 麻豆精品久久| 国产一区二区三区自拍| 亚洲久久在线| 成人国产精品| 国产亚洲精品v| 欧美精品第一区| 欧美va亚洲va日韩∨a综合色| 亚洲+小说+欧美+激情+另类| 老司机精品视频网| 日韩亚洲精品在线| 国产精品va| 国精品一区二区| 青青在线精品| 久久国产亚洲精品| 日韩精品a在线观看91| 色婷婷亚洲mv天堂mv在影片| 欧美日韩国产在线一区| 嫩呦国产一区二区三区av| 欧美日韩精品一本二本三本 | 国产亚洲电影| 亚洲午夜电影| 国产精品videossex久久发布| 欧美在线资源| 精品国产亚洲日本| 亚洲尤物av| 少妇久久久久| 国产麻豆精品| 免费久久精品视频| 欧美国产美女| 欧美日韩xxxx| 伊人成人在线视频| 精品一区二区三区的国产在线观看| 久久国产主播| 免费亚洲一区| 中文字幕一区二区av| 亚洲播播91| 欧美日韩一区二区高清| 免费观看久久av| 中文av在线全新| 青青国产91久久久久久| 制服诱惑一区二区| 日韩精品2区| 久久精品xxxxx| 中文字幕亚洲精品乱码| 99精品美女| 欧美国产免费| 日韩精品久久久久久久软件91| 成人羞羞视频在线看网址| 国产日韩欧美在线播放不卡| 三级欧美韩日大片在线看| 久久久777| 精品国产成人| 69堂免费精品视频在线播放| 999久久久国产精品| 日本欧美一区二区| 国产精品人人爽人人做我的可爱| 国精品产品一区| 国产精品亚洲人成在99www| 在线精品视频一区| 日韩亚洲国产欧美| 欧美亚洲在线日韩| 日韩深夜视频| 韩国一区二区三区视频| 国产精品麻豆成人av电影艾秋| 久久国产精品久久久久久电车| 久久婷婷激情| 四虎4545www国产精品| 日韩欧美1区| 日韩中文字幕高清在线观看| 精品日韩一区| 国产videos久久| 精品亚洲成人|