Vue2 的@hook 、 hook: 与生命周期
如何实现父组件监听子组件的生命周期
方法一:$emit
// 父组件
<template>
<div>
<Child
@mounted="onMounted"
@updated="onUpdated"
@beforeDestroy="onBeforeDestroy"
></Child>
</div>
</template>
// 子组件
...
mounted () {
this.$emit('mounted')
}
updated () {
this.$emit('updated')
}
beforeDestroy () {
this.$emit('beforeDestroy')
}
...
优点:简单易上手
缺点:此种方法子组件必须是自己编写的组件,若引用第三方库这种方式则不可行
方法二:@hook
// 父组件
<template>
<div>
<Child
@hook:mounted="onMounted"
@hook:updated="onUpdated"
@hook:beforeDestroy="onBeforeDestroy"
></Child>
</div>
</template>
// 子组件
<!--无-->
官方文档并没有太多相关解释,只在处理边界情况 #程序化的事件侦听器— Vue.js (vuejs.org)里有出现。
子组件无需相关处理就能实现侦听,这块的实现原理可以从源码里探究部分
在组件生命周期的每个函数内都调用了callHook它支持两个参入,分别是实例vm
和对应的生命周期钩子名称。而callHook
里面就执行了vm.$emit('hook:' + hook)
,此为方法一!
当在子组件上传入了对应的@hook:mounted
钩子,也就是执行了vm.$on('hook:mounted')
,而vue实例在生命周期里本身就会执行vm.$emit('hook:mounted')
,其实就连带着触发了我们绑定给子组件的回调函数了。
ps:使用callHook的好处
callHook(vm, 'beforeCreate')
调用后, const handlers = vm.$options[hook]
即读取到了当前 vm
实例上的任务队列,然后通过 for
循环依次传递给 invokeWithErrorHandling(handlers[i], vm, null, vm, info)
进行处理, 调用 invokeWithErrorHandling
如果发生异常, 则会统一报错处理。
拓展
1.hook:提升代码简洁性
在编写组件时,我们往往需要在各个生命周期里都针对某个业务逻辑做一些处理,业务散落在各个生命周期钩子里:
<script type="text/ecmascript-6">
export default {
mounted () {
// 挂载时执行一些业务A相关逻辑
// 挂载时执行一些业务B相关逻辑
}
updated () {
// 更新时执行一些业务A逻辑
// 更新时执行一些业务B逻辑
// 更新时执行一些业务C逻辑
}
beforeDestroy () {
// 销毁时执行一些业务A逻辑
// 销毁时执行一些业务C逻辑
}
}
</script>
业务逻辑散落在各个生命周期里,有时候是不利于我们阅读代码的,尤其是当该业务是一个复杂的长段代码时,这个时候我们就可以考虑利用hook:
来梳理某一块的业务代码,提升可阅读性:
<script type="text/ecmascript-6">
export default {
created() {
this.$on('hook:mounted', () => {
挂载时执行一些业务A相关逻辑
})
this.$on('hook:updated', () => {
挂载时执行一些业务A相关逻辑
})
this.$once('hook:beforeDestroy', () => {
挂载时执行一些业务A相关逻辑
})
}
}
</script>
这样就可以将散落的业务逻辑,都在一个created钩子函数里书写完毕,而且仍保持原来的生命周期逻辑。
2.避免data里的无用变量的定义
例如我们在编写组件时会执行一些事件监听或者定时器函数,我们希望在组件销毁的时候都能去销毁这些监听或者定时器,但是由于夸生命周期的问题,常常会将定时器赋值给一个全局变量或者绑定到this上,然后在另一个生命周期里获取并执行销毁操作。
// 优化前
<script type="text/ecmascript-6">
export default {
data() {
return {
timer:null
}
}
mounted () {
this.timer = setInterval(() => {
// todo
}, 1000);
}
beforeDestroy () {
clearInterval(this.timer)
}
}
</script>
// 优化后
<script type="text/ecmascript-6">
export default {
mounted () {
const timer = setInterval(() => {
// todo
}, 1000);
this.$once('hook:beforeDestroy', function () {
clearInterval(timer)
})
}
}
</script>
Vue3.X
参考 https://v3-migration.vuejs.org/zh/breaking-changes/vnode-lifecycle-events.html