中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Vue 3.0進階之應用創建的方法過程

發布時間:2021-10-18 11:11:32 來源:億速云 閱讀:145 作者:iii 欄目:web開發

本篇內容主要講解“Vue 3.0進階之應用創建的方法過程”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“Vue 3.0進階之應用創建的方法過程”吧!

接下來,我們將從一個簡單的例子出發,從頭開始一步步分析 Vue 3.0 應用創建的過程。

<div id="app"></div> <script>    const { createApp, h } = Vue    const app = createApp({ // ①      data() {        return {          name: '我是阿寶哥'        }      },      template: `<div>大家好, {{name}}!</div>`    })    app.mount('#app') // ② </script>

在以上代碼中,首先我們通過 createApp 函數創建 app 對象,然后調用 app.mount  方法執行應用掛載操作。當以上代碼成功運行后,頁面上會顯示 大家好,我是阿寶哥!,具體如下圖所示:

Vue 3.0進階之應用創建的方法過程

對于以上的示例來說,它主要包含兩個步驟:創建 app 對象和應用掛載。這里我們只分析創建 app  對象的過程,而應用掛載的過程將在下一篇文章中介紹。

一、創建 app 對象

首先,阿寶哥利用 Chrome 開發者工具的 Performance 標簽欄,記錄了創建 app 對象的主要過程:

Vue 3.0進階之應用創建的方法過程

從圖中我們看到了在創建 app 對象過程中,所涉及的相關函數。為了讓大家能直觀地了解 app 對象創建的過程,阿寶哥畫了一張圖:

Vue 3.0進階之應用創建的方法過程

大致了解了主要過程之后,我們從 createApp 這個入口開始分析。接下來,打開 Chrome 開發者工具,在 createApp 處加個斷點:

Vue 3.0進階之應用創建的方法過程

通過斷點,我們找到了 createApp  函數,調用該函數之后會返回一個提供應用上下文的應用實例。應用實例掛載的整個組件樹共享同一個上下文。createApp 函數被定義在  runtime-dom/src/index.ts 文件中:

// packages/runtime-dom/src/index.ts export const createApp = ((...args) => {   const app = ensureRenderer().createApp(...args)    const { mount } = app   app.mount = (containerOrSelector: Element | ShadowRoot | string): any => {     // 省略mount內部的處理邏輯   }   return app }) as CreateAppFunction<Element>

在 createApp 內部,會先調用 ensureRenderer 函數,該函數的內部代碼很簡單:

// packages/runtime-dom/src/index.ts function ensureRenderer() {   return renderer || (renderer = createRenderer<Node, Element>(rendererOptions)) }

在以上代碼中會延遲創建渲染器,那么為什么要這樣做呢?我們從 runtime-dom/src/index.ts 文件中的注釋,找到了答案:

// lazy create the renderer - this makes core renderer logic tree-shakable // in case the user only imports reactivity utilities from Vue.

對于我們的示例來說,是需要使用到渲染器的,所以會調用 createRenderer 函數創建渲染器。在分析 createRenderer  函數前,我們先來分析一下它的參數rendererOptions:

// packages/runtime-dom/src/index.ts export const extend = Object.assign const rendererOptions = extend({ patchProp, forcePatchProp }, nodeOps)

由以上代碼可知,參數 rendererOptions 是一個包含 patchProp、forcePatchProp 等屬性的對象,其中 nodeOps 是  node operations 的縮寫。對于 Web 瀏覽器環境來說,它定義了操作節點/元素的 API,比如創建元素、創建文本節點、插入元素和刪除元素等。因為  Vue 3.0 的源碼是使用 TypeScript 編寫的,所以可以在源碼中找到rendererOptions 參數的類型定義:

// packages/runtime-core/src/renderer.ts export interface RendererOptions<   HostNode = RendererNode,   HostElement = RendererElement > {   patchProp(el: HostElement, key: string, prevValue: any, nextValue: any, ...): void   forcePatchProp?(el: HostElement, key: string): boolean   insert(el: HostNode, parent: HostElement, anchor?: HostNode | null): void   remove(el: HostNode): void   createElement( type: string, isSVG?: boolean, isCustomizedBuiltIn?: string): HostElement   createText(text: string): HostNode   createComment(text: string): HostNode   setText(node: HostNode, text: string): void   setElementText(node: HostElement, text: string): void   parentNode(node: HostNode): HostElement | null   nextSibling(node: HostNode): HostNode | null   querySelector?(selector: string): HostElement | null   setScopeId?(el: HostElement, id: string): void   cloneNode?(node: HostNode): HostNode   insertStaticContent?(content: string, parent: HostElement, ...): HostElement[] }

在 RendererOptions  接口中定義了與渲染器相關的所有方法,這樣做的目的是對渲染器做了一層抽象。開發者在滿足該接口約束的情況下,就可以根據自己的需求實現自定義渲染器。了解完  rendererOptions 參數,我們來介紹 createRenderer 函數:

// packages/runtime-core/src/renderer.ts export interface RendererNode {   [key: string]: any  // 索引簽名 } export interface RendererElement extends RendererNode {}  export function createRenderer<   HostNode = RendererNode,   HostElement = RendererElement >(options: RendererOptions<HostNode, HostElement>) {   return baseCreateRenderer<HostNode, HostElement>(options) }

在 createRenderer 函數內部會繼續調用 baseCreateRenderer  函數來執行創建渲染器的邏輯,該函數內部的邏輯比較復雜,這里我們先來看一下調用該函數后的返回結果:

// packages/runtime-core/src/renderer.ts function baseCreateRenderer(   options: RendererOptions,   createHydrationFns?: typeof createHydrationFunctions ): any {   // 省略大部分代碼   return {     render,     hydrate,     createApp: createAppAPI(render, hydrate)   } }

在以上代碼中,我們終于看到了期待已久的 createApp 屬性,該屬性的值是調用 createAppAPI  函數后的返回結果。看過阿寶哥之前文章的小伙伴,對 createAppAPI 函數應該不會陌生,它被定義在  runtime-core/src/apiCreateApp.ts 文件中:

// packages/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 = {       _uid: uid++,       _component: rootComponent as ConcreteComponent,       _context: context,       // 省略use、mixin、unmount和provide等方法       component(name: string, component?: Component): any {      // ...       },       directive(name: string, directive?: Directive) {         // ...       },       mount(rootContainer: HostElement, isHydrate?: boolean): any {         // ...       },     })     return app   } }

通過以上的代碼可知,createApp 方法支持 rootComponent 和 rootProps 兩個參數,調用該方法之后會返回一個 app  對象,該對象為了開發者提供了多個應用 API,比如,用于注冊或檢索全局組件的 component 方法,用于注冊或檢索全局指令的  directive方法及用于將應用實例的根組件掛載到指定 DOM 元素上的 mount 方法等。

此外,在 createApp 函數體中,我們看到了 const context = createAppContext()  這行代碼。顧名思義,createAppContext 函數用于創建與當前應用相關的上下文對象。那么所謂的上下文對象長啥樣呢?要搞清楚這個問題,我們來看一下  createAppContext 函數的具體實現:

// packages/runtime-core/src/apiCreateApp.ts export function createAppContext(): AppContext {   return {     app: null as any,     config: { ... },     mixins: [],     components: {},     directives: {},     provides: Object.create(null)   } }

介紹完 app 和 context 對象之后,我們來繼續分析 createApp 函數剩下的邏輯代碼:

// packages/runtime-dom/src/index.ts export const createApp = ((...args) => {   const app = ensureRenderer().createApp(...args)    const { mount } = app   app.mount = (containerOrSelector: Element | ShadowRoot | string): any => {     // 省略mount內部的處理邏輯   }   return app }) as CreateAppFunction<Element>

由以上代碼可知,在創建完 app 對象之后,并不會立即返回已創建的 app 對象,而是會重寫 app.mount 屬性:

// packages/runtime-dom/src/index.ts export const createApp = ((...args) => {   const app = ensureRenderer().createApp(...args)    const { mount } = app   app.mount = (containerOrSelector: Element | ShadowRoot | string): any => {     const container = normalizeContainer(containerOrSelector) // 同時支持字符串和DOM對象     if (!container) return     const component = app._component     // 若根組件非函數對象且未設置render和template屬性,則使用容器的innerHTML作為模板的內容     if (!isFunction(component) && !component.render && !component.template) {       component.template = container.innerHTML     }     container.innerHTML = '' // 在掛載前清空容器內容     const proxy = mount(container) // 執行掛載操作     if (container instanceof Element) {       container.removeAttribute('v-cloak') // 避免在網絡不好或加載數據過大的情況下,頁面渲染的過程中會出現Mustache標簽       container.setAttribute('data-v-app', '')     }     return proxy   }    return app }) as CreateAppFunction<Element>

在 app.mount 方法內部,當設置好根組件的相關信息之后,就會調用 app 對象原始的mount 方法執行掛載操作:

// packages/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 = {       _uid: uid++,       _component: rootComponent as ConcreteComponent,       _props: rootProps,       _context: context,        mount(rootContainer: HostElement, isHydrate?: boolean): any {         if (!isMounted) {           // 基于根組件和根組件屬性創建對應的VNode節點           const vnode = createVNode(             rootComponent as ConcreteComponent,             rootProps           )           vnode.appContext = context // 應用上下文           if (isHydrate && hydrate) { // 與服務端渲染相關             hydrate(vnode as VNode<Node, Element>, rootContainer as any)           } else { // 把vnode渲染到根容器中             render(vnode, rootContainer)           }           isMounted = true // 設置已掛載的狀態            app._container = rootContainer           return vnode.component!.proxy         }       },     })      return app   } }

那么為什么要重寫 app.mount 方法呢?原因是為了支持跨平臺,在 runtime-dom 包中定義的 app.mount 方法,都是與 Web  平臺有關的方法。另外,在 runtime-dom 包中,還會為 Web 平臺創建該平臺對應的渲染器。即在創建渲染器時,使用的 nodeOps 對象中封裝了  DOM 相關的 API:

// packages/runtime-dom/src/nodeOps.ts export const nodeOps: Omit<RendererOptions<Node, Element>, 'patchProp'> = {   // 省略部分方法   createElement: (tag, isSVG, is): Element =>     isSVG ? doc.createElementNS(svgNS, tag) : doc.createElement(tag, is ? { is } : undefined),   createText: text => doc.createTextNode(text),   createComment: text => doc.createComment(text),   querySelector: selector => doc.querySelector(selector), }

現在創建 app 對象的過程中涉及的主要函數已經介紹完了,對這個過程還不理解的小伙伴,可以參考阿寶哥前面畫的圖,然后斷點調試一下創建 app  對象的過程。

Vue 3.0進階之應用創建的方法過程

二、阿寶哥有話說

2.1 App 對象提供哪些 API?

在 Vue 3 中,改變全局 Vue 行為的 API 現在被移動到了由新的 createApp 方法所創建的應用實例上。應用實例為我們提供了以下 API  來實現特定的功能:

  • config():包含應用配置的對象。

  • unmount():在提供的 DOM 元素上卸載應用實例的根組件。

  • mixin(mixin: ComponentOptions):將一個 mixin 應用在整個應用范圍內。

  • provide(key, value):設置一個可以被注入到應用范圍內所有組件中的值。

  • component(name: string, component?: Component):注冊或檢索全局組件。

  • directive(name: string, directive?: Directive):注冊或檢索全局指令。

  • use(plugin: Plugin, ...options: any[]):安裝 Vue.js  插件,當在同一個插件上多次調用此方法時,該插件將僅安裝一次。

  • mount(rootContainer: HostElement, isHydrate?: boolean,isSVG?:  boolean):將應用實例的根組件掛載在提供的 DOM 元素上。

2.2 使用 createApp 函數可以創建多個 Vue 應用么?

通過 createApp 函數,我們可以輕松地創建多個 Vue 應用。每個應用的上下文環境都是互相隔離的,具體的使用方式如下所示:

<div id="appA"></div> <hr> <div id="appB"></div> <script>   const { createApp, h } = Vue   const appA = createApp({     template: "我是應用A"   })   const appB = createApp({     template: "我是應用B"   })   appA.mount('#appA')    appB.mount('#appB')  </script>

到此,相信大家對“Vue 3.0進階之應用創建的方法過程”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

vue
AI

濉溪县| 柳江县| 双流县| 喜德县| 普陀区| 铁力市| 突泉县| 星座| 沿河| 浦县| 湖北省| 如东县| 庆城县| 南安市| 凤山市| 梁山县| 和硕县| 三明市| 格尔木市| 绥化市| 祁连县| 青浦区| 扶余县| 武功县| 莎车县| 长春市| 调兵山市| 瓦房店市| 盱眙县| 宜春市| 太和县| 徐州市| 海门市| 棋牌| 海淀区| 台北县| 额敏县| 修文县| 铜陵市| 雅安市| 金寨县|