生命周期函数就是 vue 实例在某一个时间点会自动执行的函数.
简单来说就是好像把人的出生到死亡分成一个个阶段, 你取名字肯定是在你出生阶段, 而不是在成年阶段; 你结婚肯定是在成年阶段, 而不是在出生阶段; 如果说你在出生阶段想去阶段, 那肯定是不行的. 组件也是一样, 在实例化的时特定阶段调用特定方法, 调用的这个方法就是钩子函数.
钩子函数
钩子函数和回调函数有什么区别吗?
它们区别是:
JS 派函数监听事件 => 监听函数就是所谓的钩子函数 => 函数钩取事件: 函数主动找事件 => 钩子函数
JS 预留函数给 dom 事件, dom 事件调用 JS 预留的函数 => 事件派发给函数: 事件调用函数 => 回调函数
打个比方:
钩子函数: 一个房间里的监控摄像头监控着每一个进入的人的面部特征, 识别出了符合条件的人就触发警告 (执行函数事件);
回调函数: 可以看做是在一片地区埋了许许多多的地雷, 一旦踩中了某个地雷 (触发事件), 地雷就会爆炸 (执行函数事件).
可以简单的理解为:
钩子函数是事件被动的监听, 一旦条件触发就执行
回调函数是主动事件, 执行函数体内容
生命周期探究
- <template>
- <div>{{msg}}</div>
- </template>
- <script>
- export default {
- name: 'HelloWorld',
- data () {
- return {
- msg: 'hello world',
- msg1: ''
- }
- },
- beforeCreate () {
- console.groupCollapsed('beforeCreate 创建前状态')
- console.log('%c%s', 'color:MediumVioletRed', 'el :' + this.$el)
- console.log(this.$el)
- console.log('%c%s', 'color:MediumVioletRed', 'data :' + this.$data)
- console.log(this.$data)
- console.log('%c%s', 'color:MediumVioletRed', 'msg:' + this.msg)
- console.groupEnd()
- },
- created () {
- console.groupCollapsed('created 创建前状态')
- console.log('%c%s', 'color:MediumVioletRed', 'el :' + this.$el)
- console.log(this.$el)
- console.log('%c%s', 'color:MediumVioletRed', 'data :' + this.$data)
- console.log(this.$data.msg)
- console.log('%c%s', 'color:MediumVioletRed', 'msg:' + this.msg)
- console.groupEnd()
- },
- beforeMount () {
- console.groupCollapsed('beforeMount 挂载前状态')
- console.log('%c%s', 'color:MediumVioletRed', 'el :' + this.$el)
- console.log(this.$el)
- console.log('%c%s', 'color:MediumVioletRed', 'data :' + this.$data)
- console.log(this.$data.msg)
- console.log('%c%s', 'color:MediumVioletRed', 'msg:' + this.msg)
- console.groupEnd()
- },
- mounted () {
- console.groupCollapsed('mounted 挂载后状态')
- console.log('%c%s', 'color:MediumVioletRed', 'el :' + this.$el)
- console.log(this.$el)
- console.log('%c%s', 'color:MediumVioletRed', 'data :' + this.$data)
- console.log(this.$data.msg)
- console.log('%c%s', 'color:MediumVioletRed', 'msg:' + this.msg)
- console.groupEnd()
- setTimeout(() => {
- this.$data.msg = '123'
- }, 5000)
- },
- activated () {
- console.groupCollapsed('activated 挂载后状态')
- console.log('%c%s', 'color:MediumVioletRed', 'el :' + this.$el)
- console.log(this.$el)
- console.log('%c%s', 'color:MediumVioletRed', 'data :' + this.$data)
- console.log(this.$data.msg)
- console.log('%c%s', 'color:MediumVioletRed', 'msg:' + this.msg)
- console.groupEnd()
- setTimeout(() => {
- this.$data.msg = 'hello tiantian'
- }, 10000)
- },
- beforeUpdate () {
- console.groupCollapsed('beforeUpdate 更新前状态')
- console.log('%c%s', 'color:MediumVioletRed', 'el :' + this.$el)
- console.log(this.$el)
- console.log('%c%s', 'color:MediumVioletRed', 'data :' + this.$data)
- console.log(document.getElementById('app').innerhtml)
- console.log('%c%s', 'color:MediumVioletRed', 'msg:' + this.msg)
- console.groupEnd()
- },
- updated () {
- console.groupCollapsed('updated 更新后状态')
- console.log('%c%s', 'color:MediumVioletRed', 'el :' + this.$el)
- console.log(this.$el)
- console.log('%c%s', 'color:MediumVioletRed', 'data :' + this.$data)
- console.log(document.getElementById('app').innerHTML)
- console.log('%c%s', 'color:MediumVioletRed', 'msg:' + this.msg)
- console.groupEnd()
- setTimeout(() => {
- this.$destroy()
- }, 5000)
- },
- beforeDestroy () {
- console.groupCollapsed('beforeDestroy 实例销毁前状态')
- console.log('%c%s', 'color:MediumVioletRed', 'el :' + this.$el)
- console.log(this.$el)
- console.log('%c%s', 'color:MediumVioletRed', 'data :' + this.$data)
- console.log(document.getElementById('app').innerHTML)
- console.log('%c%s', 'color:MediumVioletRed', 'msg:' + this.msg)
- console.groupEnd()
- },
- destroyed () {
- console.groupCollapsed('destroyed 实例销毁后状态')
- console.log('%c%s', 'color:MediumVioletRed', 'el :' + this.$el)
- console.log(this.$el)
- console.log('%c%s', 'color:MediumVioletRed', 'data :' + this.$data)
- console.log(document.getElementById('app').innerHTML)
- console.log('%c%s', 'color:MediumVioletRed', 'msg:' + this.msg)
- console.groupEnd()
- }
- }
- </script>
beforeCreate 和 created
beforeCreate: 在实例初始化完成时, 被执行
created: 在初始化结束之后会再初始化一些外部注入和一些双向绑定相关的事情时, 被执行
这两个钩子函数执行完之后, 初始化基本完成了.
在 beforeCreate 阶段, el 和 data 都没有被挂载; 而在 created 阶段, el 还没被挂在, 但 data 已经被挂载了, 如下图所示:
这里 el 为啥没有被挂载呢?
看上图, 在 created 执行完毕后, 它会询问一个条件: 你这个 Vue 实例里是否有 el 这个选项.
如果有就又会询问是否有 template 这个选项:
如果没有 template 就会走右侧的分支,
如果这个实例没有 template, 就会将 el 这个根节点当做模版, 来进行渲染
如果有 template 就会走左侧的分支
把 template 作为模版去渲染
beforeMount 和 mounted
beforeMount: 执行时, 页面还没有被渲染
mounted: 执行时, 页面已经被渲染了
从图中也可以看出, 在 beforeMount 执行时, el 还没有被挂在; 当 mounted 执行时, el 被挂载到页面了.
beforeUpdate 和 updated
beforeUpdate: 数据被改变, 还没渲染之前会被执行
updated: 数据被改变, 渲染完成后会被执行
这张图中有个奇怪的现象, 为什么在 beforeUpdate 和 updated 两个钩子函数中, el 和 msg 都是一样呢? beforeUpdate 执行是不应该是老数据嘛, 怎么这里也是最新的数据了?
因为这里的 el 是虚拟 dom, 不是真实的 dom, 和 data 都是对象, 在加上 console.log 这里是个异步操作, 当你点开 console.log 时, 其实代码早就跑完了, 数据已经是最新的了, 所以就会看到在这两个函数中输出结果是一样的了.
可以用 document.getElementById('app').innerHTML 获取真实的 Dom 结构, 这时我们就可以看到这两处不一样的地方了.
beforeDestroy 和 destroyed
调用 vm.$destroy() 方法可对实例销毁
beforeDestroy: 实例被销毁前被执行
destroyed: 实例被销毁后被执行
activated 和 deactivated
activated: 使用 keep-alive 标签时会被触发
deactivated:keep-alive 标签停止调用时会被触发
总结
created: 挂载之前需要做的一些事情可以在放在这里面, 比如页面加载时 loading 动画
mounted: 向后端发请求, 可以放在这个函数中.
这两个钩子函数使用时机重叠部分很多, 反正是怎么方便怎么来就是了.
参考资源
Vue2.0 探索之路 -- 生命周期和钩子函数的一些理解 https://segmentfault.com/a/1190000008010666
vue2 为什么 beforeUpdate 时的 data 与 updated 时的一样
JavaScript: 钩子函数与回调函数的区别
来源: https://juejin.im/post/5c73c8fbf265da2dd37c256a