vue生命周期(lifecycle)以及对nextTick的理解

  每个Vue实例在被创建的时候,都会经历一系列初始化的过程。比如说需要设置数据监听、模板编译、将实例挂载到DOM结构上并且在数据变化时对DOM结构进行更新等等。Vue允许开发者在不同的生命周期运行一些钩子函数(hook),给开发者在不同的生命周期中添加自己代码的机会。所有的生命周期钩子自动绑定 this 上下文到实例中,因此你可以访问数据,对属性和方法进行运算,这也意味着我们不能够用箭头函数来定义生命周期函数。ok,生命周期的定义就到这里,下面看一下vue具体有哪些生命周期,以及再不同的生命周期里面会发生什么事情。

  官方给的生命周期钩子函数如下:

  #beforeCreate

  #created

  #beforeMount

  #mounted

  #beforeUpdate

  #updated

  #beforeDestroy

  #destroyed

  #errorCaptured

  #activated

  #deactivated

  在这里钩子函数里面,着重看前八个,后面三个在实际开发中基本涉及不到,如果有兴趣了解可移步到官方文档https://cn.vuejs.org/v2/api/#%E9%80%89%E9%A1%B9-%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F%E9%92%A9%E5%AD%90

 1 var vm = {
 2     el: '#app',
 3     data: {
 4         message: 'hello world.'
 5     },
 6     beforeCreate: function() {
 7         // 在实例初始化之后,数据观测 (data observer) 和 event/watcher 事件配置之前被调用。
 8         // 在这个生命周期里,vue实例还只是空壳,数据和dom结构都还没有加载
 9         console.log(this.message);  // undefined
10     },
11     created: function() {
12         // 在实例创建完成后被立即调用。在这一步,实例已完成以下的配置:数据观测 (data observer),属性和方法的运算,watch/event 事件回调。然而,挂载阶段还没开始,$el 目前不可见。
13         // 在这个生命周期里,数据已经加载完成,并且在这里面修改数据并不会出发update函数。
14         // 一般情况下会在这里面做一些异步数据的获取,比如说从服务器获取数据到本地。
15         console.log(this.message);  // 'hello world'
16     },
17     beforeMount: function() {
18         // 在挂载开始之前被调用:相关的 render 函数首次被调用。
19         // 在这里面,虚拟DOM已经加载完毕,但是对应的真实DOM还未加载,在这里面是获取不到真实DOM结点的。
20         // 接下来执行render,加载真实DOM。
21         // 在这里面进行数据修改依旧不会触发update函数。
22     },
23     mounted: function() {
24         // 在这里面,真实DOM、数据、事件处理已经加载完毕,可以进行对应的修改。
25         // 从这里开始进行数据修改都会触发update函数。
26     },
27     beforeUpdate: function() {
28         // 数据更新时调用,发生在虚拟 DOM 打补丁之前。这里适合在更新之前访问现有的 DOM,比如手动移除已添加的事件监听器。
29         // 这个没啥好说的,在数据修改(或者说要重新进行渲染)的时候会触发的钩子函数。
30         // 值得注意的是,千万不要在这里面进行数据修改,否则会陷入死循环!道理你自己思考一下就知道为什么了!
31 
32     },
33     updated: function() {
34         // 由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。
35         // 当这个钩子被调用时,组件 DOM 已经更新,所以你现在可以执行依赖于 DOM 的操作。然而在大多数情况下,你应该避免在此期间更改状态。如果要相应状态改变,通常最好使用计算属性或 watcher 取而代之。
36         // 同样的,在数据修改完毕之后会调用的钩子函数。
37         // 在这里面一样不能进行数据修改!!!谨记!!!
38         
39     },
40     beforeDestroy: function() {
41         // 实例销毁之前调用。在这一步,实例仍然完全可用。
42         // 销毁前执行(手动使用$destroy方法被调用的时候就会执行),一般在这里善后:清除计时器、清除非指令绑定的事件等等。
43     },
44     destroyed: function() {
45         // Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。
46         // 组件的数据绑定、监听...都去掉了,只剩下dom空壳,这里也可以善后。
47     }
48 }

  上面我们说到,在发生数据修改的时候都会触发beforeUpdate和updated这两个钩子函数,但是如果要对特定的修改执行update函数就会变得很麻烦。官方为此提供了另外一种解决办法,那就是$nextTick。$nextTick()是在下次 DOM 更新循环结束之后执行延迟回调,可以在修改数据之后立即使用这个方法,获取更新后的 DOM。简单的理解是:当数据更新了,在dom中渲染后,自动执行该函数。下面举个简单的例子:

 1 <template>
 2   <div>
 3     <span ref="span">{{ message }}</span>
 4   </div>
 5 </template>
 6  
 7 <script>
 8 export default {
 9   name: 'test',
10   data () {
11     return {
12       message: "初始值"
13     }
14   },
15   methods:{
16     changeMsg: function(){
17       this.message =  "修改后的值";
18       console.log(this.$refs.span.innerText);   //原始值
19     }
20   },
21   created() {
22       this.changeMsg();
23   }
24 }
25 </script>

  这是因为在修改完message之后,尚未执行update函数,对应的DOM结构还未发生改变。使用nextTick稍作修改:

 1 <template>
 2   <div>
 3     <span ref="span">{{ message }}</span>
 4   </div>
 5 </template>
 6  
 7 <script>
 8 export default {
 9   name: 'test',
10   data () {
11     return {
12       message: "初始值"
13     }
14   },
15   methods:{
16     changeMsg: function(){
17       this.message =  "修改后的值";
18       this.$nextTick(function(){
19         console.log(that.$refs.span.innerText);  //修改后的值
20       });
21 
22     }
23   },
24   created() {
25       this.changeMsg();
26   }
27 }
28 </script>
29  

  因此,在某些有特殊需求的情况下,是可以使用nextTick来代替update进行使用的。那么什么情况下需要使用到nextTick()呢?

  一、需要在created()钩子函数进行的DOM操作。因为在created()钩子函数执行时,真实DOM 并未进行任何渲染,而此时进行DOM操作无异于徒劳,所以此处一定要将DOM操作的js代码放进nextTick()的回调函数中。与之对应的就是mounted钩子函数,因为该钩子函数执行时所有的DOM挂载已完成。

  二、需要在改变DOM之后基于新的DOM做点什么,对新DOM一系列的js操作都需要放进nextTick()的回调函数中。

Vue.nextTick(callback)的原理:

  Vue是异步执行dom更新的,一旦观察到数据变化,Vue就会开启一个队列,然后把在同一个事件循环 (event loop) 当中观察到数据变化的 watcher 推送进这个队列。如果这个watcher被触发多次,只会被推送到队列一次。这种缓冲行为可以有效的去掉重复数据造成的不必要的计算和DOM操作。而在下一个事件循环时,Vue会清空队列,并进行必要的DOM更新。
  当你设置 vm.data = 'new value',DOM 并不会马上更新,而是在异步队列被清除,也就是下一个事件循环开始时执行更新时才会进行必要的DOM更新。如果此时你想要根据更新的 DOM 状态去做某些事情,就会出现问题。为了在数据变化之后等待 Vue 完成更新 DOM ,可以在数据变化之后立即使用 Vue.nextTick(callback) 。这样回调函数在 DOM 更新完成后就会调用。

 

 

posted @ 2020-04-10 23:11  卑微小陈的随笔  阅读(1124)  评论(0编辑  收藏  举报