细读Vue2.6.14 Core 源码(2): After Vue
/**
@date 2021-08-10
@description 细读Vue2.6.14 Core 源码(2): After Vue
*/
壹(序)
之前简单的了解了入口,知道这时候做了引入 Vue构造函数 以及初始化一些 全局API,和一些 ssr 的事情(暂不关注),这篇文章关注一下Vue构造函数的入口文件,以及对Vue做了什么事情
function Vue (options) {
// 调用_init函数
this._init(options)
}
// Vue的prototype属性上添加_init
initMixin(Vue)
// Vue的prototype属性上添加 $data(此时为undefined), $props(此时为undefined), $set, $delete, $watch
stateMixin(Vue)
// Vue的prototype属性上添加 $on, $once, $off, $emit方法
eventsMixin(Vue)
// Vue的prototype属性上添加 _update, $forceUpdate, $destroy
lifecycleMixin(Vue)
// Vue的prototype属性上添加 _render, $nextTick, $destroy
renderMixin(Vue)
首先是声明 Vue构造函数,然后调用了几个 mixin 函数,下面仔细阅读几个 mixin 函数
贰(initMixin)
Vue函数里面最重要的代码就一句(还有一个警告,这里删掉了): this._init(options),这个 _init 函数,就在 initMixin 中添加的(这里只是声明,调用在后面,所以真正调用的时候再看内部代码):
let uid = 0
export function initMixin (Vue: Class<Component>) {
Vue.prototype._init = function (options?: Object) {
const vm: Component = this
// 记录 uid,递增
vm._uid = uid++
// a flag to avoid this being observed
vm._isVue = true
// merge options
if (options && options._isComponent) {
// 内部组件的 $options,有进行优化提升性能
initInternalComponent(vm, options)
} else {
// $options 属性
vm.$options = mergeOptions(
resolveConstructorOptions(vm.constructor),
options || {},
vm
)
}
// _renderProxy属性,非生产环境会通过 Proxy 重写 has 或 get
if (process.env.NODE_ENV !== 'production') {
initProxy(vm)
} else {
// 生产环境即为本身
vm._renderProxy = vm
}
// expose real self
vm._self = vm
/**
* 初始化以下值,并根据判断 push 自身到 parent 的 $children
* vm.$parent = vm.$options.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
*/
initLifecycle(vm)
// 初始化事件绑定,获取 _parentListeners,并绑定到自身 vm 中
initEvents(vm)
// 初始化 _node, $node, _staticTrees, $slots, $scopedSlots, $createElement, 设置$attrs, $listeners 为响应式数据
initRender(vm)
// 调用 beforeCreate 钩子函数
callHook(vm, 'beforeCreate')
// 初始化 inject 配置
initInjections(vm) // resolve injections before data/props
// 处理 props, methods, data, computed, watch
initState(vm)
// 处理 provide 对象
initProvide(vm) // resolve provide after data/props
// 调用 created 钩子函数
callHook(vm, 'created')
// 如果未传入 el, 则需要调用 $mount 方法
if (vm.$options.el) {
vm.$mount(vm.$options.el)
}
}
}
叁(stateMixin)
stateMixin 函数用于给 Vue.prototype 添加 $data
, $props
, $set
, $delete
, $watch
属性方法
export function stateMixin (Vue: Class<Component>) {
const dataDef = {}
// _data 在 initData 中声明
dataDef.get = function () { return this._data }
const propsDef = {}
// _props 在 initProps 中声明
propsDef.get = function () { return this._props }
// 向Vue.prototype中写入 $data 和 $props
Object.defineProperty(Vue.prototype, '$data', dataDef)
Object.defineProperty(Vue.prototype, '$props', propsDef)
// 向Vue.prototype中写入 $set 和 $delete
Vue.prototype.$set = set
Vue.prototype.$delete = del
// 向Vue.prototype中写入 $watch
Vue.prototype.$watch = function (
expOrFn: string | Function,
cb: any,
options?: Object
): Function {
const vm: Component = this
// cb 为纯对象时,处理之后再调用(createWatcher 中会重新调用 $watch 函数)
if (isPlainObject(cb)) {
return createWatcher(vm, expOrFn, cb, options)
}
options = options || {}
options.user = true
// 声明 watcher
const watcher = new Watcher(vm, expOrFn, cb, options)
// 立即触发回调
if (options.immediate) {
const info = `callback for immediate watcher "${watcher.expression}"`
pushTarget()
// 调用 cb
invokeWithErrorHandling(cb, vm, [watcher.value], vm, info)
popTarget()
}
// return unwatch方法
return function unwatchFn () {
watcher.teardown()
}
}
}
肆(eventsMixin)
eventsMixin 函数用于添加 $on
, $once
, $emit
, $off
属性方法
export function eventsMixin (Vue: Class<Component>) {
const hookRE = /^hook:/
Vue.prototype.$on = function (event: string | Array<string>, fn: Function): Component {
const vm: Component = this
if (Array.isArray(event)) {
// 数组的情况,遍历数组,再调子项
for (let i = 0, l = event.length; i < l; i++) {
vm.$on(event[i], fn)
}
} else {
// 初始化 _events[event] 或 在 _events[event] 初始化后调 push 方法
(vm._events[event] || (vm._events[event] = [])).push(fn)
// optimize hook:event cost by using a boolean flag marked at registration
// instead of a hash lookup
if (hookRE.test(event)) {
vm._hasHookEvent = true
}
}
return vm
}
Vue.prototype.$once = function (event: string, fn: Function): Component {
const vm: Component = this
// 内部调用 $off 方法,以实现只调用一次
function on () {
vm.$off(event, on)
fn.apply(vm, arguments)
}
on.fn = fn
vm.$on(event, on)
return vm
}
Vue.prototype.$off = function (event?: string | Array<string>, fn?: Function): Component {
const vm: Component = this
// 未提供参数,移除所有事件监听器
if (!arguments.length) {
vm._events = Object.create(null)
return vm
}
// event 为数组
if (Array.isArray(event)) {
for (let i = 0, l = event.length; i < l; i++) {
vm.$off(event[i], fn)
}
return vm
}
const cbs = vm._events[event]
// 未找到事件
if (!cbs) {
return vm
}
// 未传入fn
if (!fn) {
vm._events[event] = null
return vm
}
// specific handler
let cb
let i = cbs.length
while (i--) {
cb = cbs[i]
if (cb === fn || cb.fn === fn) {
cbs.splice(i, 1)
break
}
}
return vm
}
Vue.prototype.$emit = function (event: string): Component {
const vm: Component = this
let cbs = vm._events[event]
// _events中能找到 event 则表示有相应的 $on 事件,则调用该事件
if (cbs) {
cbs = cbs.length > 1 ? toArray(cbs) : cbs
const args = toArray(arguments, 1)
const info = `event handler for "${event}"`
for (let i = 0, l = cbs.length; i < l; i++) {
invokeWithErrorHandling(cbs[i], vm, args, vm, info)
}
}
return vm
}
}
伍(lifecycleMixin)
export function lifecycleMixin (Vue: Class<Component>) {
Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) {
const vm: Component = this
const prevEl = vm.$el
const prevVnode = vm._vnode
// 设置 activeInstance
const restoreActiveInstance = setActiveInstance(vm)
vm._vnode = vnode
if (!prevVnode) {
// 初始渲染
vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false /* removeOnly */)
} else {
// 更新渲染
vm.$el = vm.__patch__(prevVnode, vnode)
}
// 完成重新渲染后,重置 activeInstance 为 null
restoreActiveInstance()
// update __vue__ reference
if (prevEl) {
prevEl.__vue__ = null
}
if (vm.$el) {
vm.$el.__vue__ = vm
}
// if parent is an HOC, update its $el as well
if (vm.$vnode && vm.$parent && vm.$vnode === vm.$parent._vnode) {
vm.$parent.$el = vm.$el
}
// updated hook is called by the scheduler to ensure that children are
// updated in a parent's updated hook.
}
Vue.prototype.$forceUpdate = function () {
const vm: Component = this
if (vm._watcher) {
// 有 _watcher 则调用 _watcher 的 update 方法
vm._watcher.update()
}
}
Vue.prototype.$destroy = function () {
const vm: Component = this
if (vm._isBeingDestroyed) {
return
}
// 调用 beforeDestroy hook
callHook(vm, 'beforeDestroy')
vm._isBeingDestroyed = true
// remove self from parent
const parent = vm.$parent
if (parent && !parent._isBeingDestroyed && !vm.$options.abstract) {
remove(parent.$children, vm)
}
// 处理 _watcher 的卸载
if (vm._watcher) {
vm._watcher.teardown()
}
// 处理 _watchers 中所欲 watcher 的卸载
let i = vm._watchers.length
while (i--) {
vm._watchers[i].teardown()
}
// remove reference from data ob
// frozen object may not have observer.
if (vm._data.__ob__) {
vm._data.__ob__.vmCount--
}
// call the last hook...
vm._isDestroyed = true
// invoke destroy hooks on current rendered tree
// 调用 __patch__ 函数,第二个参数传入 null,销毁当前 vnode
vm.__patch__(vm._vnode, null)
// 调用 destroyed hook
callHook(vm, 'destroyed')
// 调用 $off ,且不传参,表示移除所有事件监听器
vm.$off()
// remove __vue__ reference
if (vm.$el) {
vm.$el.__vue__ = null
}
// release circular reference (#6759)
if (vm.$vnode) {
vm.$vnode.parent = null
}
}
}
陆(renderMixin)
export function renderMixin (Vue: Class<Component>) {
// install runtime convenience helpers
// 向 Vue.prototype 中 写入 _o, _n, _s ...属性
installRenderHelpers(Vue.prototype)
Vue.prototype.$nextTick = function (fn: Function) {
return nextTick(fn, this)
}
Vue.prototype._render = function (): VNode {
const vm: Component = this
const { render, _parentVnode } = vm.$options
if (_parentVnode) {
// 如果有 _parentVnode ,则处理插槽
vm.$scopedSlots = normalizeScopedSlots(
_parentVnode.data.scopedSlots,
vm.$slots,
vm.$scopedSlots
)
}
// set parent vnode. this allows render functions to have access
// to the data on the placeholder node.
vm.$vnode = _parentVnode
// render self
let vnode
try {
currentRenderingInstance = vm
vnode = render.call(vm._renderProxy, vm.$createElement)
} catch (e) {
// 处理 render error
handleError(e, vm, `render`)
vnode = vm._vnode
} finally {
currentRenderingInstance = null
}
// if the returned array contains only a single node, allow it
if (Array.isArray(vnode) && vnode.length === 1) {
vnode = vnode[0]
}
// return empty vnode in case the render function errored out
if (!(vnode instanceof VNode)) {
vnode = createEmptyVNode()
}
// set parent
vnode.parent = _parentVnode
return vnode
}
}
终(导航)
细读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