一般说的 Vue 生命周期指的是打包完成后的 js 代码去执行的过程,区别于开发环境和 HTML 嵌入 Vue.js。
注意:本文中,“本实例”、“本组件”、“本页面”等称呼,都是一个意思。
生命周期函数的局限性
- 不可阻止(无法阻止周期的运行,除非发生错误)。
- 全局性(函数都是全局,在更新系类周期函数中,对具体变化操作变得很难)
- 目前想不到了,或无法用语言表达出来。
beforeCreate( 创建前 )
也就是执行new Vue()
,读取了代码的信息,但是 Vue 的一系列操作还未执行,参考 JS 的New
操作符,也就初始化了 Vue ,也就包括了生命周期,然后执行这个函数。
在实例初始化之后,数据观测和事件配置之前被调用,此时组件的选项对象还未创建,el 和 data 并未初始化,因此无法访问 methods, data, computed ……等属性上的方法和数据。
可以干啥:
因为生命周期的不可阻止特性,也就意味当前页面必然加载了,在使用了 Vuex 的时候,可以在这个阶段去把本实例需要的东西进行同步获取或者创建准备。
但是不要去进行异步操作(常见:数据请求),这时候没有 data,无法放进 data,多此一举,就算是你保存到 vuex 或本地缓存,在 created 阶段也不一定能获取,因为获取数据一般都是异步请求,使用async/await
可以解决。
同步操作是没有任何问题的,在这个阶段可以进行同步操作,比如进行数据准备,在 created 把准备的数据赋值过来 。
不论是异步操作还是同步操作,由于不是通过 Vue 响应式的获取,无法触发 Vue 的一些初始动画效果和监听效果等,需要慎重考虑。
created ( 创建后 )
这个阶段有什么?
- 当前实例的方法(methods)
- 监听器(watch)
- 属性(data)
- watch/event 事件回调(注意是回调,并不是操作)
- 数据观测(已经可以使用 vue 的响应特性了,最直观的表现就是获取数据可以触发初始动画)
- 计算属性(computed)等等操作。
- 其他不常用的实例的属性/方法
以上这些都存在后,再执行这个函数,也就是说你可以对以上进行操作和调用。
$el 属性目前不可见,至于 dom 等更不可能。
通常我们可以在这里对实例进行预处理。
一些争议:
在本阶段进行异步数据请求。
可以这样,但是不建议,有些关键数据没获取或发生错误,页面布局会崩溃掉,并且由于开文的不可阻挡特性,页面无法阻断。
建议使用 Vue-Router,这时当前实例还拥有组件内的 beforeRouteEnter 钩子,其实就是组建内导航守卫,可以阻止跳转。
beforeMount
挂载开始之前被调用。
其实 Vue 的打包就是把 页面结构->html 都是经过处理,生成 rander 函数,页面渲染的时候再通过解析函数,解析成 html。
编译模板为虚拟 dom 放入到 render 函数中准备渲染
虚拟 DOM 和编译模板,不是一个操作,除非你对 Vue 机制特别了解,现在你可以把它看成一个东西。
这时,如果你获取 dom 会发现结果为类似于<b>{{name}}<b>
的差值表达式,获取的是模板中的 DOM, data 还没有进行赋值,只是有了虚拟 DOM。
虚拟 DOM 你就可以看成 Rander 函数,但是要比 rander 函数复杂得多。
在这些完成后执行这个函数。
也就是说 data 属性,这时候你还可以进行赋值和操作。
注意此时还没有挂在 html 到页面上。
mounted
开始执行 Rander,建议了解: 渲染函数
生成了真实的 HTML ,可以进行 DOM 操作和实例操作 。
这时才会触发这个函数
mounted 只会执行一次。
beforeUpdata (更新前)
数据发生了变化
执行这个函数
发生在虚拟 DOM 重新渲染和打补丁之前,可以在该钩子中进一步地更改状态。
这时候你对数据进行修改,会影响创建的新的虚拟 DOM
创建的虚拟 DOM 会对内存保存的虚拟 DOM 进行 diff 运算。
所以在这个阶段修改数据,会对本次触发更新的变化造成干扰,除非你合理的在本函数内进行数据修改,应为本函数是一个全局的函数,没有任何的回调和传参,一般不做什么事,最多也就事做个日志系统。
找到具体变化的 DOM,去对这些变化的 DOM 进行渲染。
把新的虚拟 DOM 替换掉旧的虚拟 DOM,等待下次 diff.
注意:现在 Vue 已经不是每次都要对本实例进行完全的虚拟 DOM 进行渲染,会根据虚拟 DOM 的 ID 值,快速找到,所以本阶段,真的没啥需要干的事,或许我水平不到。
updated (更新后)
更新后的 DOM 页面,类似于页面初次渲染完成。
可以对更新后的页面进行 DOM 操作和实例操作。
但是这还是一个全局函数,任何的数据变化都会执行,性能问题很大。
局部更新:要想获取更新后的 DOM 建议在数据改变操作后使用 $nextTick来获取对数据更新后的操作。
至于全局更新:对更新后 DOM 进行操作,还是要在这个函数内操作,虽然有性能问题,而且你几乎无法判断触发本次操作到底是那个数据变化造成的。
不要在这个阶段进行数据更改,会触发死循环。
beforeDestrioy (销毁前)
在实例销毁之前调用,实例仍然完全可用。
先执行这个函数
这时一切正常,任何操作都可以,做一些清除操作,比如缓存,比如离开的回调。
执行你对组件销毁前的操作
执行 Vue 自动的销毁方法,销毁清除计时器、清除非指令绑定的事件。
一般情况下,Vue 会自动的销毁当前实例的事件和监听等等,但是在使用 EventBus 的时候必须要手动清除。因为不是当前实例创建的,属于手动创建,Vue 处理的只是 DOM 和数据,至于事件的绑定在 JS 事件栈中保存,如果不手动销毁,再次创建本组件的时候,会触发两次(现在一般事件都是 DOM3 的事件,一个元素能够绑定多个相同事件)。
一般在这一步做一些重置的操作,比如清除掉组件中的定时器 和 监听的 dom 事件
destroyed(销毁后)
在实例销毁之后调用,调用后,所以的事件监听器会被移出,所有的子实例也会被销毁,该钩子在服务器端渲染期间不被调用
什么都没了,也没啥操作得了,也可以做点善后操作,虽然当前实例啥都没了,但是这个函数还是在当前实例的生命周期内,也就属于亲人,当然还是可以操作的。
其他
想到了再补充吧
防喷
由于水平有限,和时间仓促,有问题恳请指出。