深入理解Vue父子组件生命周期执行顺序及钩子函数


Posted in Javascript onAugust 12, 2018

先附一张官网上的vue实例的生命周期图,每个Vue实例在被创建的时候都需要经过一系列的初始化过程,例如需要设置数据监听,编译模板,将实例挂载到DOM并在数据变化时更新DOM等。同时在这个过程中也会运行一些叫做生命周期钩子的函数(回调函数),这给了用户在不同阶段添加自己代码的机会。

1、vue的生命周期图

深入理解Vue父子组件生命周期执行顺序及钩子函数

在vue实例的整个生命周期的各个阶段,会提供不同的钩子函数以供我们进行不同的操作。先列出vue官网上对各个钩子函数的详细解析。

生命周期钩子

生命周期钩子     详细
beforeCreate 在实例初始化之后,数据观测(data observer) 和 event/watcher 事件配置之前被调用。
created 实例已经创建完成之后被调用。在这一步,实例已完成以下的配置:数据观测(data observer),属性和方法的运算, watch/event 事件回调。然而,挂载阶段还没开始,$el 属性目前不可见。
beforeMount 在挂载开始之前被调用:相关的 render 函数首次被调用。
mounted el 被新创建的 vm.$el 替换,并挂载到实例上去之后调用该钩子。如果 root 实例挂载了一个文档内元素,当 mounted 被调用时 vm.$el 也在文档内。
beforeUpdate 数据更新时调用,发生在虚拟 DOM 重新渲染和打补丁之前。你可以在这个钩子中进一步地更改状态,这不会触发附加的重渲染过程。
updated 由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。当这个钩子被调用时,组件 DOM 已经更新,所以你现在可以执行依赖于 DOM 的操作。
activated keep-alive 组件激活时调用。
deactivated keep-alive 组件停用时调用。
beforeDestroy 实例销毁之前调用。在这一步,实例仍然完全可用。
destroyed Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也

实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。

2、实际操作

下面我们在实际的代码执行过程中理解父子组件生命周期创建过程以及钩子函数执行的实时状态变化。

测试基于下面的代码,引入vue.js文件后即可执行。(打开页面后,再按一次刷新会自动进入debugger状态)

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <style>
    
  </style>
</head>  
<body>
<div id="app">
  <p>{{message}}</p>
  <keep-alive>
    <my-components :msg="msg1" v-if="show"></my-components>
  </keep-alive>
</div>
</body>
<script src="../../node_modules/vue/dist/vue.js"></script>
<script>
  var child = {
    template: '<div>from child: {{childMsg}}</div>',
    props: ['msg'],
    data: function() {
      return {
        childMsg: 'child'
      }  
    },
    beforeCreate: function () {
      debugger;
    },
    created: function () {
      debugger;
    },
    beforeMount: function () {
      debugger;
    },
    mounted: function () {
      debugger;
    },
    deactivated: function(){
      alert("keepAlive停用");
    },
    activated: function () {
      console.log('component activated');
    },
    beforeDestroy: function () {
      console.group('beforeDestroy 销毁前状态===============》');
      var state = {
        'el': this.$el,
        'data': this.$data,
        'message': this.message
      }
      console.log(this.$el);
      console.log(state);
    },
    destroyed: function () {
      console.group('destroyed 销毁完成状态===============》');
      var state = {
        'el': this.$el,
        'data': this.$data,
        'message': this.message
      }
      console.log(this.$el);
      console.log(state);
    },
  };
  var vm = new Vue({
    el: '#app',
    data: {
        message: 'father',
        msg1: "hello",
        show: true
      },
    beforeCreate: function () {
      debugger;
    },
    created: function () {
      debugger;
    },
    beforeMount: function () {
      debugger;
    },
    mounted: function () {
      debugger;  
    },
    beforeUpdate: function () {
      alert("页面视图更新前");
      
    },
    updated: function () {
      alert("页面视图更新后");
    },
    beforeDestroy: function () {
      console.group('beforeDestroy 销毁前状态===============》');
      var state = {
        'el': this.$el,
        'data': this.$data,
        'message': this.message
      }
      console.log(this.$el);
      console.log(state);
    },
    destroyed: function () {
      console.group('destroyed 销毁完成状态===============》');
      var state = {
        'el': this.$el,
        'data': this.$data,
        'message': this.message
      }
      console.log(this.$el);
      console.log(state);
    },
    components: {
      'my-components': child
    }
  });
</script>
</html>

3.1、生命周期调试

首先我们创建了一个Vue实例vm,将其挂载到页面中id为“app”的元素上。

3.1.1、根组件的beforeCreate阶段

深入理解Vue父子组件生命周期执行顺序及钩子函数

