模拟vue响应式,依赖收集
// 通过调用实现对对象的[响应式]化
function defineReactive(obj, key, val) {
// 初始化一个订阅者
const dep = new Dep()
// 响应
Object.defineProperty(obj, key, {
enumerable: true, // 属性可枚举
configurable: true, // 属性可被修改或删除
get: function reactiveGetter() {
// 将Dep.target(即当前的Watcher对象存入dep的subs中)
dep.addSub(Dep.target)
return val
},
set: function reactiveSetter(newVal) {
if (newVal === val) {
return
}
// 在set的时候触发dep的notify来通知所有的Watcher对象更新视图
dep.notify()
}
})
}
// 批量实现响应式
function observer(obj) {
if (!obj || (typeof obj !== 'object')) {
return
}
Object.keys(obj).forEach((key) => {
defineReactive(obj, key, obj[key])
})
}
// 封装一个Vue
/* class Vue {
// Vue构造类
constructor(options) {
this._data = options.data
observer(this._data)
}
} */
// 订阅者,存放Watcher观察者对象
class Dep {
constructor() {
// 用来存储Watcher对象的数组
this.subs = []
}
// 在subs中添加一个Watcher对象
addSub(sub) {
this.subs.push(sub)
}
// 通知所有Watcher对象更新视图
notify() {
this.subs.forEach((sub) => {
sub.update()
})
}
}
// 观察者
class Watcher {
constructor() {
// 在new一个Watcher对象时将该对象赋值给Dep.target,在get中会用到
Dep.target = this
}
// 更新视图的方法
update() {
console.log('视图更新了...')
}
}
Dep.target = null
// 实例化
class Vue {
constructor(options) {
this._data = options.data
observer(this._data)
/* 新建一个Watcher观察者对象,这时候Dep.target会指向这个Watcher对象 */
new Watcher()
}
}
let newVue = new Vue({
data: {
'aa': 11,
'test': '212'
}
})
console.log(newVue)
/* 在这里模拟render的过程,为了触发test属性的get函数,这样就加入依赖了 */
console.log('render~', newVue._data.test)
/* 在这里模拟触发test属性的set函数,这样就触发依赖的监听者从而update */
newVue._data.test = '改变'
// 流程图: https://github.com/Tetegw/summarize/blob/master/%E3%80%90%E5%AD%A6%E4%B9%A0%E3%80%91vue/%E6%A8%A1%E6%8B%9F%E5%93%8D%E5%BA%94%E5%BC%8F.jpg