• 【Vue2.0源码学习】生命周期篇-销毁阶段(destroy)


    1. 前言

    接下来到了生命周期流程的最后一个阶段——销毁阶段。从官方文档给出的生命周期流程图中可以看到,当调用了vm.$destroy方法,Vue实例就进入了销毁阶段,该阶段所做的主要工作是将当前的Vue实例从其父级实例中删除,取消当前实例上的所有依赖追踪并且移除实例上的所有事件监听器。也就是说,当这个阶段完成之后,当前的Vue实例的整个生命流程就全部走完了,最终“寿终正寝”了。

    img

    本篇文章就来分析一下在销毁阶段都做了哪些工作。

    2. 销毁阶段分析

    上文说了,当调用了实例的$destroy方法之后,当前实例就进入了销毁阶段。所以分析销毁阶段就是分析$destroy方法的内部实现。该方法的定义位于源码的src/core/instance.lifecycle.js中,如下:

    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
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    可以看到,在上述代码中,首先判断当前实例的_isBeingDestroyed属性是否为true,因为该属性标志着当前实例是否处于正在被销毁的状态,如果它为true,则直接return退出函数,防止反复执行销毁逻辑。如下:

    const vm: Component = this
    if (vm._isBeingDestroyed) {
      return
    }
    
    • 1
    • 2
    • 3
    • 4

    接着,触发生命周期钩子函数beforeDestroy,该钩子函数的调用标志着当前实例正式开始销毁。如下:

    callHook(vm, 'beforeDestroy')
    
    • 1

    接下来,就进入了当前实例销毁的真正逻辑。

    首先,需要将当前的Vue实例从其父级实例中删除,如下:

    const parent = vm.$parent
    if (parent && !parent._isBeingDestroyed && !vm.$options.abstract) {
      remove(parent.$children, vm)
    }
    
    • 1
    • 2
    • 3
    • 4

    上面代码表示:如果当前实例有父级实例,同时该父级实例没有被销毁并且不是抽象组件,那么就将当前实例从其父级实例的$children属性中删除,即将自己从父级实例的子实例列表中删除。

    把自己从父级实例的子实例列表中删除之后,接下来就开始将自己身上的依赖追踪和事件监听移除。

    我们知道, 实例身上的依赖包含两部分:一部分是实例自身依赖其他数据,需要将实例自身从其他数据的依赖列表中删除;另一部分是实例内的数据对其他数据的依赖(如用户使用$watch创建的依赖),也需要从其他数据的依赖列表中删除实例内数据。所以删除依赖的时候需要将这两部分依赖都删除掉。如下:

    // teardown watchers
    if (vm._watcher) {
      vm._watcher.teardown()
    }
    let i = vm._watchers.length
    while (i--) {
      vm._watchers[i].teardown()
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    在上述代码中,首先执行vm._watcher.teardown()将实例自身从其他数据的依赖列表中删除,teardown方法的作用是从所有依赖向的Dep列表中将自己删除。然后,在前面文章介绍initState函数时我们知道,所有实例内的数据对其他数据的依赖都会存放在实例的_watchers属性中,所以我们只需遍历_watchers,将其中的每一个watcher都调用teardown方法,从而实现移除实例内数据对其他数据的依赖。

    接下来移除实例内响应式数据的引用、给当前实例上添加_isDestroyed属性来表示当前实例已经被销毁,同时将实例的VNode树设置为null,如下:

    if (vm._data.__ob__) {
      vm._data.__ob__.vmCount--
    }
    vm._isDestroyed = true
    vm.__patch__(vm._vnode, null)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    接着,触发生命周期钩子函数destroyed,如下:

    callHook(vm, 'destroyed')
    
    • 1

    最后,调用实例的vm.$off方法(关于该方法在后面介绍实例方法时会详细介绍),移除实例上的所有事件监听器。如下:

    vm.$off()
    
    • 1

    最后,再移除一些相关属性的引用,至此,当前实例算是销毁完毕。

    3. 总结

    本篇文章介绍了生命周期流程的最后一个阶段——销毁阶段。

    我们知道了,当调用了实例上的vm.$destory方法后,实例就进入了销毁阶段,在该阶段所做的主要工作是将当前的Vue实例从其父级实例中删除,取消当前实例上的所有依赖追踪并且移除实例上的所有事件监听器。并且对照源码将所做的工作都进行了逐行分析。

  • 相关阅读:
    1.7.4、计算机网络体系结构中的术语
    Grander因果检验(格兰杰)原理+操作+解释
    (附源码)springboot宠物医疗服务网站 毕业设计688413
    使用Vue的transition组件写一个数字滚动竟然如此简单
    1.3.2有理数减法(第一课时)作业设计
    springboot自定义表格(动态合并单元格)
    PostgreSQL常用管理命令
    【论文阅读】(CVPR2023)用于半监督医学图像分割的双向复制粘贴
    R语言七天入门教程七:项目实战
    JSON parse error: Cannot deserialize instance of `xxx` out of START_ARRAY token
  • 原文地址:https://blog.csdn.net/weixin_46862327/article/details/133632171