Vue3.0源码学习——初始化流程分析(1.实例创建过程)

481 阅读2分钟

「这是我参与2022首次更文挑战的第1天,活动详情查看:2022首次更文挑战」。

前言

如何搭建环境及调试源码 Vue3.0源码学习——环境搭建

Vue实例创建的过程

如何创建实例

  • 还是从 todomvc 开始调试(路径 /packages/vue/examples/composition/todomvc),在 createApp() 处打上第一个断点

图片.png

  • 单步调试 F11 进入,找到入口文件 /packages/runtime-dom/src/index.ts

图片.png

  • createApp() 返回的 app 就是创建的实例
export const createApp = ((...args) => {
  const app = ensureRenderer().createApp(...args)

  ...

  const { mount } = app
  app.mount = (containerOrSelector: Element | ShadowRoot | string): any => {
    ...
  }
  // 最终返回
  return app
}) as CreateAppFunction<Element>

实例在哪里创建

  • app 是从 ensureRenderer().createApp() 中返回,在当前文件中找到 ensureRenderer()

图片.png

  • renderer 一开始未定义,因此实际返回 createRender() 单步进入 createRenderer() 位置 /packages/runtime-core/src/renderer.ts

图片.png

  • 返回的是一个 baseCreateRenderer() 再次进入,这是Vue3中最大的函数,是一个渲染器
  • 直接翻到这个函数的最下面大概2300多行,看到返回了一个对象,找到 createApp
  return {
    render,
    hydrate,
    createApp: createAppAPI(render, hydrate)
  }
  • createApp 执行了一个 createAppAPI 函数,进入位置 /packages/runtime-core/src/apiCreateApp.ts

图片.png

  • 终于看到了 createApp 的庐山真面目

图片.png

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

    const app: App = (context.app = {
      _uid: uid++,
      _component: rootComponent as ConcreteComponent,
      _props: rootProps,
      _container: null,
      _context: context,
      _instance: null,

      ...

      use(plugin: Plugin, ...options: any[]) {
          ...
      },

      mixin(mixin: ComponentOptions) {
        ...
      },

      component(name: string, component?: Component): any {
        ...
      },

      directive(name: string, directive?: Directive) {
        ...
      },

      mount(
        rootContainer: HostElement,
        isHydrate?: boolean,
        isSVG?: boolean
      ): any {
        ...
      },

      unmount() {
        ...
      },

      provide(key, value) {
        ...
    })

    ...

    return app
  }
}
  • createApp().mount('#app') 执行的就是 app.mount() 这个函数
  • 主要作用是将 createApp() 中传入的数据和状态转换为真实 dom,并追加到宿主元素 #app,详细执行过程将在下一篇文章中进行讲解
      mount(
        rootContainer: HostElement,
        isHydrate?: boolean,
        isSVG?: boolean
      ): any {
        if (!isMounted) {
          // 创建虚拟dom
          const vnode = createVNode(
            rootComponent as ConcreteComponent,
            rootProps
          )
          // store app context on the root VNode.
          // this will be set on the root instance on initial mount.
          vnode.appContext = context

          // HMR root reload 
          // 转化为真实dom并挂载到rootContainer
          if (__DEV__) {
            context.reload = () => {
              render(cloneVNode(vnode), rootContainer, isSVG)
            }
          }

          if (isHydrate && hydrate) {
            hydrate(vnode as VNode<Node, Element>, rootContainer as any)
          } else {
            render(vnode, rootContainer, isSVG)
          }
          isMounted = true
          app._container = rootContainer
          // for devtools and telemetry
          ;(rootContainer as any).__vue_app__ = app

          if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
            app._instance = vnode.component
            devtoolsInitApp(app, version)
          }

          return getExposeProxy(vnode.component!) || vnode.component!.proxy
        } else if (__DEV__) {
          ...
        }
      },

往期回顾 Vue3.0源码学习——环境搭建

Vue3.0源码学习——整体架构