前言
什么是数据双向绑定?
Vue使用的是一个MVVM框架,即数据双向绑定,即当数据发生变化的时候,视图也就发生变化,当视图发生变化的时候,数据也会跟着同步变化。这也算是vue的精髓之处了。值得注意的是,我们所说的数据双向绑定,一定是对于UI控件来说的,非UI控件不会涉及到数据双向绑定。
为什么要实现数据的双向绑定?
在Vue中,如果使用Vuex,实际上数据还是单向的,之所以说是数据双向绑定,这是对于的UI控件来说,对于我们处理表单,Vue的双向数据绑定用起来就特别舒服了。
即两者并不互斥, 在全局性数据流使用单向,方便跟踪; 局部性数据流使用双向,简单易操作。
如何实现双向绑定?
Vue是通过“数据劫持”+“发布-订阅模式”实现数据的双向绑定的。
“订阅-发布”模式:
其中主要包括两个类:Dep类、Watcher类
Watcher:用于订阅事件。该类中主要包含: 我们将实例化的一个个Wacher对象比作一个个的顾客
- 在构造函数中创建一个回调(其中包含着根据最新的数据重新渲染界面的方法)。 因为回调中包含了渲染页面的方法,我们就将回调比作顾客想要做某件事的方法(例如看书的方法)
- update:在这个方法中,可以拿到最新的数据,然后触发回调。(该方法供给Dep的notify方法来控制调用执行) 将获取最新数据比作拿到书,触发回调比作用上面的看书的方法来看书
Dep:主要作用是进行依赖收集,触发所收集的依赖。该类中主要包含: 我们将Dep比作一个咖啡馆
- [ ] :一个数组,专门存放所有的订阅者信息。这个数组中包含了一个个的订阅信息,就像咖啡馆坐着一个个想要看书、聊天、玩游戏的人,他们知道自己想做什么,但是要等到notify方法执行的时候,轮到自己了才可以做
- add:向数组添加订阅。 比作向咖啡馆拉有事要做的人,让他们在咖啡馆做
- notify:循环触发数组的每个订阅信息,向DOM发布通知(其中就包含执行每个订阅的update和保存在其中的回调函数)。让每桌的顾客,按顺序地来做他们想做的事。第一桌的顾客先看书,看完第二桌的顾客去聊天,聊完第三桌的顾客去玩游戏。
简单的模拟一下订阅发布的实现过程,带大家更好的理解一下:
// 收集依赖、订阅依赖
class Dep{ // 酒吧
constructor(){
// 这个 subs 数组,用来存放所有的订阅者的信息
this.subs = []; // 迪迦、雷欧、泰罗一会会被拽进来,因为他们有事要和地球人做
}
// 向 subs 数组中,添加订阅者的信息
addSub(watcher){
this.subs.push(watcher); // 一会就是通过这个方法把他们仨拽进去的
}
// 发布通知的方法
notify(){
this.subs.forEach((watcher)=>watcher.update()); // 循环遍历数组中的对象,让这三个奥特曼依次执行他们的 update 方法,也就让他们依次拿着他们得到的东西去做他们想做的事 注意!东西不是在这得到的,是在 update 方法中得到的
}
}
// 订阅者的类 每实例化一个对象,就可以得到一个取数据来渲染的 Dom 就可以得到一个有事要做的奥特曼
class Watcher {
constructor(cb){
this.cb = cb; // 这里面用于保存每个奥特曼想做的事,例如迪迦的回调里面说他想看书,那就把看书这件事保存进去
}
// 调用回调的方法
update(){
this.cb(); // 执行回调,在这里会得到最新的数据,这个方法由notify调用。会把迪迦想看的书给他,然后告诉迪迦,去看你的书吧。这个方法交给酒吧的notify方法,让notify这个方法告诉每个奥特曼,该谁去做自己想做的事了
}
}
const w1 = new Watcher(()=>{
// 想做的事
console.log("我是迪迦,我想看会地球的书~");
})
const w2 = new Watcher(()=>{
console.log("我是泰罗,我想和地球人聊会天~");
})
const w3 = new Watcher(()=>{
console.log("我是雷欧,我想和地球人打会游戏~");
})
// 建个酒吧
const dep = new Dep();
// 把迪迦拽进去
dep.addSub(w1);
// 把泰罗拽进去
dep.addSub(w2);
// 把雷欧拽进去
dep.addSub(w3);
dep.notify(); // 执行notify方法,让他们依次得到需要的书、人、手机,然后去做自己想做的事。
这里暂时没有细说数据劫持的过程,只是一笔带过,大致的回顾一下:
先说一下Watcher的作用,将“获取数据”和“用新数据渲染Dom的方法”封装到一个订阅事件中。Dep通过add方法添加订阅事件到数组中,在调用Dep实例对象的notify方法时,拿着获取到的最新数据到页面去渲染。
整个数变化到渲染的过程,大致就是:
首先通过数据劫持来监听data的数据变化,一旦监听到了数据发生变化,订阅发布开始获取数据,然后调用Dep的notify方法发布通知到每个订阅,订阅根据最新的数据渲染Dom。
这是数据从data到view的过程。从view到data相对简单一些,只需要借助Object.defineProperty的set,即可以获取页面变化的数据,然后将这个数据更新到data中,再渲染到需要的地方。
这是本人的一些理解,如有错误请及时指出,定及时更正,谢谢。