1.首先复习一下我们平时是怎么写双向绑定的
<div id="app">
<input type="text" v-model="msg">
<p> {{msg}}</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>
new Vue({
el:'#app',
data() {
return {
msg:'',
}
},
})
</script>
基本原理如图:
2.现在,让我们来看一下双向绑定的原理,此时我们先要了解到一个es6的对象添加属性的方法:defineProperty
a.defineProperty给对象赋值的写法:
<script>
var obj = {};
//defineProperty有三个参数,不写全会报错
Object.defineProperty(obj, 'name', {
get() {
console.log('取值了')
},
set() {
console.log('赋值了')
}
})
</script>
这样做的好处是什么呢?- - -可以监听属性的赋值和取值
当赋值时,会触发set事件,当取值时,会触发get事件
如果你抱有疑惑,可以执行上面代码,在浏览器中调试,你会得到下面的结果:
b.规范写法:get中:必须返回 _属性名 set中:必须写 _属性名=value
这里的value,就是你传过来的值
<script>
var obj = {};
//defineProperty有三个参数,不写全会报错
Object.defineProperty(obj, 'name', {
get() {
console.log('取值了')
// 必须返回 _属性名
return _name;
},
set(value) {
// 必须写 _属性名=value
_name=value;
console.log('赋值了:' + value)
}
})
</script>
这样写之后,你会发现obj中的name有值了(任意注释一个再执行,然后观察结果,你可以试一下)
3.让我们回过头来再来看看双向绑定的特点:
我们需要接收文本框输入的值,给msg,具体而言是data里面的msg,data.msg再把值赋给p标签;相当于:我们要监听文本框输入的值,一边输入,一边把值赋给msg
让我们仿写一下,代码如下:
<!-- 写一个输入框和几个p标签 v-mode和v-bind是一个行内属性 只是模拟一下-->
<input type="text" v-model>
<p v-bind></p>
<p v-bind></p>
<p v-bind></p>
<p v-bind></p>
<script>
var txt=document.querySelector('[v-model]')
txt.oninput=function(){
// 那我们什么时候知道值发送改变了呢? 当data.msg赋值的时候,就会调用set方法
data.name=txt.value
//这样写后,就已经实现了把输入框的value双向绑定到了name属性中了,你可以去set中打印进行验证
}
// 为了模仿的像一点 我们把对象的名字改为data
var data={};
Object.defineProperty(data,'name',{
get(){
console.log('取值了')
return _name;
},
set(value){
_name=value;
console.log(data.name)
//但是如何绑定到P标签呢?
var list=document.querySelectorAll('[v-bind]')
for(var i=0;i<list.length;i++) {
list[i].innerHTML=value;
}
}
})
</script>
效果如下图:
换言之:当我们在输入框输入值时,会触发oninput方法,然后txt.value将得到的值赋值给data.name,这时会触发set方法,在set中,不仅完成了赋值,还找到了所有绑定的p标签然后把文本框输入的值给了P标签,最终实现了双向绑定
这就是目前vue2.0中的双向绑定,但是尤大神说将在vue3.0中使用proxy来实现双向绑定
4.proxy中的双向绑定原理:
a.使用proxy代码如下:
<script>
var data={}
var dataProxy=new Proxy(data,{
get(obj,prop) {
console.log('调用了')
},
set(obj,prop,value) {
console.log('赋值了')
}
})
dataProxy.name='jack'
console.log(dataProxy.name)
</script>
执行结果如下:
看到这里,你也许会问,proxy和defineproprety有什么区别吗?
defineProperty只能监听到自己所写的属性,其他的无法监听,但是proxy可以监听到所有写的属性,例如我们在调试时写其他的属性,也会监听到,如图:
b.利用proxy实现双向绑定,代码如下:
<input type="text" v-model>
<p v-bind></p>
<script>
var txt=document.querySelector('[v-model]')
txt.oninput=function(){
dataProxy.prop=txt.value
}
var data={}
var dataProxy=new Proxy(data,{
get(obj,prop) {
return obj[prop]
console.log('调用了')
},
set(obj,prop,value) {
obj[prop]=value;
//console.log(obj==data);//true
//console.log('赋值了')
document.querySelector('[v-bind]').innerHTML=value
}
})
// dataProxy.name='jack'
// console.log(dataProxy.name)
</script>
- 与defineProperty类似,proxy在get中要返回
return obj[prop]
,在set中,要赋值obj[prop]=value
- obj是我们传过来的对象 打印console.log(obj==data),得到的值为true
- prop为属性名
- value为属性值
实现的结果如图: