Vue官方文档“组件”一栏中提到,Vue在2.0移除了.sync,又在2.3重新添加了它,但是采用了全新的内部逻辑:
这次它只是作为一个编译时的语法糖存在。它会被扩展为一个自动更新父组件属性的 v-on
监听器。只有在子组件中显式传递一个名为update:变量名
的自定义事件,父组件的相应变量才会被更新。
下面是坑:
Vue官方文档中对于最新版的.sync修饰符的原理解释如下:
如下代码
<comp :foo.sync="bar"></comp>
会被扩展为:
<comp :foo="bar" @update:foo="val => bar = val"></comp>
其实很好理解,就是简单的父组件的变量绑定给子组件的变量,子组件变量改变时通过事件提醒父组件更新。
为了进行实践,我写了以下demo,就是两个输入框,上面输入框的v-model绑定了父组件的变量parentMessage,下面的输入框的v-model绑定了子组件的变量myMessage,然后把父组件和子组件的两个变量通过.sync双向绑定:
<h1>写法1</h1>
<div id="parent">
<span>父组件</span>
<input type="text" v-model="parentMessage">
<br>
<!---注意下面这行用的是.sync修饰符--->
<child v-bind:my-message.sync="parentMessage"></child>
</div>
以下是js部分:
Vue.component('child', {
props: ['myMessage'],
template: `<div>
<span>子组件</span>
<input type="text" v-model="myMessage">
</div>`,
watch: {
myMessage: function() {
this.$emit('update:myMessage', this.myMessage);//请注意这一行的自定义事件名为update:myMessage
}
}
})
var parent = new Vue({
el: '#parent',
data: {
parentMessage: 'hello, child!'
}
})
以上代码运行一切正常,两个输入框任意改变一个的内容,另一个会相应改变。
但是,按照官方文档给出的说法,我把.sync改成v-bind和v-on两个属性后:
<h1>写法2</h1>
<div id="parent1">
<span>父组件</span>
<input type="text" v-model="parentMessage">
<br>
<!---下面这行改成了v-bind和v-on的写法--->
<child v-bind:my-message="parentMessage" v-on:update:my-message="myMessage => parentMessage = myMessage"></child>
</div>
在父输入框中改变文本,子输入框中会相应改变,但是子输入框改变文本却不影响父输入框内容!也就是说父输入框并没有像我们想象的那样接收到update:myMessage
事件。
此时,只有改变子组件向上传递的事件名才可以达到预期效果:
Vue.component('child', {
//...
watch: {
myMessage: function() {
this.$emit('update:my-message', this.myMessage);//把这里的自定义事件名改为update:my-message后达到预期效果
}
}
})
//...
由此可见,.sync的实现绝不仅仅是把它拆成那两个属性这么简单,官方文档这样解释应该只是为了通俗易懂地说明其原理。
以下为注意事项,具体内部原理日后慢慢发掘
- 使用sync的时候,子组件传递的事件名必须为
update:value
,其中value必须与子组件中props中声明的名称完全一致(如上例中的myMessage
,不能使用my-message
) - .sync只是个语法糖,完全可以使用v-bind和v-on来实现相同的效果。这种情况下,就相当于父子组件的通信而已,v-on的事件可以随便取名字,没必要用
update:value
。因为html中大小写不敏感,如果在html中设置v-on:online-student
,子组件中传递的事件名必须为online-student
,不得采用驼峰命名法。 - 官方文档写错了吧,上述二者不是单纯的换个写法这么简单。内部一定还有更深层次的不同。