可以看出,在调用beforeCreate()函数时,只进行了一些必要的初始化操作(例如一些全局的配置和根实例的一些属性初始化),此时data属性为undefined,没有可供操作的数据。

3.1.2、根组件的Created阶段

深入理解Vue父子组件生命周期执行顺序及钩子函数

调用Created()函数,在这一步,实例已完成以下的配置:数据代理和动态数据绑定(data observer),属性和方法的运算, watch/event 事件回调。然而,挂载阶段还没开始,$el 属性目前不可见。

3.1.3、根组件的beforeMount阶段

深入理解Vue父子组件生命周期执行顺序及钩子函数

深入理解Vue父子组件生命周期执行顺序及钩子函数

在调用boforeMount()函数前首先会判断对象是否有el选项。如果有的话就继续向下编译,如果没有el选项,则停止编译,也就意味着停止了生命周期,直到在该vue实例上调用vm.$mount(el)

在这个例子中,我们有el元素,因此会调用boforeMount()函数,此时已经开始执行模板解析函数,但还没有将$el元素挂载页面,页面视图因此也未更新。在标红处,还是 {{message}},这里就是应用的Virtual DOM(虚拟Dom)技术,先把坑占住了。到后面mounted挂载的时候再把值渲染进去。

3.1.4、子组件的beforeCreate、Created、beforeMount、Mounted阶段

在父组件执行beforeMount阶段后,进入子组件的beforeCreate、Created、beforeMount阶段,这些阶段和父组件类似,按下不表。beforeMount阶段后,执行的是Mounted阶段,该阶段时子组件已经挂载到父组件上,并且父组件随之挂载到页面中。

由下图可以知道,在beforeMount阶段之后、Mounted阶段之前,数据已经被加载到视图上了,即$el元素被挂载到页面时触发了视图的更新。

深入理解Vue父子组件生命周期执行顺序及钩子函数

3.1.5、子组件的activated阶段

我们发现在子父组件全部挂载到页面之后被触发。这是因为子组件my-components被<keep-alive> 包裹,随$el的挂载被触发。如果子组件没有被<keep-alive>包裹,那么该阶段将不会被触发。

深入理解Vue父子组件生命周期执行顺序及钩子函数

3.1.6、父组件的mounted阶段

mounted执行时:此时el已经渲染完成并挂载到实例上。

至此,从Vue实例的初始化到将新的模板挂载到页面上的阶段已经完成,退出debugger。下面我们来看一下deactivated、beforeUpdate、updated、beforeDestroy、destroyed钩子函数。

3.2、deactivated、beforeUpdate、updated阶段

由生命周期函数可知:当数据变化后、虚拟DOM渲染重新渲染页面前会触发beforeUpdate()函数,此时视图还未改变。当虚拟DOM渲染页面视图更新后会触发updated()函数。

深入理解Vue父子组件生命周期执行顺序及钩子函数

我们不妨改变vm.show = false,当修改这个属性时,不仅会触发beforeUpdate、updated函数,还会触发deactivated函数(因为keep-alive 组件停用时调用)。我们不妨想一下deactivated函数会在beforeUpdate后还是updated后调用。

我们在控制台输入vm.show = false。得到三者的调用顺序分别为beforeUpdate、deactivated、updated。我们可以知道的是deactivated函数的触发时间是在视图更新时触发。因为当视图更新时才能知道keep-alive组件被停用了。

深入理解Vue父子组件生命周期执行顺序及钩子函数

深入理解Vue父子组件生命周期执行顺序及钩子函数

深入理解Vue父子组件生命周期执行顺序及钩子函数

3.3、beforeDestroy和destroyed钩子函数间的生命周期

现在我们对Vue实例进行销毁,调用app.$destroy()方法即可将其销毁,控制台测试如下:

深入理解Vue父子组件生命周期执行顺序及钩子函数

我们发现实例依然存在,但是此时变化已经发生在了其他地方。

beforeDestroy钩子函数在实例销毁之前调用。在这一步,实例仍然完全可用。

destroyed钩子函数在Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁(也就是说子组件也会触发相应的函数)。这里的销毁并不指代'抹去',而是表示'解绑'。

销毁时beforeDestory函数的传递顺序为由父到子,destory的传递顺序为由子到父。

4、一些应用钩子函数的想法在created钩子中可以对data数据进行操作,这个时候可以进行ajax请求将返回的数据赋给data。

虽然updated函数会在数据变化时被触发,但却不能准确的判断是那个属性值被改变,所以在实际情况中用computed或match函数来监听属性的变化,并做一些其他的操作。在mounted钩子对挂载的dom进行操作,此时,DOM已经被渲染到页面上。在使用vue-router时有时需要使用<keep-alive></keep-alive>来缓存组件状态,这个时候created钩子就不会被重复调用了,如果我们的子组件需要在每次加载或切换状态的时候进行某些操作,可以使用activated钩子触发。所有的生命周期钩子自动绑定this上下文到实例中,所以不能使用箭头函数来定义一个生命周期方法(例如created: () => this.fetchTodos())。这是导致this指向父级。

