MVVM
说到 Vue 的双向绑定首先联系到的就是 MVVM(Model-View-ViewModel)模式了,当视图发生改变的时候传递给 VM,再让数据得到更新,当数据发生改变的时候传给 VM,使得视图发生改变。
MVVM 模式是通过以下三个核心组件组成:
- M: Model - 包含了业务和验证逻辑的数据模型;
- V: View - 定义屏幕中 View 的结构,布局和外观;
- VM: ViewModel - 扮演“View”和“Model”之间的使者,帮忙处理 View 的全部业务逻辑。
优点
- 低耦合
视图(View)可以独立于 Model 变化和修改,一个 ViewModel 可以绑定到不同的 View 上,当 View 变化的时候Model 可以不变,当 Model 变化的时候 View 也可以不变
- 可重用性
你可以把一些视图逻辑放在一个 ViewModel 里面,让很多 View 重用这段视图逻辑
- 独立开发
开发人员可以专注于业务逻辑和数据的开发(ViewModel),设计人员可以专注于页面设计,使用Expression Blend可以很容易设计界面并生成xaml代码
- 可测试
界面素来是比较难于测试的,而现在测试可以针对ViewModel来写
缺点:
- 数据绑定使得 Bug 很难被调试
- 一个大的模块中model也会很大,虽然使用方便了也很容易保证了数据的一致性,当时长期持有,不释放内存就造成了花费更多的内存
- 数据双向绑定不利于代码重用
vue2使用的是“数据劫持”结合“发布者订阅”模式的方式来实现的。
那么什么是发布者订阅模式?什么是数据劫持?
布者订阅模式
发布者订阅模式分两类:Dep类与watcher类
Dep类:
- 负责进行依赖收集
- 首先,要有一个数组,专门进行存储所有的订阅信息
- 其次,要提供一个向数组中可以追加订阅信息的方法
- 然后还要提供一个循环,循环触发数组中的每一个订阅信息
watcher类:
- 负责订阅一些事件
用js写一个简单的发布订阅:
class Dep {
constructor() { //创建一个实例
this.subs = [] //这个数组用于存储所有的发布者信息
}
//向subs数组中添加订阅者的信息
addSubs(watcher) {
this.subs.push(watcher)
}
//通知的方法
notify() {
//循环收藏的订阅者,触发其回调函数
this.subs.forEach((watcher) => watcher.upData())
}
}
//订阅者的类
class Watcher {
constructor(cb) { //cb回调传一个函数(根据最新的数据,更新自己)
this.cb = cb //将cb挂载在自己身上
}
//提供了一个触发回调的方法
upData() {
this.cb() //当数据更新时,调用cb更新数据
}
}
//第一个订阅者
const w1 = new Watcher(() => {
console.log('第一个订阅者')
})
//第二个订阅者
const w2 = new Watcher(() => {
console.log('第二个订阅者')
})
//创建一个Dep
const dep = new Dep();
// 向dep中的subs数组中存储订阅者
dep.addSubs(w1) //添加第一个订阅者
dep.addSubs(w2) //添加第二个订阅者
dep.notify() //发布通知
如何进行的监听数据的改变?
---答:数据劫持Object.defineProperty
Object.defineProperty 对传入的数据进行相应的数据拦截,为其动态添加 get 与 set 方法。当数据变化的时候,就会触发对应的 set 方法,当 set 方法执行完成时,内部会进一步触发订阅者 watcher,当数据改变,进行虚拟 Dom 对比(diff 算法),执行 render 函数,进行视图更新。
缺点
1、不能监听数组的变化
2、只能监听已存在的属性,对于新增或删除的属性不能监听
如何使用Object.defineProperty:
const obj={
name:"2z",
age:20
}
//赋值操作
obj.name="ls"
//取值操作
console.log(obj.name);
//如何监听数据?
Object.defineProperty(obj,'name',{
enumerable:true,//当前属性是否允许被循环
configurable:true,//当前属性是否允许被配置,例:delete删除,如果当前为false,
//当前这个属性无法删除
get(){//监听获取
//当有人获取obj.name时,将触发get,可以拦截别人对obj.name的获取
return '有人获取了obj.name'
},
set(newVal){//监听更改
//当有人想要对obj.name重新赋值的时候,将会触发
console.log('赋值'+newVal+'失败')
},
})
vue在监听数据更新变化的时候是在哪里通知订阅者的?
--答:在set中!
//只要给obj,name进行赋值时,就会触发name属性中的set,然后可以在set中拿到新值,
//所以在拿到新值的时候,就需要通知订阅者,要更新数据了,所以我们应该在set中调用
//notify()
set(newVal){//监听更改
//当有人想要对obj.name重新赋值的时候,将会触发
console.log('赋值'+newVal+'失败')
dep.notify()//通知每个订阅者,数据变了,更新自身数据
},