理解 Vue 中的组件与 .sync 修饰符

746 阅读1分钟

这篇笔记作为自己在学习过程中对 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元,父组件中的剩余钱数也被更新了。

image.png

上面的代码一共进行了两个步骤:

  1. 父组件上的<Child />监听自定义事件,并让属性值total等于传递过来的$event
<Child :money="total" @update:money="total = $event" />
        <!-- 监听 update:money 事件,
        并把子组件传过来的`money - 100`作为`$event`赋值给`total` -->
  1. 子组件内的<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的格式。