细读Vue2.6.14 Core 源码(4): _init

426 阅读1分钟

细读Vue2.6.14 Core 源码(4): _init

    /**
        @date 2021-08-22
        @description 细读Vue2.6.14 Core 源码(4): _init
    */

壹(序)

上一章走完了 initGlobalAPI,这一章走到真正执行 _init 函数的地方,_init 除了在 new Vue 时会执行,在生成组件时,每个组件也会调用,_init 内部具体的过程在 第二章:After Vue 中的 initMixin 讲过,下面具体分析 _init 内部的执行。

首先是声明 Vue构造函数,然后调用了几个 mixin 函数,下面仔细阅读几个 mixin 函数

贰(initLifecycle)

export function initLifecycle (vm: Component) {
  const options = vm.$options

  // 找到第一个不是 abstract 的 parent,填充此 parent 的 $children
  let parent = options.parent
  if (parent && !options.abstract) {
    while (parent.$options.abstract && parent.$parent) {
      parent = parent.$parent
    }
    parent.$children.push(vm)
  }
  // 一些变量的声明
  vm.$parent = parent
  vm.$root = parent ? parent.$root : vm

  vm.$children = []
  vm.$refs = {}

  vm._watcher = null
  vm._inactive = null
  vm._directInactive = false
  vm._isMounted = false
  vm._isDestroyed = false
  vm._isBeingDestroyed = false
}

叁(initEvents)

初始化事件监听

// initEvents:
export function initEvents (vm: Component) {
  vm._events = Object.create(null)
  vm._hasHookEvent = false
  // 初始化 parent 的事件
  // 注意这里是获取的是 _parentListeners,但并不是 parent 上的,是在自身上面的
  // 比如组件 <HelloWorld msg="Welcome to Your Vue.js App" @handleClick="test" /> 
  // 此处的 listeners 就是 [handleClick]
  const listeners = vm.$options._parentListeners
  if (listeners) {
    updateComponentListeners(vm, listeners)
  }
}

// updateComponentListeners: 
export function updateComponentListeners(
  vm: Component,
  listeners: Object,
  oldListeners: ?Object
) {
  target = vm
  updateListeners(listeners, oldListeners || {}, add, remove, createOnceHandler, vm)
  target = undefined
}

// updateListeners:
export function updateListeners (
  on: Object,
  oldOn: Object,
  add: Function,
  remove: Function,
  createOnceHandler: Function,
  vm: Component
) {
  let name, def, cur, old, event
  // 遍历 on(listeners)
  for (name in on) {
    def = cur = on[name]
    old = oldOn[name]
    // 标准化 event
    event = normalizeEvent(name)
    /* istanbul ignore if */
    if (__WEEX__ && isPlainObject(def)) {
      cur = def.handler
      event.params = def.params
    }
    if (isUndef(cur)) {
      // 当前事件是 undefined 或 null,报警告
      process.env.NODE_ENV !== 'production' && warn(
        `Invalid handler for event "${event.name}": got ` + String(cur),
        vm
      )
    } else if (isUndef(old)) {
      
      if (isUndef(cur.fns)) {
        // old 是 undefined 或 null 且 cur.fns 也是 undefined 或 null,给 on[name] 赋值
        cur = on[name] = createFnInvoker(cur, vm)
      }
      // 只调用一次(once),更新 on[name] 为调用之后使用 $off 移除
      if (isTrue(event.once)) {
        cur = on[name] = createOnceHandler(event.name, cur, event.capture)
      }
      // 调用 $on 处理此事件
      add(event.name, cur, event.capture, event.passive, event.params)
    } else if (cur !== old) {
      // 当前事件与 old 事件不是同一事件,则更新
      old.fns = cur
      on[name] = old
    }
  }
  //  remove(调用 $off) 不存在的事件
  for (name in oldOn) {
    if (isUndef(on[name])) {
      event = normalizeEvent(name)
      remove(event.name, oldOn[name], event.capture)
    }
  }
}


肆(initRender)

export function initRender (vm: Component) {
  vm._vnode = null // the root of the child tree
  vm._staticTrees = null // v-once cached trees
  const options = vm.$options
  // _parentVnode 就是自身 vnode
  const parentVnode = vm.$vnode = options._parentVnode // the placeholder node in parent tree
  const renderContext = parentVnode && parentVnode.context
  vm.$slots = resolveSlots(options._renderChildren, renderContext)
  vm.$scopedSlots = emptyObject
  vm._c = (a, b, c, d) => createElement(vm, a, b, c, d, false)
  vm.$createElement = (a, b, c, d) => createElement(vm, a, b, c, d, true)

  const parentData = parentVnode && parentVnode.data

  /* istanbul ignore else */
  if (process.env.NODE_ENV !== 'production') {
    defineReactive(vm, '$attrs', parentData && parentData.attrs || emptyObject, () => {
      !isUpdatingChildComponent && warn(`$attrs is readonly.`, vm)
    }, true)
    defineReactive(vm, '$listeners', options._parentListeners || emptyObject, () => {
      !isUpdatingChildComponent && warn(`$listeners is readonly.`, vm)
    }, true)
  } else {
    // 设置 $attrs, $listeners 为响应式数据
    defineReactive(vm, '$attrs', parentData && parentData.attrs || emptyObject, null, true)
    defineReactive(vm, '$listeners', options._parentListeners || emptyObject, null, true)
  }
}

伍(callHook)

export function callHook (vm: Component, hook: string) {
  // 调用生命周期挂钩时禁用 dep 收集
  // 通过将 Dep.target 设置为 undefined 实现
  pushTarget()
  // 获取钩子函数
  const handlers = vm.$options[hook]
  const info = `${hook} hook`
  if (handlers) {
    // 调用钩子函数
    for (let i = 0, j = handlers.length; i < j; i++) {
      invokeWithErrorHandling(handlers[i], vm, null, vm, info)
    }
  }
  // 在组件使用时可以传入 @hook:[hookEvent]="method" 也能调用 hook 比如 <HelloWorld @hook:beforeCreate="beforeCreateTest" />
  // 在 vm.$on 中监听的事件,在这里通过 vm.$emit 调用
  if (vm._hasHookEvent) {
    vm.$emit('hook:' + hook)
  }
  // 取消
  popTarget()
}

陆(others)

其他都是一些的声明属性,在After Vue中可以看到。

终(导航)

细读Vue2.6.14 Core 源码(1): 入口

细读Vue2.6.14 Core 源码(2): After Vue

细读Vue2.6.14 Core 源码(3): initGlobalAPI

细读Vue2.6.14 Core 源码(4): _init

细读Vue2.6.14 Core 源码(5): initState

细读Vue2.6.14 Core 源码(6): defineReactive

细读Vue2.6.14 Core 源码(7): $mount

细读Vue2.6.14 Core 源码(8): $createElement