vue3 首次页面加载 Vue.createApp(App).mount('#counter')

94 阅读2分钟

随着前端越来越卷 并且面试题越来越常见的问到vue3源码中的问题 现在不得把源码学习起来,此次记录只会一条线一条线的走下去

一、下载vue3源码并运行

源码下载网上有很多推荐链接vue3源码安装推荐连接

二、初始化

根据上面的连接操作过后会我们新建一个test.html在路径/packages/vue/examples

<script src="../../dist/vue.global.js"></script>
<div id="counter">
   
</div>
<script>
   const App = {
   
   }
 
  Vue.createApp(App).mount('#counter')
</script>

入口文件 /packages/vue/src/index.ts

目前这个流程我们只需要看最后一行export * from '@vue/runtime-dom'这个代码 点进去 进入/package/runtime-dome/index.ts 找到createApp方法 我们只看其中关键的代码



export const createApp = ((...args) => {
  //初始化vue
  const app = ensureRenderer().createApp(...args) //初始化component,config,directive,mixin,mount,provide,runWithContext,unmount,use

  const { mount } = app
  app.mount = (containerOrSelector: Element | ShadowRoot | string): any => {
    // clear content before mounting
    container.innerHTML = ''
    const proxy = mount(container, false, resolveRootNamespace(container))
    if (container instanceof Element) {
      container.removeAttribute('v-cloak')
      container.setAttribute('data-v-app', '')
    }
    return proxy
  }

  return app
}) as CreateAppFunction<Element>

解析 const app = ensureRenderer().createApp(...args)

找到ensureRenderer

function ensureRenderer() {
  return (
    renderer ||
    (renderer = createRenderer<Node, Element | ShadowRoot>(rendererOptions))
  )
} 

找到createRenderer 来到/package/runtime-core/renderer.ts

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

找到baseCreateRenderer

function baseCreateRenderer(
  options: RendererOptions,
  createHydrationFns?: typeof createHydrationFunctions,
): any {
`....省略里面有很多方法 大概两千行左右这里我们只看
return {
    ···省略,
    createApp: createAppAPI(render, hydrate),//初始化mount的时候没有用到这两个参数
  }

}

进入createAppAPI

export function createAppAPI<HostElement>(
  render: RootRenderFunction<HostElement>,
  hydrate?: RootHydrateFunction,
): CreateAppFunction<HostElement> {
  debugger
  return function createApp(rootComponent, rootProps = null) {
      const app: App = (context.app = {
       //省略
      mount(
        rootContainer: HostElement,
        isHydrate?: boolean,
        namespace?: boolean | ElementNamespace,
      ): any {
        if (!isMounted) {

          const vnode = createVNode(rootComponent, rootProps)
          // store app context on the root VNode.
          // this will be set on the root instance on initial mount.
          vnode.appContext = context

          if (namespace === true) {
            namespace = 'svg'
          } else if (namespace === false) {
            namespace = undefined
          }

          // HMR root reload
          if (__DEV__) {
            context.reload = () => {
              // casting to ElementNamespace because TS doesn't guarantee type narrowing
              // over function boundaries
              render(
                cloneVNode(vnode),
                rootContainer,
                namespace as ElementNamespace,
              )
            }
          }

          if (isHydrate && hydrate) {
            hydrate(vnode as VNode<Node, Element>, rootContainer as any)
          } else {
            //初始化的时候走这里
            render(vnode, rootContainer, namespace)
          }
          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__) {
          warn(
            `App has already been mounted.\n` +
              `If you want to remount the same app, move your app creation logic ` +
              `into a factory function and create fresh app instances for each ` +
              `mount - e.g. \`const createMyApp = () => createApp(App)\``,
          )
        }
      },
    })

    return app
  }
}

最终找到了我们的mount方法 实际上首次Vue.createApp(App).mount('#counter')最终走到了这里