Vue.prototype.$destroy = function () {
const vm: Component = this
if (vm._isBeingDestroyed) {
return
}
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)
}
// teardown watchers
if (vm._watcher) {
vm._watcher.teardown()
}
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
vm.__patch__(vm._vnode, null)
// fire destroyed hook
callHook(vm, 'destroyed')
// turn off all instance listeners.
vm.$off()
// remove __vue__ reference
if (vm.$el) {
vm.$el.__vue__ = null
}
// release circular reference (#6759)
if (vm.$vnode) {
vm.$vnode.parent = null
}
}
}
可以看到,destroy步骤如下:
remove(parent.$children, vm)从父节点中先移除自己
vm._watcher.teardown() 销毁watchers
vm._data.__ob__.vmCount-- 从数据ob中删除引用
vm.__patch__(vm._vnode, null) 调用当前渲染树上的销毁钩子
callHook(vm, 'destroyed') 调用destroyed钩子
vm.$off()销毁事件监听 ...
到这里差不多就执行完了销毁任务,从而触发了destroyed钩子
一些警告
不要在选项属性或回调上使用箭头函数,比如 created: () => console.log(this.a) 或 vm.$watch('a', newValue => this.myMethod())。因为箭头函数是和父级上下文绑定在一起的,this 不会是如你所预期的 Vue 实例,经常导致 Uncaught TypeError: Cannot read property of undefined或Uncaught TypeError: this.myMethod is not a function 之类的错误。
我们可以看一下Vue是如何执行生命周期函数的:
export function callHook (vm: Component, hook: string) {
const handlers = vm.$options[hook]
if (handlers) {
for (let i = 0, j = handlers.length; i < j; i++) {
try {
handlers[i].call(vm)
} catch (e) {
handleError(e, vm, `${hook} hook`)
}
}
}
if (vm._hasHookEvent) {
vm.$emit('hook:' + hook)
}
}
关于生命周期的源码执行
首先我们先来看一张官网的图: 然后我们来看一下源码里什么时候开始执行各个生命周期的:
1. beforeCreate、created
beforeCreate
和created
钩子在core/instance/init.js
的_init
方法中执行这里主要是初始化一些vm的属性,
initState
主要为定义的data
属性进行obsever
以及处理一些props
、watch
和computed
:2. beforMounted
在执行
beforMounted
的钩子的时候,会进行几部判断:1. 判断存不存在
$el
属性2. 判断存不存在
template
属性:3. mounted
这一步主要是经过了
render --> VNode --> path
步骤后生成了一个真实的dom
节点,并挂载到el
上:4. beforeUpdate
当我们执行dom更新之前,且已经经过
mounted
。会触发的钩子:5. updated
这个钩子函数主要是在异步更新队列中执行,也就是
nextTick
更新dom后会执行的钩子关于什么是
nextTick
?以及Event loop
相关知识,有兴趣可以参考我的这两篇文章:Vue nextTick 机制
Event loop 简介
6. beforeDestroy destroyed
当
$destroy
函数被调用时,会首先触发beforeDestroy
钩子:可以看到,
destroy
步骤如下:remove(parent.$children, vm)
从父节点中先移除自己vm._watcher.teardown()
销毁watchersvm._data.__ob__.vmCount--
从数据ob中删除引用vm.__patch__(vm._vnode, null)
调用当前渲染树上的销毁钩子callHook(vm, 'destroyed')
调用destroyed
钩子vm.$off()
销毁事件监听 ...到这里差不多就执行完了销毁任务,从而触发了
destroyed
钩子一些警告
说到这里,应该明白了为什么不要在选项属性或回调上使用箭头函数了吧...