摘要
我们都知道Vue的响应式是通过Object.defineProperty来进行数据劫持。但是那是针对Object类型可以实现, 如果是数组呢? 通过set/get方式是不行的。
但是Vue作者使用了一个方式来实现Array类型的监测: 拦截器。
核心思想
通过创建一个拦截器来覆盖数组本身的原型对象Array.prototype。
拦截器
通过查看Vue源码路径vue/src/core/observer/array.js。
/** * Vue对数组的变化侦测 * 思想: 通过一个拦截器来覆盖Array.prototype。 * 拦截器其实就是一个Object, 它的属性与Array.prototype一样。 只是对数组的变异方法进行了处理。*/function def (obj, key, val, enumerable) { Object.defineProperty(obj, key, { value: val, enumerable: !!enumerable, writable: true, configurable: true })}// 数组原型对象const arrayProto = Array.prototype// 拦截器const arrayMethods = Object.create(arrayProto)// 变异数组方法:执行后会改变原始数组的方法const methodsToPatch = [ 'push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse']methodsToPatch.forEach(function (method) { // 缓存原始的数组原型上的方法 const original = arrayProto[method] // 对每个数组编译方法进行处理(拦截) def(arrayMethods, method, function mutator (...args) { // 返回的value还是通过数组原型方法本身执行的结果 const result = original.apply(this, args) // 每个value在被observer()时候都会打上一个__ob__属性 const ob = this.__ob__ // 存储调用执行变异数组方法导致数组本身值改变的数组,主要指的是原始数组增加的那部分(需要重新Observer) let inserted switch (method) { case 'push': case 'unshift': inserted = args break case 'splice': inserted = args.slice(2) break } // 重新Observe新增加的数组元素 if (inserted) ob.observeArray(inserted) // 发送变化通知 ob.dep.notify() return result })})
关于Vue什么时候对data属性进行Observer
如果熟悉Vue源码的童鞋应该很快能找到Vue的入口文件vue/src/core/instance/index.js。
function Vue (options) { if (process.env.NODE_ENV !== 'production' && !(this instanceof Vue) ) { warn('Vue is a constructor and should be called with the `new` keyword') } this._init(options)}initMixin(Vue)// 给原型绑定代理属性$props, $data// 给Vue原型绑定三个实例方法: vm.$watch,vm.$set,vm.$deletestateMixin(Vue)// 给Vue原型绑定事件相关的实例方法: vm.$on, vm.$once ,vm.$off , vm.$emiteventsMixin(Vue)// 给Vue原型绑定生命周期相关的实例方法: vm.$forceUpdate, vm.destroy, 以及私有方法_updatelifecycleMixin(Vue)// 给Vue原型绑定生命周期相关的实例方法: vm.$nextTick, 以及私有方法_render, 以及一堆工具方法renderMixin(Vue)export default Vue
新闻热点
疑难解答
图片精选