Skip to content

Vue3 源码阅读

createApp 函数

代码所在位置:packages/runtime-dom/src/index.ts

源码

点击查看源码
ts
export const createApp = ((...args) => {
  const app = ensureRenderer().createApp(...args)

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

  const { mount } = app
  app.mount = (containerOrSelector: Element | ShadowRoot | string): any => {
    const container = normalizeContainer(containerOrSelector)
    if (!container) return

    const component = app._component
    if (!isFunction(component) && !component.render && !component.template) {
      // __UNSAFE__
      // Reason: potential execution of JS expressions in in-DOM template.
      // The user must make sure the in-DOM template is trusted. If it's
      // rendered by the server, the template should not contain any user data.
      component.template = container.innerHTML
      // 2.x compat check
      if (__COMPAT__ && __DEV__) {
        for (let i = 0; i < container.attributes.length; i++) {
          const attr = container.attributes[i]
          if (attr.name !== 'v-cloak' && /^(v-|:|@)/.test(attr.name)) {
            compatUtils.warnDeprecation(
              DeprecationTypes.GLOBAL_MOUNT_CONTAINER,
              null,
            )
            break
          }
        }
      }
    }

    // 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>

解析

createApp 函数是 Vue3 中的一个核心函数,用于创建一个 Vue 应用。createApp 函数接受一个参数,这个参数是一个组件对象。createApp 函数的实现非常简单,只是创建一个应用对象,并且返回这个应用对象。

代码可以看到通过调用 ensureRenderer().createApp 创建一个应用对象,然后重写了 mount 方法,最后返回了 app 实例。

ensureRenderer 如下

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

从代码中可以看到 ensureRenderer 函数中 render 是一个全局变量,如果 render 存在则直接返回,否则调用 createRenderer 函数创建一个渲染器。

这种写法很巧妙,通过全局变量 render 来缓存渲染器,避免重复创建渲染器,同时也能够确保核心渲染器逻辑可以在用户只从 Vue 导入响应性工具时进行树摇优化,从而减少打包体积。

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

深入查看 createRenderer 函数,可以发现 createRenderer 返回了 baseCreateRenderer 函数,这个函数是真正创建渲染器的函数。

h 函数

代码所在位置:packages/runtime-core/src/h.ts

源码

点击查看源码
ts
export function h(type: any, propsOrChildren?: any, children?: any): VNode {
  const l = arguments.length
  if (l === 2) {
    if (isObject(propsOrChildren) && !isArray(propsOrChildren)) {
      // single vnode without props
      if (isVNode(propsOrChildren)) {
        return createVNode(type, null, [propsOrChildren])
      }
      // props without children
      return createVNode(type, propsOrChildren)
    } else {
      // omit props
      return createVNode(type, null, propsOrChildren)
    }
  } else {
    if (l > 3) {
      children = Array.prototype.slice.call(arguments, 2)
    } else if (l === 3 && isVNode(children)) {
      children = [children]
    }
    return createVNode(type, propsOrChildren, children)
  }
}

使用

js
// 创建一个 div 节点
const vnode = h('div')

// 创建一个带有属性的 div 节点
const vnode = h('div', { id: 'app' })

// 创建一个带有子节点和属性的 div 节点
const vnode = h('div', { id: 'app' }, ['foo'])

解析

h 函数是 Vue3 中的一个核心函数,用于创建虚拟节点。h 函数接受三个参数,第一个参数是节点的类型,第二个参数是节点的属性或者子节点,第三个参数是子节点。h 函数的实现非常简单,只是根据参数的不同,创建不同的虚拟节点。

总的来说,h 函数的作用就是创建虚拟节点,虚拟节点是一个轻量级的对象,用于描述真实 DOM 节点的结构。虚拟节点的好处是可以在不操作真实 DOM 的情况下,对 DOM 进行操作,这样可以提高性能。