Child.vue
<script setup lang="ts">
import { getCurrentInstance, defineEmits, onMounted, ref } from 'vue'
const emit = defineEmits(['update'])
// 用来存储父组件是否真正传入了 @update
const hasUpdateListener = ref(false)
onMounted(() => {
const internal = getCurrentInstance()
const props = (internal?.vnode.props || {}) as Record<string, any>
// 事件处理器会被归一化为 onXxx
hasUpdateListener.value = typeof props.onUpdate === 'function'
})
function clickBtn() {
if (hasUpdateListener.value) {
emit('update', '新的内容')
} else {
console.warn('[Child] 父组件未绑定 @update,emit 会被忽略')
}
}
</script>
<template>
<button @click="clickBtn">
{{ hasUpdateListener ? '触发 update' : '无 update 监听' }}
</button>
</template>
Father.vue
<!-- 绑定了update事件 -->
<Child @update="handleUpdate"/>
<!-- 未绑定 -->
<Child/>
使用案例
子组件:点击按钮切换功能的模块,想在切换前增加一个before-change方法,可以控制被切换前的事件。若父组件未绑定@before-change,则默认直接切换(即执行.then()方法)。
SwitchBtn.vue
<script setup lang="ts">
import { ref, onMounted, getCurrentInstance } from 'vue';
defineOptions({
name: 'SwitchBtn',
});
const emit = defineEmits(['beforeChange']);
const beforeChangeListener = ref(false);
const clickBtn = () => {
new Promise((resolve, reject) => {
if (beforeChangeListener.value) {
emit('beforeChange', (result: boolean) => {
if (result) {
resolve(1);
} else {
reject(1);
}
});
} else {
resolve(1);
}
})
.then(() => {
console.log('切换成功');
})
.catch(() => {
console.log('取消切换');
})
};
onMounted(() => {
const internal = getCurrentInstance();
const props = (internal?.vnode.props || {}) as Record<string, any>;
beforeChangeListener.value = typeof props.onBeforeChange === 'function';
});
</script>
<template>
<button @click="clickBtn">切换模式</button>
</template>
使用组件
<script setup>
import SwitchBtnfrom '../language.vue';
import { showConfirmDialog } from 'vant';
import 'vant/lib/dialog/style';
const beforeChange = (cb) => {
showConfirmDialog({
title: '切换',
message: `确定切换模式吗?`,
})
.then(() => {
cb(true);
})
.catch(() => {
cb(false);
});
};
</script>
<template>
<SwitchBtn @before-change="beforeChange" />
</template>