简易说明
Vue内部通过Object.defineProperty方法属性拦截的方式,把data对象里每个数据的读写转化成getter/setter,当数据变化时通过watch观测通知视图更新
MVVM是什么
数据变化更新视图,视图变换更新数据
如何让数据变得可以观测呢
Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象。
如何起作用的呢
let car = {} let val = 3000 Object.defineProperty(car, 'price', { get(){ console.log('price属性被读取了') return val }, set(newVal){ console.log('price属性被修改了') val = newVal } })
然后你对car进行赋值以及读写都会触发get或者set的函数
一个简单的双向绑定函数
/** * 把一个对象的每一项都转化成可观测对象 *
@param { Object } obj 对象 */
function observable (obj) { if (!obj || typeof obj !== 'object') { return; } let keys = Object.keys(obj); keys.forEach((key) =>{ defineReactive(obj,key,obj[key]) }) return obj; }
/** * 使一个对象转化成可观测对象 *
@param { Object } obj 对象 *
@param { String } key 对象的key *
@param { Any } val 对象的某个key的值 */
function defineReactive (obj,key,val) { Object.defineProperty(obj, key, { get(){ console.log(`${key}属性被读取了`); return val; }, set(newVal){ console.log(`${key}属性被修改了`); val = newVal; } }) }
因为被拦截,所以就会定义出来一个观测的属性
依赖收集,根据订阅者-观察模式,创建一个消息收集模式
class Dep { constructor(){ this.subs = [] }, //增加订阅者 addSub(sub){ this.subs.push(sub); }, //判断是否增加订阅者 depend () { if (Dep.target) { this.addSub(Dep.target) } }, //通知订阅者更新 notify(){ this.subs.forEach((sub) =>{ sub.update() }) } }Dep.target = null;
修改objectdefineProperty的观察者模式
function defineReactive (obj,key,val) { let dep = new Dep(); Object.defineProperty(obj, key, { get(){ dep.depend(); console.log(`${key}属性被读取了`); return val; }, set(newVal){ val = newVal; console.log(`${key}属性被修改了`); dep.notify() //数据变化通知所有订阅者 } }) }
创建一个watch订阅者
class Watcher { constructor(vm,exp,cb){ this.vm = vm; this.exp = exp; this.cb = cb; this.value = this.get(); // 将自己添加到订阅器的操作 }, update(){ let value = this.vm.data[this.exp]; let oldVal = this.value; if (value !== oldVal) { this.value = value; this.cb.call(this.vm, value, oldVal); }, get(){ Dep.target = this; // 缓存自己 let value = this.vm.data[this.exp] // 强制执行监听器里的get函数 Dep.target = null; // 释放自己 return value; } }
网友评论