5、 小结

加载渲染过程

父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount->子mounted->父mounted

子组件更新过程

父beforeUpdate->子beforeUpdate->子updated->父updated

父组件更新过程

父beforeUpdate->父updated

销毁过程

父beforeDestroy->子beforeDestroy->子destroyed->父destroyed

总结

以上所述是小编给大家介绍的Vue父子组件生命周期执行顺序及钩子函数,希望对大家有所帮助,如果大家有任何疑问欢迎给我留言,小编会及时回复大家的!

Javascript 相关文章推荐
javascript 建设银行登陆键盘
Jun 10 Javascript
Javascript中的Array数组对象详谈
Mar 03 Javascript
js仿小米官网图片轮播特效
Sep 29 Javascript
jQuery实现简单弹窗遮罩效果
Feb 27 Javascript
使用vue框架 Ajax获取数据列表并用BootStrap显示出来
Apr 24 Javascript
最通俗易懂的javascript变量提升详解
Aug 05 Javascript
JS严格模式知识点总结
Feb 27 Javascript
vue实现点击关注后及时更新列表功能
Jun 26 Javascript
es6基础学习之解构赋值
Dec 10 Javascript
解析vue、angular深度作用选择器
Sep 11 Javascript
JS严格模式原理与用法实例分析
Apr 27 Javascript
JavaScript前端面试扁平数据转tree与tree数据扁平化
Jun 14 Javascript
VUE在for循环里面根据内容值动态的加入class值的方法
Aug 12 #Javascript
JS中的两种数据类型及实现引用类型的深拷贝的方法
Aug 12 #Javascript
原生JS封装_new函数实现new关键字的功能
Aug 12 #Javascript
axios向后台传递数组作为参数的方法
Aug 11 #Javascript
让axios发送表单请求形式的键值对post数据的实例
Aug 11 #Javascript
axios的拦截请求与响应方法
Aug 11 #Javascript
解决axios发送post请求返回400状态码的问题
Aug 11 #Javascript
You might like
php基于curl扩展制作跨平台的restfule 接口
2015/05/11 PHP
php实现图片等比例缩放代码
2015/07/23 PHP
apache php mysql开发环境安装教程
2016/07/28 PHP
AppBaseJs 类库 网上常用的javascript函数及其他js类库写的
2010/03/04 Javascript
js实现的点击div区域外隐藏div区域
2014/06/30 Javascript
Vue.js第四天学习笔记
2016/12/02 Javascript
angular十大常见问题
2017/03/07 Javascript
js中document.write和document.writeln的区别
2018/03/11 Javascript
vue组件命名和props命名代码详解
2019/09/01 Javascript
node.js +mongdb实现登录功能
2020/06/18 Javascript
解决vue里a标签值解析变量,跳转页面,前面加默认域名端口的问题
2020/07/22 Javascript
JavaScript Array.flat()函数用法解析
2020/09/02 Javascript
python网络编程之读取网站根目录实例
2014/09/30 Python
实现python版本的按任意键继续/退出
2016/09/26 Python
Python重新加载模块的实现方法
2018/10/16 Python
Python 脚本获取ES 存储容量的实例
2018/12/27 Python
python f-string式格式化听语音流程讲解
2019/06/18 Python
python中的逆序遍历实例
2019/12/25 Python
Pytorch模型转onnx模型实例
2020/01/15 Python
flask框架蓝图和子域名配置详解
2020/01/25 Python
python3 使用openpyxl将mysql数据写入xlsx的操作
2020/05/15 Python
Python性能分析工具py-spy原理用法解析
2020/07/27 Python
Python3爬虫关于代理池的维护详解
2020/07/30 Python
基于OpenCV的路面质量检测的实现
2020/11/04 Python
html5启动原生APP总结
2020/07/03 HTML / CSS
美国新蛋IT数码商城:Newegg.com
2016/07/21 全球购物
ALDO英国官网:加拿大女鞋品牌
2018/02/19 全球购物
一份婚庆公司创业计划书
2014/01/11 职场文书
安全生产网格化管理实施方案
2014/03/01 职场文书
大学生团日活动总结
2015/05/06 职场文书
2015暑期社会实践个人总结
2015/07/13 职场文书
运输公司工作总结
2015/08/11 职场文书
小学班主任工作随笔
2015/08/15 职场文书
班干部竞选演讲稿(精选5篇)
2019/09/24 职场文书
Python实现DBSCAN聚类算法并样例测试
2021/06/22 Python
浅谈Vue的computed计算属性
2022/03/21 Vue.js