一文彻底读懂Vue的生命周期(源码的开端,面试必备,应用场景)

556 阅读4分钟

Vue的生命周期

Vue实例创建销毁的过程 ,这些过程中会伴随着一些函数的自调用,我们称这些函数为钩子函数。

总览Vue生命周期的三个阶段

1.挂载(创建):初始化相关属性->beforeCreate,created,beforeMount,mounted

2.更新(运行):元素或组件的变更操作->beforeUpdate,updated

3.销毁:销毁相关属性->beforeDestroy,destroyed

  • beforeCreate(初始化界面前)

  • created(初始化界面后)

  • beforeMount(渲染dom前)

  • mounted(渲染dom后)

  • beforeUpdate(更新数据前)

  • updated(更新数据后)

  • beforeDestroy(卸载组件前)

  • destroyed(卸载组件后)

问题来了:new 出来的Vue到底干了一件什么事

也就是说先去编译tempalte,然后再挂载到el指定的DOM元素上, 在此期间他会执行一些函数,这些函数就是生命周期函数

<div id="app">
        <div id="h3">{{msg}}</div>
        <input type="button" value="修改msg" @click="msg='no'">
</div>
// var vm =new Vue表示开始创建一个Vue的实例对象
    //1. init Events & Lifecycle表示刚初始化了一个Vue空的实例对象
    //这时候这个对象身上只有默认的一些生命周期函数和默认的事件,其他的东西未创建
    //也就是该执行beforeCreate了
    var vm = new Vue({
        el: '#app',
        data: {
            msg: '初始化'
        },
        methods: {
            show() {
                console.log('执行了show方法');
            }
        },
   beforeCreate() { //1. 这是第一个生命周期函数,表示实例完全被创建出来之前,会执行它
            console.log(this.msg); //undefined
            //this.show();   //this.show is not a function 
            // 结论:在beforeCreate生命周期执行的时候,data和methods中的数据还没有被初始化
        },
created() { //2.这是第二个生命周期函数
        console.log(this.msg); //初始化
        this.show(); //执行了show方法
        //结论:在created中,data和methods都已经被初始化好了
        //如果要调用methods里面的方法或者操作data中的数据,最早只能在created中操作
    },

 //3.根据是否有el和templete来编译模板,把vue中的指令进行执行,最终在内存中生成一个编译好的
 //模板字符串,然后把这个模板字符串渲染为内存中的DOM,但是并没有把模板挂载到真正的页面上去
 beforeMount() { //3.这是第三个生命周期函数,模板已经被编译,但是没有被渲染进页面
           console.log(document.getElementById('h3').innerText); //{{msg}}
        },
 ```
 
 mounted() { //4.这是第四个生命周期函数,已经将编译好的模板挂载到页面上去了
       //mounted是实例创建期间的最后一个生命周期函数,内存中的模板已经被挂载到了页面上
       console.log(document.getElementById('h3').innerText); //初始化
     },
 ```
 
//5.组件运行阶段的生命周期函数:beforeUpdate,updated,她两最少执行0次,代表data数据从来没有改变
 beforeUpdate() { //5.这是第五个生命周期函数,界面没有更新,数据更新了
  console.log(`界面上元素的内容:${document.getElementById('h3').innerText}`);  
  //界面上元素的内容:初始化
  console.log(`data中的数据${this.msg}`);  //data中的数据no
 //定义按钮改变msg的值,然后beforeUpdate函数会自动执行,界面显示数据是旧的,但是数据是最新的
         },
 ```
 
 updated() { //6.这是第六个生命周期函数,界面更新,数据也更新
     console.log(`界面上元素的内容:${document.getElementById('h3').innerText}`);
     //界面上元素的内容:no
      console.log(`data中的数据${this.msg}`); //data中的数据no
 },
beforeDestroy(){ //7.这是第七个生命周期函数,所有数据处于可用状态

},

destroyed(){  //8.这是第八个生命周期函数,组件已经完全被销毁了,所有的数据,方法,指令通通被销毁

  }
})

有人问我,异步请求该放到哪个生命周期?

createdmounted都可以,但是具体的还得看业务场景

  • 对于作为子组件被调用的组件里,异步请求应当在mounted里调用,因为这个时候子组件可能需要涉及到对dom的操作;

  • 对于页面级组件,当我们需要使用ssr(服务端渲染)的时候,只有created是可用的,所以这个时候请求数据只能用它;

  • 对于页面级组件, 当我们做异步操作时,涉及到要访问dom的操作,我们仍旧只能使用mounted;

  • 对于一般情况,createdmounted都是可以的;

补充一点,在mounted被调用时,vue不保证子组件也都被挂载完毕了,所以如果希望整个视图渲染完毕再执行操作,需要使用$nexttick,使用方法如下:

mounted: function () {
  this.$nextTick(function () {
    // Code... 
  })
}
  • $nexttick`本身会接收一个回调函数,它实际上会让里面的代码延时执行,类似于setTimeout。

根据上面的结论,我们知道大多数情况下,使用createdmounted做异步请求没什么区别,但是对于上面提到的几种情况需要选择其中之一。我的个人习惯是:对于页面级组件,一般在created里调用,这样做是为了与子组件里需要在mounted里调用做一个区分,便于提醒这个知识点。

lifecycle.png