技术背景
在使用Vue 3的组合式API从子组件向父组件传递数据时,可能会遇到 [Vue warn]: Extraneous non-emits event listeners
警告。这个警告通常表示传递给组件的非 emits
事件监听器无法被自动继承,因为组件渲染的是片段或文本根节点。
实现步骤
1. 定义emits
选项
在子组件中明确声明要触发的自定义事件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| <template> <h1>{{ store.count }}</h1> <button @click="fired">click me</button> </template>
<script> import useStore from "../store/store.js"; export default { name: "HelloWorld", emits: ["updatedcount"], setup(_, { emit }) { const store = useStore();
const fired = () => { store.count++; emit("updatedcount", store.count); };
return { store, fired }; }, }; </script>
|
2. 使用defineEmits
(适用于<script setup>
)
如果使用 <script setup>
语法,使用 defineEmits
来声明自定义事件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <template> <h1>{{ store.count }}</h1> <button @click="fired">click me</button> </template>
<script setup> import useStore from "../store/store.js"; const emits = defineEmits(["updatedcount"]); const store = useStore();
const fired = () => { store.count++; emits("updatedcount", store.count); }; </script>
|
3. 包裹模板内容
将组件的模板内容包裹在一个单一的根元素中,如 <div>
。
1 2 3 4 5 6
| <template> <div> <h1>{{ store.count }}</h1> <button @click="fired">click me</button> </div> </template>
|
核心代码
子组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| <template> <div> <h1>{{ store.count }}</h1> <button @click="fired">click me</button> </div> </template>
<script> import useStore from "../store/store.js"; export default { name: "HelloWorld", emits: ["updatedcount"], setup(_, { emit }) { const store = useStore();
const fired = () => { store.count++; emit("updatedcount", store.count); };
return { store, fired }; }, }; </script>
|
父组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| <template> <div> {{ hello }} <br /> <br /> <input type="text" v-model="hello.searchQuery" /> <br><br> <button @click="hello.count--">click me too!</button> <hello-world @updatedcount="mydata" /> </div> </template>
<script> import HelloWorld from "./components/HelloWorld.vue"; import useStore from "./store/store.js";
export default { components: { HelloWorld, }, setup() { const hello = useStore();
function mydata(event) { console.log(event); }
return { hello, mydata }; }, }; </script>
|
最佳实践
- 明确声明
emits
:在子组件中始终明确声明要触发的自定义事件,这样可以避免警告并提高代码的可读性。 - 使用单一根元素:确保组件的模板有一个单一的根元素,避免渲染片段或文本根节点导致的继承问题。
- 遵循命名规范:事件名称尽量使用小写字母,避免使用连字符或驼峰命名法带来的潜在问题。
常见问题
1. 命名问题
如果事件名称使用了连字符(kebab-case),运行时可能会抱怨没有以驼峰命名法(camelCase)声明;而使用驼峰命名法时,ESLint 可能会警告事件应该使用连字符命名。建议使用全小写字母的命名方式。
2. 包裹元素无效
有时包裹模板内容在 <div>
中可能仍然无法解决问题,这时需要检查是否正确声明了 emits
选项。