一、关于双向数据绑定
-
数据模型和视图之间的双向绑定
-
Vue 中的 MVVM
-
通常,我们需要编写代码,将从服务器获取的数据进行“渲染”,展现到视图上。每当数据有变更时,我们会再次进行渲染,从而更新视图,使得视图与数据保持一致。也就是:
model-view.png -
而另一方面,页面也会通过用户的交互,产生状态、数据的变化,这个时候,我们则编写代码,将视图对数据的更新同步到数据,以致于同步到后台服务器。也就是:
view-model.png
双向数据绑定
-
当数据发生变化的时候,视图也就发生变化,当视图发生变化的时候,数据也会跟着同步变化;可以这样说用户在视图上的修改会自动同步到数据模型中去,数据模型也是同样的变化
model-view-mode.png -
双向数据绑定的优点:无需和单向数据绑定那样进行 CRUD(Create,Retrieve,Update,Delete)操作,双向数据绑定最常应用在就表单上,这样当用户在前端页面完成输入后,不用任何操作,我们就已经拿到了用户输入好的数据,并放到数据模型中了
二、双向数据绑定的实现
v-model 实现双向数据绑定
- v-model 是一个 value 绑定加 input 事件回调的语法糖
<input v-model="something">
// 等价于
<input
v-bind:value="something"
v-on:input="something = $event.target.value">
三、双向数据绑定的原理
-
Object.defineProperty()
方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象 -
Proxy
对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等) -
Reflect
是一个内置的对象,它提供拦截 JavaScript 操作的方法。这些方法与proxy handlers的方法相同。Reflect
不是一个函数对象,因此它是不可构造的
2.0 版
<div>
<input type="text" id="userName" name="">
<br>
<span id="view"></span>
</div>
<script type="text/javascript">
let defineObject = {
userInfo: {
userName: '',
password: '123456'
}
}
let input = document.getElementById('userName')
let view = document.getElementById('view')
// 更新视图
function update() {
view.innerText = defineObject.userInfo.userName
}
//更新数据
input.oninput = function () {
defineObject.userInfo.userName = this.value
}
observerData(defineObject)
// 属性循环绑定
function observerData(target) {
if(!target || typeof target !== 'object') return target;
Object.keys(target).forEach(item => {
bindProps(target, item, target[item])
})
}
// 属性绑定监听
function bindProps(target, prop, value) {
observerData(value) // 循环绑定
Object.defineProperty(target, prop, {
get() {
console.log('getProp: ', value)
return value
},
set(newValue) {
console.log('setProp: ', newValue)
if(newValue === value) return
value = newValue
update()
}
})
}
</script>
- 缺点:
- 在 Vue 中,Object.defineProperty 无法监控到数组下标的变化,导致直接通过数组的下标给数组设置值,不能实时响应。 为了解决这个问题,经过 vue 内部处理后可以使用以下几种方法来监听数组
push() pop() shift() unshift() splice() sort() reverse()
- Object.defineProperty 只能劫持对象的属性,因此我们需要对每个对象的每个属性进行遍历。Vue 里,是通过递归以及遍历 data 对象来实现对数据的监控的,如果属性值也是对象那么需要深度遍历,显然如果能劫持一个完整的对象,不管是对操作性还是性能都会有一个很大的提升
- 在 Vue 中,Object.defineProperty 无法监控到数组下标的变化,导致直接通过数组的下标给数组设置值,不能实时响应。 为了解决这个问题,经过 vue 内部处理后可以使用以下几种方法来监听数组
3.0版
let defineObject = {
userInfo: {
userName: '',
password: '123456'
}
}
let input = document.getElementById('userName')
let view = document.getElementById('view')
// 更新视图
function update(newValue) {
view.innerText = newValue
}
//更新数据
input.oninput = function () {
newProxy.userName = this.value
}
let newProxy = new Proxy(defineObject, {
get(target, key, receiver) {
console.log('getProp: ', target[key])
return Reflect.get(target, key);
},
set(target, key, newValue, receiver) {
console.log('setProp: ', target[key])
if(key === 'userName') {
update(newValue)
}
return Reflect.set(target, key, newValue)
}
});
网友评论