vue 2.x 版本使用 defineProperty 来实现双向数据绑定,而 3.0 之后换成了 proxy。
事实上 defineProperty 本意并不是用来做双向数据绑定的,而是进行对象属性的定义,由于其中的访问器可以对属性进行拦截,所以尤大用这个特性来实现双向数据绑定。
现在有如图所示的界面:

想要实现双向数据绑定的效果,事实上只需要两步:
- 数据更新通知数据容器
- 数据容器接到通知更新视图

而 es6 中新增的 proxy 对象可以很好的帮我们实现这个容器。
那么首先我们需要创建一个容器:
class View{
constructor(){
const proxy = new Proxy({},{
get(obj,props){},
set(obj,props,value){}
})
}
}
我们通过 proxy 代理这个容器对象,然后为输入源增加事件:
class View{
constructor(){
const proxy = new Proxy({},{
get(obj,props){},
set(obj,props,value){}
})
this.init(proxy)
}
init(proxy){
const els = document.querySelectorAll("[v-model]")
els.forEach(el =>{
el.addEventListener('keyup',() =>{})
})
}
}
当我们点击的时候,将输入源中的值存入容器中:
class View{
constructor(){
const proxy = new Proxy({},{
get(obj,props){},
set(obj,props,value){}
})
this.init(proxy)
}
init(proxy){
const els = document.querySelectorAll("[v-model]")
els.forEach(el =>{
el.addEventListener('keyup',() =>{
proxy[el.getAttribute("v-model")] = el.value;
})
})
}
}
到这里我们完成了一半,接下来就是当容器收到消息之后进行 render 渲染页面:
class View{
constructor(){
const proxy = new Proxy({},{
get(obj,props){},
set(obj,props,value){
this.render(prop,value)
// set 成功需要返回一个 bool,否则在严格模式下会抛出错误
return true;
}
})
this.init(proxy)
}
init(proxy){
const els = document.querySelectorAll("[v-model]")
els.forEach(el =>{
el.addEventListener('keyup',() =>{
proxy[el.getAttribute("v-model")] = el.value;
})
})
}
render(prop,value){
document.querySelectorAll(`[v-model="${prop}"]`).forEach(el => {
el.value = value;
});
document.querySelectorAll(`[v-bind="${prop}"]`).forEach(el => {
el.innerHTML = value;
});
}
}
到这里就实现了双向数据绑定的基本原理:
