参考
一、 vue 双向数据绑定语法
Vue.js作为前端MVVM三大框架之一,最核心的功能便是数据的双向绑定了,Vue使用HTML的模版语法实现了ViewModel。Vue开发者们可以使用“Mustache”语法 (双大括号)来将数据解析成文本,也可以使用v-model等特性指令来实现数据的双向绑定。
二、 v-model的原理和应用
在开发过程中,我们经常使用v-model指令在表单元素上创建双向数据绑定。比如:
<input v-model="parentValue" />
而实际的含义是:
<input v-bind:value="parentValue"
v-on:input="parentValue = $event.target.value" />
所以,v-model相当于通过v-bind向子组件传递prop,然后通过子组件的事件监听来更新父组件的值。那么,清楚了v-model的语法,我们也可以使用v-model来实现一个双向数据绑定的组件。下面是官方文档的一个例子:
Vue.component('base-checkbox', {
model: {
prop: 'checked',
event: 'change'
},
props: {
checked: Boolean
},
template: `
<input
type="checkbox"
v-bind:checked="checked"
v-on:change="$emit('change', $event.target.checked)"
>
`
})
至此,我们已经可以自己封装一个双向数据绑定的组件了,v-model就是vue的双向数据绑定的原理了吗?显然不是的!
三、 真•核心原理:数据劫持
大家都知道,要想实现双向数据绑定,v-model传入的参数须是data属性里的变量,否则还是无法实现双向绑定的效果。那么,data定义的变量和普通声明的变量究竟有何区别?
data () {
return {
test: {
a: 1
}
}
},
mounted () {
console.log(this.test)
}
控制台输出的结果:
我们可以看到test Object的a属性带有getter和setter两个function。可以看出vue data定义的变量的属性是由Object.defineProperty()添加的,同时Object.defineProperty()也实现了vue的数据劫持。下面代码中,我们给a赋值触发了setter,然而setter并没有把value赋值给a,所以a的值还是undefined,我把这种现象理解为数据劫持。
使用Object.defineProperty()定义一个test Object:
var test = {};
Object.defineProperty(test, 'a', {
set: function reactiveSetter(value) {
console.log('set a ...');
},
get: function reactiveGetter() {
console.log('get a ...');
}
});
test.a = 1;
console.log(test);
console.log(test.a);
控制台输出结果:
可以看到Object.defineProperty()定义的test和data定义的test相同。而且,我们在给test.a赋值的时候会触发setter,获取test.a时会触发getter。所以我们可以在setter里通过事件来驱动View的更新,结合v-model 的DOM事件驱动数据更新就可以达到双向数据绑定的效果了。