在 Vue 中,数据通常以单向方式从父组件传递到其子组件。这是通过 props 传递的,props是我们赋予组件的属性或属性。
例如,如果我们调用一个组件PageOne
,它有一个名为的属性name
,该name
属性将在PageOne
组件本身中变得可用,让我们可以用它做我们想做的事情。这样,当我们在父组件或页面中声明数据时,数据就会向下传递给子组件:
组件在 Vue 中是如何工作的
在大多数情况下,道具允许我们做我们需要对数据做的一切。然而,有时我们需要向上发送数据——从子组件到其父组件。为此,我们使用$emit
,它允许我们向上发送数据,然后在触发事件时在父组件中触发事件$emit
。
$emit
在 Vue 中如何工作?
在 Vue中有三种触发方式$emit
,具体取决于您使用的是 Options API、Composition API 还是内联$emit
事件。
this.$emit
在选项 API 中。$emit
如果在您的 HTML 模板中使用。defineEmits
emit
如果在合成 API 中使用。
让我们通过一个愚蠢的例子来看看它是如何工作的。假设我们有一个计数器组件,如下所示:
<template>
<button @click="$emit('counterEvent')">Click Me</button>
</template>
该组件存储在一个名为Counter.vue
. 我们的组件无法更改,因为它在其他地方使用,但它确实会在$emit
任何时候被点击时触发一个事件。这是完美的,因为我们可以在父组件中使用它。
那么如果我们想在某个地方添加这个组件——例如,在我们的App.vue
文件中——并使用它来显示我们的计数器的值。现在让我们尝试这样做:
<template>
<h1>{{ counter }}</h1>
<Counter @counter-event="incrCounter"/>
</template>
<script>
import Counter from './Counter.vue'
export default {
// Add our components
components: {
Counter
},
// Store our data
data() {
return {
counter: 0
}
},
methods: {
incrCounter: function() {
this.counter += 1;
}
}
}
</script>
让我们分解一下 – 首先,我们包括我们的Counter
. 由于有一个$emit
名为 的事件counterEvent
,我们可以将它附加到我们的Counter
HTML 中。每当$emit
触发时,它都会触发 ,counterEvent
从而触发该属性中的函数。在这里,我们 incrCounter
随时运行counterEvent
火灾。
通过这样做,我们还可以将counter
数据增加 1,因为这就是这样incrCounter
做的。因此,我们已经将 click 事件向上发送到我们的父组件。
为什么我们使用 Kebab Case?
您可能会注意到,当我们定义$emit
事件时,我们使用了驼峰式大小写(counterEvent
),但在跟踪事件时,我们使用了烤肉串大小写(counter-event
)。
在Vue 3counterEvent
中,counter-event
由于 Vue 3 自动转换counterEvent
为counter-event
. 在Vue 2中,这个功能不存在,所以counter-event
两者都坚持。
传递数据$emit
假设相反,我们希望我们的组件定义counterEvent
应该增加多少。如果我们想这样做,我们可以将第二个参数传递给$emit
函数,它是值:
<template>
<button @click="$emit('counterEvent', 2)">Click Me</button>
</template>
在这里,我们将值传递2
给我们的counterEvent
. 让我们回到我们的App.vue
文件。为了在 中利用这个值counterEvent
,我们需要把它写成一个函数。下面n
是值:
<template>
<h1>{{ counter }}</h1>
<Counter @counter-event="(n) => incrCounter(n)"/>
</template>
<script>
import Counter from './Counter.vue'
export default {
// Add our components
components: {
Counter
},
// Store our data
data() {
return {
counter: 0
}
},
methods: {
incrCounter: function(value) {
this.counter += value;
}
}
}
</script>
现在,我们的计数器将增加放入子组件中的值,从而使我们也可以将数据传递给父组件。如您所料,这不仅限于数字,还可以包括任何数据结构——包括对象和字符串。
$emit
与选项 API 一起使用
我们已经展示了一个非常简单的示例,但我们也可以Counter.vue
使用函数来编写我们的子组件。这是Options API的示例,使用this.$emit
:
<template>
<button @click="emitFunction">Click Me</button>
</template>
<script>
export default {
emits: [ 'counterEvent' ],
methods: {
emitFunction: function() {
this.$emit('counterEvent', 2)
}
}
}
</script>
这可能被证明是一种更简洁的使用方式$emit
,特别是如果您想在$emit
单击按钮时使用其他操作。
将您的发射事件添加到您的原型
您可能注意到我们还在emits
原型中定义了我们的发出事件。这是一个很好的做法,原因有两个:
- 它允许您通过显示该组件中可能发生的发射事件来对代码进行自我记录。
- 它可以帮助您跟踪已弃用的 emits
emits
,因为如果使用了 emit 事件但在数组中找不到,Vue 会抛出错误。
$emit
与组合 API 一起使用
我们可以使用$emit
Composition API – 唯一的区别是我们必须使用它defineEmits
。
<template>
<button @click="emitFunction">Click Me</button>
</template>
<script setup>
import { defineEmits } from 'vue'
const emit = defineEmits(['counterEvent']);
const emitFunction = function() {
emit('counterEvent', 2)
}
</script>
defineEmits
用于定义所有允许emit
事件的完整列表。在这里,我们只有一个,counterEvent
。如果你有多个,你可以这样定义它们:
const emit = defineEmits(['counterEvent', 'anotherEvent', 'finalEvent']);
如果您使用未在 中列出的发出事件defineEmits
,Vue 将抛出警告,类似于emits
在 Options API 上使用。否则,您可以emit()
像往常一样使用该函数发出,而根本不需要使用Options API。
最后的想法和最佳实践
Emit 是一个强大的工具,可以在需要时将数据发送回父级。这意味着数据流在 Vue 中可以是双向的。在定义emit
代码时,两个主要的最佳实践是:
emits
始终在或中定义您的发出事件defineEmits
,这将帮助您保持代码清洁和有据可查。- Vue 3 中的常规约定是
this-is-kebab-case
对 HTML 使用 kebab case ( ),thisIsCamelCase
在脚本中使用骆驼大小写 ( )。因此,最好也遵循此约定。
learn-how-to-emit-custom-events-in-vue-with-$emit