observe方法
-
这个方法是响应式处理的一个入口 它对所以非VNode类型的对象进行一个响应式的处理
observe方法的核心
function observe(value): Observer | void { if (!isObject(value) || value instanceof VNode) { return; } if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) { // 该数据已经进行过响应式处理了 return value.__ob__; } else { // 实例化一个Observer对象 在其中对于value进行响应式处理 ob = new Observer(value); } return ob; }
Observer
-
实例化的时候接收一个值作为参数, 对于数组和普通对象进行不同的处理
数组无法通过Object.defineProperty去进行数据劫持, 通过修改该数组的原型对象来实现数组操作的响应式
// 所有的数组对象指向同一个公共的方法 const arrayProto = Array.prototype // arrayMethods是一个原型对象为Array的原型对象 但是进行了处理 const arrayMethods = Object.create(arrayProto) // 以下七种方法就是可以触发响应式(重新渲染界面)的方法 const methodsToPatch = ['push','pop','shift','unshift','splice', 'sort','reverse'] // 覆盖arrayMethods中的方法 methodsToPatch.forEach(function(method) { const original = arrayMethods[method]; Object.defineProperty(arrayMethods, method, { value: function(...args) { // 保存初始方法的执行结果 const result = original(...args) // __ob__ 属性上保存的就是该对象的响应式对象 const ob = this.__ob__ let inserted // args是一个参数数组 对于所有需要新加入值的方法 必须对新加入的值进行一个响应式处理 switch(method) { case 'push': case 'unshift': inserted = args break case 'splice': inserted = args.slice(2) break } // inserted是一个数组 对该数组中的数据进行响应式处理 if (inserted) ob.observeArray(inserted) // 派发更新 让所有依赖于该数据的watcher触发更新 ob.dep.notify() return result }, enumerable: false, writable: true, configurable: true }) }) function observeArray(items) { for (let i = 0 ; i < items.length; i++) { observe(items[i]); } }对于数组对象是通过替换原型对象来实现方法的一个拦截。 解决一个兼容性的问题
// 有些浏览器是支持__proto__的 有些是不支持的 const hasProto = '__proto__' in {} const arrayKeys = Object.getOwnPropertyNames(arrayMethods); // 支持__proto__属性 直接修改执行即可 function protoAugment (target, src: Object) { target.__proto__ = src } // 不支持__proto__属性, 将公共方法上的属性拷贝到对象上 function copyAugment (target: Object, src: Object, keys: Array<string>) { for (let i = 0, l = keys.length; i < l; i++) { const key = keys[i] def(target, key, src[key]) } }对于一个非数组对象来说 就只需要遍历对象的key 对每一个属性进行一个响应式处理
function walk(obj) { const keys = Object.keys(obj); for (let i = 0 ; i < keys.length ; i++) { // 响应式的核心 defineReactive(obj, keys[i]); } }Observer的核心代码
class Observer { value: any; // 与数据绑定的依赖对象 dep: Dep; constructor(value) { this.value = value dep = new Dep() // 将Observer绑定到value的__ob__属性上 def(value, '__ob__', this) if (Array.isArray(value)) { if (hasProto) { protoAugment(value, arrayMethods) } else { copyAugment(value, arrayMethods, arrayKeys) } observeArray(value) } else { walk(value) } } }
defineReactive方法
-
响应式处理的核心: 主要功能就是通过Object.defineProperty() 方法去对数据的访问和赋值进行一个拦截
-
defineReactive方法执行之后, 属性的getter和setter是一个Function 其中使用到了defineReactive中的数据, 所以val和childOb等都是**存在闭包Closure (defineReactive)**当中的
defineReactive的核心代码
function defineReactive(obj, key, val) { // dep对应该数据 const dep = new Dep() const property = Object.getOwnPropertyDescriptor(obj, key) if (property && property.configurable) { // 该属性无法配置 return; } // 接下来就要重写属性的getter和setter了 所以提前缓存原来的 const getter = property && property.get; const setter = property && property.set; if ((!getter || setter) && arguments.length === 2) { // 如果没有传value值 给val赋一个初值 val = obj[key]; } // 如果该属性的值 是一个对象的话 也对其进行响应式处理 let childOb = observe(val); Object.defineProperty(obj, key , { enumerable: true, configurable: true, get: function reactiveGetter() { const value = getter? getter.call(obj) : val; // 此处先不考虑依赖收集 return value; }, set: function reactiveSetter(newVal) { const value = getter? getter.call(obj) : val; if (newVal === value || (newVal !== newVal && value !== value)) { // 新值和原来的值相同的话不处理 // newVal !== newVal && value !== value 这个判断是源码当中存在的 个人理解为NaN的一个判断 return; } if (getter && !setter) { // 只读属性 return; } if (setter) { val = setter.call(obj, newVal) } else { val = newVal } // 新的值是对象时的响应式处理 childOb = observe(val) // 此处先不考虑派发更新 } }) }