这篇笔记作为自己在学习过程中对 Vue 中的 .sync 修饰符的一个理解记录。
一、理解组件
Vue 组件实例的作用域是独立的,这意味着不能在子组件的模板中直接引用父组件的数据。
所以父组件的数据如果想要向下传递给子组件,需要子组件显示的用 props 选项来声明它期望获得的数据。
props 是单项绑定的:当父组件的属性变化时,将传到给子组件,但是不会反过来。这是为了防止子组件无意中修改父组件的状态。
那如果我们想要子组件修改父组件中的数据应该怎么办呢?
答案是采用 Vue 的自定义事件系统 —— 在子组件上触发一个自定义事件,使用$emit
将这个自定义事件传递给父组件。在父组件中的子组件器上使用v-on
监听这个自定义事件。
示例代码
子组件:
// Child.vue
<template>
<div class="child">
儿子每次花 100 元,爸爸口袋里还剩 {{ money }} 元。
<button @click="$emit('update:money', money - 100)">
<!-- 点击触发一个"update:money"事件,
并且把`money - 100`传递个监听器。 -->
<span>花钱</span>
</button>
</div>
</template>
<script>
export default {
props: ["money"],
};
</script>
<style>
.child {
border: 3px solid green;
}
</style>
父组件:
// App.vue
<template>
<div id="app">
爸爸身上现在有 {{ total + "元" }}
<hr />
<Child :money="total" @update:money="total = $event" />
<!-- 监听 update:money 事件,
并把子组件传过来的`money - 100`作为`$event`赋值给`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>
main.js:
import Vue from "vue";
import App from "./App.vue";
Vue.config.productionTip = false;
new Vue({
render: h => h(App)
}).$mount("#app");
运行结果如下,每点击一次花钱按钮,少100元,父组件中的剩余钱数也被更新了。
上面的代码一共进行了两个步骤:
- 父组件上的
<Child />
监听自定义事件,并让属性值total
等于传递过来的$event
:
<Child :money="total" @update:money="total = $event" />
<!-- 监听 update:money 事件,
并把子组件传过来的`money - 100`作为`$event`赋值给`total` -->
- 子组件内的
<button>
按钮点击后触发自定义事件update:money
,并传递一个参数:
<button @click="$emit('update:money', money - 100)">
<!-- 点击触发一个"update:money"事件,
并且把`money - 100`传递个监听器。 -->
<span>花钱</span>
</button>
使用.sync
修饰符可以为我们简化上面的代码。
二、.sync 语法糖
历史
sync 在英语中的意思是同步、同时。
.sync
修饰符最初存在于 Vue 的早期版本里,但是在 2.0 中被移除了。官方的理由是:Props 现在只能单向传递。为了对父组件产生反向影响,子组件需要显式地传递一个事件而不是依赖于隐式地双向绑定。
但 2.3.0 版本官方又重新引入了 .sync
修饰符,但这次它只是作为一个编译时的语法糖存在,它会被扩展为一个自动更新父组件属性的 v-on
监听器。
使用方法
将上面父组件里的这段代码改写成用.sync
后,代码简洁多了:
<!--原来-->
<Child :money="total" @update:money="total = $event" />
<!--改写后-->
<Child :money.sync="total" />
在父组件上告诉子组件传递过去的money
跟父组件上的total
保持同步,上面两句代码的效果是完全一样的。
注意使用.sync
修饰符的话,子组件中触发的事件名 eventName 需要采用update:prop
的格式。