举个例子:
main.js
import Vue from "vue";
import App from "./App.vue";
Vue.config.productionTip = false;
new Vue({
render: h => h(App)
}).$mount("#app");
App.vue 父组件
引入一个Child组件,Child需要外部给它传一个数据,money。于是App把它的total数据传给组件
<template>
<div class="app">
App.vue 我现在有 {{total}}
<hr>
<Child :money="total"/>
</div>
</template>
<script>
import Child from "./Child.vue";
export default {
data() {
return { total: 10000 };
},
components: { Child }
};
</script>
<style>
.app {
border: 3px solid red;
padding: 10px;
}
</style>
Child.vue 子组件
需要一个外部数据money,由父组件App传入。有一个“花钱”按钮
<template>
<div class="child">
{{money}}
<button>
<span>花钱</span>
</button>
</div>
</template>
<script>
export default {
props: ["money"]
};
</script>
<style>
.child {
border: 3px solid green;
}
</style>
类似于,儿子想花爸爸的钱。点击儿子身上的按钮,爸爸和儿子的钱会相应减少一定值。

想法1:直接在儿子身上花爸爸的钱
在Child.vue里:
<button @click="money-=100">
<span>花钱</span>
</button>
会有警告,说不要直接修改外部属性,因为这个值当父组件重新渲染的时候会被父组件重写。
所以直接修改不行,因为你不能改外部属性,改了也没用
那怎么办呢?
想法2:想花钱,通知你爸爸不就好了
怎么通知? MVC里的eventBus。Vue内置了eventBus,而且this继承了eventBus类。
在Child.vue里:
<button @click="$emit('useMoney',money-100)">
<span>花钱</span>
</button>
如果点击按钮,就使用$emit触发"useMoney" 事件,花100块钱,结果是money-100
在App.vue里:
<Child :money="total" @useMoney="total=$event"/>
使用Child组件时,先传total。监听useMoney事件,如果这个事件被触发了,就拿到$emit的结果值。这个结果值保存在$event变量里。
整个过程就是:爸爸给儿子一个money,儿子就看到了这个money。儿子想花钱的时候,就触发一个事件;爸爸监听了这个事件,当事件被触发时,就把儿子传过来的参数进行处理,而这个参数是存在$event上的。

想法3:改进,最终写法
这里我们自己定义的事件名useMoney,Vue 规定了这个事件名:update:money
这是Vue里的一个很常见的场景。我给你一个数据,但是你要改的话,不能自己改。必须通知我,我去改。
每次监听都要写一大堆:传给你一个数据,监听事件,得到$event...
Vue把这些封装成了sync修饰符
在Child.vue里:
<button @click="$emit('update:money',money-100)">
<span>花钱</span>
</button>
子组件里还是要使用 $emit 触发,要注意事件名是 update:...
在App.vue里:
<Child :money.sync="total" />
直接在给子组件的属性后边加 .sync ,就代替了想法2里的监听,取值等一长串代码。表示我给你一个money,你如果改了这个money,我就同步过来。
总结
Vue有以下规则:
- 组件不能修改props外部数据
- 使用
$emit触发事件,并传参 $event可以获取$emit的参数