1. 使数据“可观测” (Observer)
class Observer {
value: any
constructor(value: any) {
this.value = value
if (Array.isArray(value)) {
// 数组的响应式原理
} else {
this.walk(value)
}
}
walk(data: any) {
const keys = Object.keys(data)
for (let i = 0; i < keys.length; i++) {
this.defineReactive(data, keys[i]) // 给数据增加响应式绑定
}
}
defineReactive(obj: any, key: string) {
if(typeof key === 'object') {
new Observer(key)
}
let val = obj[key]
const dep = new Dep() // 实例化一个依赖管理器
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get() {
// 触发依赖收集
console.log('触发依赖收集')
if (Dep.target) {
dep.depend()
}
return val
},
set(newVal) {
// 通知更新
if (newVal === val || (newVal !== newVal && val !== val)) return
console.log('数据更新了')
dep.notify()
val = newVal
}
})
}
}
2. 依赖管理器 (Dep)
class Dep {
subs: any[]
static target: any
constructor() {
this.subs = []
}
addSub (sub: any) {
this.subs.push(sub)
}
// 添加一个依赖
depend() {
if (Dep.target) {
Dep.target.addDep(this)
}
}
// 通知所有依赖进行更新
notify() {
const subs = this.subs.slice()
for (let i = 0; i < subs.length; i ++) {
subs[i].update()
}
}
}
Dep.target = null'
3. 依赖到底是谁?(Watcher)
class Watcher {
value: any
getter: Function
constructor(exOrFn: Function) {
this.getter = exOrFn
this.value = this.get() // 手动触发get
}
get() {
Dep.target = this // 设置 Dep.target,在触发依赖收集的时候能够分别
let value = this.getter.call(this) // 执行exOrFn触发get(触发渲染函数)
Dep.target = null // 重置 Dep.target,为下一个watcher实例做准备
return value
}
addDep(dep: any) {
dep.addSub(this)
}
update() {
console.log('watcher update', )
}
}
验证
const glob: any = global
// 模拟渲染render
function render() {
console.log('开始render', glob.$data.name, glob.$data.age)
setTimeout(() => {
console.log('开始render2', glob.$data.name, glob.$data.age)
},0)
}
const obj = new Observer({
name: 'xwk',
age: '30'
});
glob.$data = new Proxy(obj.value, {}) //代理到$data
new Watcher(render) // render里面进行了依赖收集
obj.value.name = 'xwk2' // 改变数据
obj.value.age = '31' // 改变数据
总结
- vue采用观察者模式实现的mvvm模式
- 每个data数据都会实例化一个Dep依赖管理器
- 每个vue模板文件在mounted生命周期的时候,会初始化一个渲染Watcher实例,并手动触发了一次本次渲染用到的所有data的get,进行依赖收集(dep.depend(),就是往watcher实例的subs里面追加),如果数据改变触发了set,则会通知依赖管理器(dep.notify()),然后dep执行依赖们(watcher实例,可能不止一个)进行update更新。