实例生命周期:
-
beforeCreate
:在实例初始化之后,数据观测data observer(props、data、computed
) 和event/watcher
事件配置之前被调用。 -
created
:实例已经创建完成之后被调用。在这一步,实例已完成以下的配置:数据观测(data observer),属性和方法的运算,watch/event
事件回调。然而,挂载阶段还没开始,$el
属性目前不可见。 -
beforeMount
:在挂载开始之前被调用:相关的render
函数首次被调用。 -
mounted
:el
被新创建的vm.$el
替换,并挂载到实例上去之后调用该钩子。 -
beforeUpdate
:数据更新时调用,发生在虚拟DOM
重新渲染和打补丁之前。 你可以在这个钩子中进一步地更改状态,这不会触发附加的重渲染过程。 -
updated
:无论是组件本身的数据变更,还是从父组件接收到的props
或者从vuex
里面拿到的数据有变更,都会触发虚拟DOM
重新渲染和打补丁,并在之后调用updated
。 -
beforeDestroy
:实例销毁之前调用。在这一步,实例仍然完全可用。 -
destroyed
:Vue
实例销毁后调用。调用后,Vue
实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。 该钩子在服务器端渲染期间不被调用。
注意:
created
阶段的ajax
请求与mounted
请求的区别:前者页面视图未出现,如果请求信息过多,页面会长时间处于白屏状态。
单个组件的生命周期
- 初始化组件时,仅执行了
beforeCreate/Created/beforeMount/mounted
四个钩子函数 - 当改变data中定义的变量(响应式变量)时,会执行
beforeUpdate/updated
钩子函数 - 当切换组件(当前组件未缓存)时,会执行
beforeDestory/destroyed
钩子函数 - 初始化和销毁时的生命钩子函数均只会执行一次,
beforeUpdate/updated
可多次执行
Vue.nextTick():
在下次
DOM
更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的DOM
。
获取更新后的DOM
言外之意就是什么操作需要用到了更新后的DOM
而不能使用之前的DOM
或者使用更新前的DOM
会出问题,所以就衍生出了这个获取更新后的 DOM
的Vue
方法。所以放在Vue.nextTick()
回调函数中的执行的应该是会对DOM
进行操作的 js
代码,比如Swiper扩展包的:
var swiper = new Swiper('.swiper-container', {
pagination: '.swiper-pagination',
nextButton: '.swiper-button-next',
prevButton: '.swiper-button-prev',
paginationClickable: true,
spaceBetween: 30,
centeredSlides: true,
autoplay: 2500,
autoplayDisableOnInteraction: false
});
什么时候需要用Vue.nextTick()
:
- 你在
Vue
生命周期的created()钩子函数进行的DOM
操作一定要放在Vue.nextTick()
的回调函数中。原因是什么呢,原因是在created()
钩子函数执行的时候DOM
其实并未进行任何渲染,而此时进行DOM
操作无异于徒劳,所以此处一定要将DOM
操作的js
代码放进Vue.nextTick()
的回调函数中。与之对应的就是mounted钩子函数,因为该钩子函数执行时所有的DOM
挂载和渲染都已完成,此时在该钩子函数中进行任何DOM
操作都不会有问题 。 - 在数据变化后要执行的某个操作,当你设置
vm.someData = 'new value'
,DOM
并不会马上更新,而是在异步队列被清除,也就是下一个事件循环开始时执行更新时才会进行必要的DOM
更新。如果此时你想要根据更新的DOM
状态去做某些事情,就会出现问题。。为了在数据变化之后等待Vue
完成更新DOM
,可以在数据变化之后立即使用Vue.nextTick(callback)
。这样回调函数在DOM
更新完成后就会调用。 -
mounted
不会承诺所有的子组件也都一起被挂载。如果你希望等到整个视图都渲染完毕,可以用vm.$nextTick
替换掉mounted
:
mounted: function () {
this.$nextTick(function () {
// Code that will run only after the
// entire view has been rendered
})
}
2021.9.7补充:为什么nextTick里的代码会在DOM更新后执行
先看一段代码:
this.$nextTick(() => {
console.log('获取最新dom1:', this.$refs.con) // 1
// debugger
})
this.value++ // 2 这里实际上会执行 watcher.run()
this.$nextTick(() => {
console.log('获取最新dom2:', this.$refs.con) // 3
// debugger
})
打印结果如下:
但是如果把
this.value++
和第一个nextTick调换位置,两个nextTick都能获取到最新的dom了。是什么原因,我们来从vue的源码分析一下:this.value++
-> 触发set函数
-> 通知依赖更新: dep.notify()
-> 调用 watcher.update()
-> watcher.run()放在vue的nextTick函数中执行
-> flushCallbacks调用队列中的所有cb,执行顺序为1、2、3
-> 执行到2的时候调用_render 、_update、patch更新dom
所以执行到1的时候dom是没有进行更新的,因此打印的也是没有更新的dom,执行到2的时候已经完成了patch,dom更新了,因此拿到的是新的dom。
这个流程走完后,vue替换掉了老的dom节点,这时候dom树已经是新的了,所以在JS中获取dom数据也是新的,但是视图不一定更新了,因为不一定完成了绘制(这个正确性还有待考证)。
参考文章:
https://mp.weixin.qq.com/s/4ukhHAcMQN07y0ssYqUeuA
https://segmentfault.com/a/1190000008570622
https://segmentfault.com/a/1190000008570874