返回

Vue源码剖析:深入浅出解读$mount()的挂载细节(11)

前端

导语

在前面的文章中,我们已经对Vue.js的mount()方法进行了详细的分析。我们了解了这个方法的基本原理,以及它在Vue.js组件生命周期中的作用。在本文中,我们将继续深入挖掘mount()方法的细节,重点关注closeElement的三个工具方法:processSlotOutlet、processComponent和transforms,以及processAttrs是如何工作的。通过对这些方法的分析,我们将更好地理解Vue.js是如何处理插槽、组件和属性的。

processSlotOutlet

processSlotOutlet方法的作用是处理插槽。它首先会检查当前元素是否有插槽,如果有,则会将插槽的内容添加到元素的子节点中。然后,它会检查元素的父元素是否有插槽,如果有,则会将元素本身添加到父元素的插槽内容中。

export function processSlotOutlet(
  parentVnode: VNode,
  node: VNode,
  slotOutlet: SlotOutlet,
  container?: VNode[],
  host?: ComponentInstance | null | undefined,
  ssr?: boolean
): VNode | null {
  const res = resolveSlot(
    node.data.slot,
    parentVnode,
    parentVnode,
    // slot context
    host,
    node.children, // fallback
    container,
    slotOutlet,
    ssr
  )
  if (res) {
    node.children = res
    if (!ssr) {
      updateSlotCache(parentVnode.data.slot, res)
    }
  }
  return node
}

processComponent

processComponent方法的作用是处理组件。它首先会检查当前元素是否是一个组件,如果是,则会创建一个组件实例。然后,它会将组件实例的el属性添加到元素的子节点中。最后,它会调用组件实例的mount()方法,将组件挂载到DOM中。

export function processComponent(
  parentVnode: VNode,
  node: VNode,
  ssr?: boolean
): VNode | null {
  const Ctor = resolveAsyncComponent(node.type as string)
  if (!Ctor) {
    if (
      process.env.NODE_ENV !== 'production' &&
      typeof node.type === 'function'
    ) {
      warn(
        `Component <${node.type.displayName || node.type.name}> was 
       registered incorrectly. Note that anonymous components must be defined 
       in the template using the "is" directive and registered using the "component" 
       option. See https://vuejs.org/v2/guide/components.html#Anonymous-Components.`
      )
    }
    return node
  }
  // factory-only
  if (Ctor === def) {
    def.__isComponent = false
    parentVnode.data.pendingInsert = true
    if (isAsyncPlaceholder(node)) {
      node.children = resolveAsyncComponent(Ctor) as VNode[]
    }
    if (process.env.NODE_ENV !== 'production') {
      warn(
        `Async component in slot "${parentVnode.data.slot}" is not mounted when 
        its parent is rendered, but mounted after render completes. This 
        removes the guarantee that async components will not be rendered 
        until the entire tree has been rendered, which can cause errors when 
        using async components in slot templates. In the future, we will warn 
        when an async component in slot mounts after the parent has been 
        rendered.`
      )
    }
    return node
  }
  node.componentInstance = createComponentInstance(Ctor, data, context, parentVnode, activeInstance)
  if (ssr) {
    setupComponent(node.componentInstance as Component, parentVnode, parentElm)
  } else if (
    activationHook &&
    !node.componentInstance._isMounted &&
    node.data.keepAlive
  ) {
    (node.componentInstance as Component)._activationChildren.push(activationHook)
  }
  handleSSRApp(node, parentVnode)
  if (
    Ctor.options.abstract
  ) {
    // abstract, not mount
    return node
  }
  setupComponent(node.componentInstance as Component, parentVnode, parentElm)
  // component mount正常挂载
  return node.componentInstance.$mount(parentVnode.elm, parentVnode)
}

transforms

transforms方法的作用是处理属性。它首先会检查当前元素的属性是否有任何需要转换的指令。如果有,则会将指令转换成相应的函数,并将其添加到元素的data.on属性中。

export function transforms(data: VNodeData | undefined): void {
  transformVNodeHook(data, 'created')
  transformVNodeHook(data, 'beforeMount')
  transformVNodeHook(data, 'mounted')
  // we need the second change/remove hook
  // for component & attribute bindings
  transformVNodeHook(data, 'beforeUpdate')
  transformVNodeHook(data, 'updated')
  transformVNodeHook(data, 'beforeDestroy')
  transformVNodeHook(data, 'destroyed')
}

processAttrs

processAttrs方法的作用是处理属性。它首先会将元素的属性复制到data.attrs对象中。然后,它会将元素的属性转换成相应的DOM属性,并将其添加到data.domProps对象中。

function processAttrs(data: VNodeData | undefined, context: Component | null): void {
  const site = data.attrs && data.attrs.slot
  if (site) {
    delete data.attrs.slot
  }
  const cur = data.componentInstance
  if (cur) {
    const oldSlot = cur._slotContext
    cur._slotContext = data
    updateComponentSlot(cur, site, parentVnode)
    cur._slotContext = oldSlot
  } else {
    updateAttrs(data.attrs, data.propsData || {})
  }
}

结语

在本文中,我们对Vue.js的$mount()方法的四个工具方法进行了详细的分析。我们了解了这些方法是如何处理插槽、组件和属性的。通过对这些方法的分析,我们更好地理解了Vue.js是如何实现组件挂载的。