思考一个问题:Vue中如果你想监听子组件的生命周期,你会怎么办?
通过emit发出事件,这肯定是可行的:
// Child
<script setup>
import { onMounted } from "vue";
const emit = defineEmits(["childMounted"]);
onMounted(() => {
// . . .
emit("childMounted");
});
</script>
// App
<script setup>
import Child from "./Child.vue";
const childMounted = () => {
console.log("child component onMounted execution");
};
</script>
<template>
<Child @childMounted="childMounted" />
</template>
那如果子组件是第三方插件里面的应该怎么办呢?
隐藏技巧
先说Vue2,使用v-on:hook:[生命周期]指令:
<Child v-on:hook:mounted="handleClick" />
<Child @hook:mounted="handleClick" />
Vue3中可以使用另一个指令,使用v-on:vue:[生命周期]指令:
<Child v-on:vue:mounted="handleClick" />
<Child @vue:mounted="handleClick" />
源码解读
官方文档没有该记载,我翻阅Vue3源码在vOn部分有这个实现:
if (arg.isStatic) {
let rawName = arg.content
if (__DEV__ && rawName.startsWith('vnode')) {
context.onError(createCompilerError(ErrorCodes.X_VNODE_HOOKS, arg.loc))
}
if (rawName.startsWith('vue:')) {
rawName = `vnode-${rawName.slice(4)}`
}
const eventString =
node.tagType !== ElementTypes.ELEMENT ||
rawName.startsWith('vnode') ||
!/[A-Z]/.test(rawName)
? // for non-element and vnode lifecycle event listeners, auto convert
// it to camelCase. See issue #2249
toHandlerKey(camelize(rawName))
: // preserve case for plain element listeners that have uppercase
// letters, as these may be custom elements' custom events
`on:${rawName}`
eventName = createSimpleExpression(eventString, true, arg.loc)
} else {
// #2388
eventName = createCompoundExpression([
`${context.helperString(TO_HANDLER_KEY)}(`,
arg,
`)`,
])
}
这段代码是位置transformOn 函数内,transformOn是 Vue 3 的内部指令转换函数,用于处理 v-on 指令。
该函数将 v-on 指令转换为可执行的 JavaScript 代码。在 Vue 编译过程中,这个函数会将模板中的 v-on 指令转换为渲染函数所需的格式。
事件名处理逻辑:
-
如果事件名称是静态的 (
arg.isStatic) 且以vue:开头,会被转换为vnode-事件(例如vue:mounted会变为vnode-mounted)。 -
对于一般的事件名称,会转换为
on:前缀的形式,或者对一些非 DOM 元素和以vnode开头的事件名进行处理。 -
代码中的
vnode-前缀处理逻辑,说明 Vue 允许开发者通过v-on监听自定义的 vnode 生命周期事件。
总结
该方法不是推荐的公共 API,没有出现在官方文档。这种写法出现在项目的内部实现中,大家可以进一步研究其实现细节。
感谢您的阅读!