细读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 源码(2): After Vue
细读Vue2.6.14 Core 源码(3): initGlobalAPI
细读Vue2.6.14 Core 源码(5): initState
细读Vue2.6.14 Core 源码(6): defineReactive