一、$emit
子组件通过props可以引用父组件的数据,那么当子组件想要修改这个数据时,则可以通过$emit在子组件上修改父组件的数据。
$emit 共接收两个参数 :
{string} eventName触发当前实例上的事件。[...args]附加参数都会传给监听器回调。
示例
情景:爸爸给儿子钱,儿子要花钱,儿子可以看到具体的钱是多少,但是儿子要花钱不能直接花,需要通过触发一个事件,爸爸监听这个事件,爸爸通过$event接受儿子传递过来的参数
- 子组件可以通过调用
$emit传入事件名称来触发一个事件:
//child.vue
<template>
<div class="child">
爸爸有{{money}}
<button @click="$emit('update:money', money - 10)">我要花钱</button>
</div>
</template>
<script>
export default {
props: ["money"]
}
</script>
<style>
.child{
border:1px solid green;
}
</style>
复制代码
- 父组件的子组件器上使用
v-on监听这个自定义事件,通过$event接受子组件传递过来的参数
//parent.vue
<template>
<div class="parent">
<div>
爸爸现在有{{ total }}元钱
<hr/>
<Child :money="total" v-on:update:money="total = $event"/>
// :money="total"的意思是 money 这个外部值传入的是 total 这个变量,传变量需要前面加:
</div>
</div>
</template>
<script>
import Child from "./child.vue"
export default {
components: { Child },
data() {
return {
total: 10000
}
}
}
</script>
<style>
.parent{
border:1px solid red;
}
</style>
复制代码
二、.sync
.sync简化了上述代码,直接在父组件上使用
//parent.vue
<template>
<div class="parent">
<div>
爸爸现在有{{ total }}元钱
<hr/>
<Child :money.sync="total"/>
</div>
</div>
</template>
复制代码
:money.sync="total" 等价于:money="total" v-on:update:money="total = $event"
三、案例
<div id="app">
<div>{{bar}}</div>
<my-comp :foo.sync="bar"></my-comp>
<!-- <my-comp :foo="bar" @update:foo="val => bar = val"></my-comp> -->
</div>
<script>
Vue.component('my-comp', {
template: '<div @click="increment">点我+1</div>',
data: function() {
return {copyFoo: this.foo}
},
props: ['foo'],
methods: {
increment: function() {
this.$emit('update:foo', ++this.copyFoo);
}
}
});
new Vue({
el: '#app',
data: {bar: 0}
});
</script>
说明: 代码会被扩展成<comp :foo="bar" @update:foo="val => bar = val">,就是一个语法糖。
四、总结
- 组件不能修改
props的外部数据 $emit可以触发事件并传参$event可以获取$emit的参数
从 2.3.0 起我们重新引入了 .sync 修饰符,但是这次它只是作为一个编译时的语法糖存在。它会被扩展为一个自动更新父组件属性的 v-on 监听器。
示例代码如下:
**
<comp :foo.sync="bar"></comp>
会被扩展为:
**
<comp :foo="bar" @update:foo="val => bar = val"></comp>
当子组件需要更新 foo 的值时,它需要显式地触发一个更新事件:
**
this.$emit('update:foo', newValue)
猛一看不明白,下边我么通过一个实例(弹窗的关闭事件)来说明这个代码到底是怎么运用的。
**
<template>
<div class="details">
<myComponent :show.sync='valueChild' style="padding: 30px 20px 30px 5px;border:1px solid #ddd;margin-bottom: 10px;"></myComponent>
<button @click="changeValue">toggle</button>
</div>
</template>
<script>
import Vue from 'vue'
Vue.component('myComponent', {
template: `<div v-if="show">
<p>默认初始值是{{show}},所以是显示的</p>
<button @click.stop="closeDiv">关闭</button>
</div>`,
props:['show'],
methods: {
closeDiv() {
this.$emit('update:show', false); //触发 input 事件,并传入新值
}
}
})
export default{
data(){
return{
valueChild:true,
}
},
methods:{
changeValue(){
this.valueChild = !this.valueChild
}
}
}
</script>
效果如下:
vue 修饰符sync的功能是:当一个子组件改变了一个 prop 的值时,这个变化也会同步到父组件中所绑定。如果我们不用.sync,我们想做上面的那个弹窗功能,我们也可以props传初始值,然后事件监听,实现起来也不算复杂。这里用sync实现,只是给大家提供一个思路,让其明白他的实现原理,可能有其它复杂的功能适用sync。