以下均为个人理解,希望大家带着自己疑问去阅读,有异议请在讨论区或公众号留言,作者是一个喜欢讨论的人。
一. 双向绑定是什么?
目前在前端框架中,多多少少都会有一些双向绑定的内容,我们也许用的很熟练,但是却很少去思考它的本质。
双向,理解即两个方向。在前端中,可理解为分别是视图上展现出数据的页面端和浏览器分配的一块特定存储数据的内存端。
绑定,理解为能相互改变对方,最终达到一致性。比如在Vue或React中,,通过把接口返回的数据置给Vue的data或React的state中,经过页面渲染后便可以在视图上表现出来。
双向绑定,即眼睛不可见的一块特定计算机内存中的数据发生变化后,能够体现我们眼睛能看到的页面视图层面上。比如在Vue中,当改变实例中data时,页面便对应改变,当我们主动改变页面上值时,再去检查实例中的data时,也相应发生了改变。注意React是单向数据流,只能是内存数据变化引起视图数据变化,而视图数据变化则需要手动setState去改变特定内存数据。
二. 如何实现一个双向绑定?
首先,理解了上面关于双向绑定的含义后,请停一下,花几分钟思考一下,如果让你去实现一个双向绑定,请理一下自己的思路,继续往下阅读,看看我们的思想有没有碰撞。
思路1:
如果想让A和B两块内容相互保持一致,可以让A和B同步起来,当在改变A的触发事件执行的时候顺手改一下B,在改变B的时候在改一下A,从而达到AB同步。
在页面中,当接口返回后,先将内存的变量赋值,再将页面某一个dom要展示的value重新设置一下。当手动改变页面上dom的value时,同时将之前申请的内存里的变量再做一次赋值操作,这样便可以完成两块内容的同步。
看下面这段代码即上面的实现:
const obj = {};
const dom = document.querySelector('.input');
let text = '初始化'
Object.defineProperty(obj, 'text', {
set(value) {
text = value;
dom.value = value;
}
});
// 页面手动改变input值
dom.onchange=function(e){
const inputVal = e.target.value;
data.text = inputVal
};
思路2:
想让AB保持一致,但是改变A时候并不去关注B的内容,这时候就需要B自身订阅监听A的内容是否变化,如果变化更新自己的值,从而达到目标。
具体操作,当改变内存中的某个变量A的值变化前,由于B想要跟A同步,B即需要监听A的值是否变化,则需要一个主动轮询检查AB内容值是否一致,一旦变化则更新B,从而达到AB一致性。
看下面这段代码即上面的实现:
let text = '初始化';
const dom = document.querySelector('.input');
const oldText = dom.innerText;
setInterval(()=>{
if (oldText!==text) {
dom.value = text;
}
}, 1000);
setTimeout(()=>{
text = '内容变化';
}, 3000);
// 页面手动改变input值
dom.onchange=function(e){
const inputVal = e.target.value;
data.text = inputVal
};
思路3:
AB想保持一致性,但是想要AB两者之间要完全解耦,没有过多的联系,则需要引入一个第三方进来,让A变化时,告诉一下第三方,第三方去做更新B的值的动作。
在实现过程中,引入发布者订阅者的方式,当A发生变化时,发布一条消息,而B则提前订阅好A的内容,由A的变化引起发布,从而B的订阅也得到变化,保持了AB的一致性。
需要先完成发布/订阅的角色。
let data ={ text: 'sada' };
const dom = document.querySelector('.input');
const oldText = dom.innerText;
var event = {
ev: '',
listen (fn) {
this.ev = fn;
},
trigger (money) {
this.ev(money);
}
};
event.listen((text)=>{
dom.value = text;
});
// 模拟数据变化
setInterval(()=>{
data.text = `变化了${Math.random()}`
event.trigger(data.text)
}, 3000);
// 页面手动改变input值
dom.onchange=function(e){
const inputVal = e.target.value;
data.text = inputVal
};
三. 总结
以上从三种方式去梳理了双向绑定的实现。大名鼎鼎的Vue便属于第一种和第三种的结合,称之为“数据挟持”,而Angularjs即是第二种称为“脏检查”,两大框架在此之上做了很多的优化内容,最终成就如今被人称赞的“双向绑定”。
如上内容均为自己总结,难免会有错误或者认识偏差,如有问题,希望大家留言指正,以免误人。有什么问题请留言,会尽力回答之。