前情回顾

本篇文章对 Vue.createApp(App) 做了什么进行了简单分析,以上篇文章的 demo 为例。

戳链接回顾上篇文章

Vue3源码学习笔记—— Vue.createApp(App) 和 app.mount(“#app”)(一) - 掘金 (juejin.cn)

Vue.createApp(App) 做了什么

createApp 函数

位置:/packages/runtime-dom/src/index.ts

首先,我们定义了一个根组件 App ,并将 App 传入 Vue.createApp() ,而 createApp 存在于 runtime-dom (运行时)中,代码如下。

export const createApp = ((...args) => {
  // 1.创建app对象
  /*
    (1)ensureRenderer()是一个完整的渲染器Renderer,和渲染相关的代码都在这里
    (2)传入的参数用于调用ensureRenderer().createApp(...args),该函数最终返回一个app对象并赋值给新创建的app对象
  */
  const app = ensureRenderer().createApp(...args)

  if (__DEV__) {
    injectNativeTagCheck(app)
    injectCompilerOptionsCheck(app)
  }

  const { mount } = app
  app.mount = (containerOrSelector: Element | ShadowRoot | string): any => { ... }

  return app
}) as CreateAppFunction<Element>

可见,传入的参数并没有在函数中被使用,而是用于调用ensureRenderer().createApp(),该函数最终返回一个app对象并赋值给新创建的app对象。

ensureRenderer 函数

位置:/packages/runtime-dom/src/index.ts

该函数判断已定义的 renderer 渲染器有没有值,如果已经有渲染器就直接返回,如果没有渲染器,那么调用 createRenderer 创建渲染器。

let renderer: Renderer<Element | ShadowRoot> | HydrationRenderer

let enabledHydration = false

function ensureRenderer() {
  // 已有渲染器 -> 直接返回
  // 没有渲染器 -> 调用createRenderer创建渲染器
  return (
    renderer ||
    (renderer = createRenderer<Node, Element | ShadowRoot>(rendererOptions))
  )
}

createRenderer 函数

位置:/packages/runtime-core/src/renderer.ts

该函数返回调用了 baseCreateRenderer 函数。

export function createRenderer<
  HostNode = RendererNode,
  HostElement = RendererElement
>(options: RendererOptions<HostNode, HostElement>) {
  return baseCreateRenderer<HostNode, HostElement>(options)
}

baseCreateRenderer 函数

位置:/packages/runtime-core/src/renderer.ts

// baseCreateRenderer函数进行了函数重载,真正实现是在这个位置的
function baseCreateRenderer(
  options: RendererOptions,
  createHydrationFns?: typeof createHydrationFunctions
): any {
  // 由于篇幅限制,此处省略2000多行......
  return {
    render,
    hydrate,
    // 此处采用了柯里化,将render函数及其他相关参数全部传入createAppAPI函数中
    createApp: createAppAPI(render, hydrate)
  }
}

现在我们回到 createApp 函数中,可以发现,ensureRenderer().createApp(...args) 最终调用的是 createAppAPI() 函数,如图所示。

在这里插入图片描述

createAppAPI 函数

位置:/packages/runtime-core/src/apiCreateApp.ts

该函数通过返回调用 createApp 函数,最终返回了一个app对象。

export function createAppAPI<HostElement>(
  render: RootRenderFunction,
  hydrate?: RootHydrateFunction
): CreateAppFunction<HostElement> {
  return function createApp(rootComponent, rootProps = null) {
    if (!isFunction(rootComponent)) {
      rootComponent = { ...rootComponent }
    }

    if (rootProps != null && !isObject(rootProps)) {
      __DEV__ && warn(`root props passed to app.mount() must be an object.`)
      rootProps = null
    }

    const context = createAppContext()
    const installedPlugins = new Set()

    let isMounted = false

    // 定义了一个app对象,用以返回
    const app: App = (context.app = {
      _uid: uid++,
      _component: rootComponent as ConcreteComponent,
      _props: rootProps,
      _container: null,
      _context: context,
      _instance: null,

      version,

      get config() {
        return context.config
      },

      // set是不允许赋值的,赋值后会报错 
      set config(v) {
        if (__DEV__) {
          warn(
            `app.config cannot be replaced. Modify individual options instead.`
          )
        }
      },
      
      use() { ... return app },
      mixin() { ... return app },
      component() { ... return app },
      directive() { ... return app },
      mount() { ... },
      unmount() { ... },
      provide(key, value) { ... return app }
    })

    if (__COMPAT__) {
      installAppCompatProperties(app, context, render)
    }

    return app
  }
}

总结

以上就是对 Vue.createApp(App) 的阅读分析,下篇文章我们将对 demo 中 app.mount("#app") 的实现流程进行分析。

戳链接直达

Vue3源码学习笔记—— Vue.createApp(App) 和 app.mount(“#app”)(三) - 掘金 (juejin.cn)

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