最近因为在准备面试而重新温习vue的相关知识点,看到有一些文章介绍了 “如何实现父组件监听子组件生命周期“ 以下是关于该需求的两个实现方式的理解
方法一
使用$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')
}
...
以上的方式可以看出,父组件给子组件传递了多个回调函数,依赖子组件在自身的生命周期里通过vm.$emit
的方式,调用父组件生命的回调函数。这样的方式可以基本满足我们的需求,但总觉得还是有点笨拙。而且仔细思考的话,不难发现,子组件必须是我们自己编写的组件,如果子组件是一个第三方库引用的组件,我们就没办法去在它的生命周期里实现各个回调函数的调用了。
方法二
使用@hook:
// 父组件
<template>
<div>
<Child
@hook:mounted="onMounted"
@hook:updated="onUpdated"
@hook:beforeDestroy="onBeforeDestroy"
></Child>
</div>
</template>
// 子组件
<!--无-->
看到这里,可能大家会有点疑惑,在父组件中添加类似@hook:mounted="onMounted"
这样的属性,子组件无需相关代码即可实现生命周期监听吗?这里的hook:
是什么东西?
官方文档中并没有太多相关的解释说明,但在程序化的事件侦听器在这个介绍里,我们看到了hook的身影。既然子组件无需相关代码就能触发父组件通过@hook:
绑定的事件,那么我们猜测这一定是vue自身的逻辑里实现了这一块的功能。于是我们尝试着去源码里一探究竟。
在lifecycle.js
文件里,我们发现vue
的生命周期的各个阶段都会去调用一个callHook
函数,它支持两个参入,分别是实例vm
和对应的生命周期钩子名称。而callHook
里面就执行了vm.$emit('hook:' + hook)
,看到这里,大家应该就有点熟悉了,这不就是刚才的方法一吗!所以方法二巧妙的地方就是复用了vue自身的逻辑。
当我们在子组件上传入了对应的@hook:mounted
钩子,也就是执行了vm.$on('hook:mounted')
,而vue实例在生命周期里本身就会执行vm.$emit('hook:mounted')
,其实就连带着触发了我们绑定给子组件的回调函数了。这就是@hook:
的原理,但是官方文档在vue2.0里并没有将这个api正式地介绍给大家。
拓展
借助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钩子函数里书写完毕,而且仍保持原来的生命周期逻辑。
此外,另一个使用场景也是比较常见的,例如我们在编写组件时会执行一些事件监听或者定时器函数,我们希望在组件销毁的时候都能去销毁这些监听或者定时器,但是由于夸生命周期的问题,常常会将定时器赋值给一个全局变量或者绑定到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>
可以看到,经过优化后,我们可以避免了许多data里的无用变量的定义。
本网站是一个以CSS、JavaScript、Vue、HTML为核心的前端开发技术网站。我们致力于为广大前端开发者提供专业、全面、实用的前端开发知识和技术支持。 在本网站中,您可以学习到最新的前端开发技术,了解前端开发的最新趋势和最佳实践。我们提供丰富的教程和案例,让您可以快速掌握前端开发的核心技术和流程。 本网站还提供一系列实用的工具和插件,帮助您更加高效地进行前端开发工作。我们提供的工具和插件都经过精心设计和优化,可以帮助您节省时间和精力,提升开发效率。 除此之外,本网站还拥有一个活跃的社区,您可以在社区中与其他前端开发者交流技术、分享经验、解决问题。我们相信,社区的力量可以帮助您更好地成长和进步。 在本网站中,您可以找到您需要的一切前端开发资源,让您成为一名更加优秀的前端开发者。欢迎您加入我们的大家庭,一起探索前端开发的无限可能!