中所周知,vue采用的是MVVM的设计思想,那么到底什么是MVVM呢?
MVVM,说到底还是一种分层架构。它的分层如下:
MVVM是Model-View-ViewModel的缩写。
- Model: 域模型,用于持久化
- View: 作为视图模板存在
-
ViewModel: 作为视图的模型,为视图服务
vue的核心部分是响应数据
在
new Vue()
之后。 Vue 会调用 _init
函数进行初始化,也就是这里的 init 过程,它会初始化生命周期、事件、 props、 methods、 data、computed 与 watch 等。其中最重要的是通过Object.defineProperty
设置 setter
与 getter
函数,用来实现响应式以及依赖收集,它使得当被设置的对象被读取的时候会执行 getter
函数,而在当被赋值的时候会执行 setter
函数。接下来简单实现一下vue的响应式:
class MVVM {
// Vue 构造类
constructor(options) {
this._data = options.data
// 数据劫持,给所有属性添加getter和setter
new Observer(this._data)
/* 将数据代理到vue实例上
在开发中我们之所以能够以this.xx 操作data中的数据,就是因为vue将data中的数据代理到vue的实例上了 */
this.proxyData(this._data)
new Watcher()
}
// 代理数据的方法
proxyData(data) {
Object.keys(data).forEach(key => {
Object.defineProperty(this, key, {
get() {
return data[key]
},
set(newVal) {
data[key] = newVal
}
})
})
}
}
class Observer {
constructor(data) {
this.observer(data)
}
//添加数据监听
observer(data) {
//对data做校验
if (!data || typeof data !== 'object') {
return
}
Object.keys(data).forEach(key => {
this.defineReactive(data, key, data[key])
// 递归的方式深度监听
this.observer(data[key])
})
}
// 响应数据
defineReactive(object, key, val) {
let _this = this
let dep = new Dep()
Object.defineProperty(object, key, {
enumerable: true,
configurable: true,
get() { // 当getter的时候添加订阅
Dep.target && dep.add(Dep.target)
return val
},
set(newVal) {
if (newVal === val) return
_this.observer(newVal) // 如果设置的新值是对象,需要深度监听
val = newVal
dep.notify()
}
})
}
}
class Watcher {
constructor() {
Dep.target = this
}
updata() {
alert('数据更新')
}
}
Dep.target = null;
class Dep {
constructor() {
this.subs = []
}
add(watcher) { // 添加订阅
this.subs.push(watcher)
}
notify() { // 遍历subs通知更新
this.subs.forEach(watcher => {
watcher.updata()
})
}
}
在的代码中 observe
的目的是遍历对象,在内部对数据进行劫持,即添加getter
和 setter
,我们把劫持的逻辑单独抽取成 defineReactive
方法,需要注意的是 observe
方法在执行最初就对当前的数据进行了数据类型验证,然后再循环对象每一个属性进行劫持,目的是给同为 Object
类型的子属性递归调用 observe
进行深度劫持。
在 defineReactive
方法中,创建了 Dep
的实例,并对 data
的数据使用 get
和 set
进行劫持,当访问的时候触发getter
,添加订阅,将这个 watcher
添加到 Dep
的 subs
数组中进行统一管理,因为在代码中获取 data
中的值操作比较多,会经常触发get
,我们又要保证 watcher
不会被重复添加,所以在 Watcher
类中,获取旧值并保存后,立即将 Dep.target
赋值为 null
,并且在触发 get
时对 Dep.target
进行了短路操作,存在才调用 Dep 的 addSub 进行添加。
通过以上的代码可见,vue的响应式原理主要是通过Object.defineProperty
方法来实现,但是它存在着一些局限,如对属性的添加、删除动作的检测,对数据基于下标的修改和对 .length
修改的检测存在着一些问题,所以vue官方提供一个API$set()
方法,来解决基于数组下标操作无法更新视图的问题。据说vue的下一个版本,vue3.0计划使用es6的Proxy
来重写vue的数据监听。
网友评论