作为vue.js的核心之一,vue的响应式原理也是一个被讨论烂了的话题,但是我觉得作为一个前端工作者,甚至是一名专业从事vue开发的码农,这也是你必须要了解甚至掌握的知识.Vue 最独特的特性之一,是其非侵入性的响应式系统。数据模型仅仅是普通的 JavaScript 对象。而当你修改它们时,视图会进行更新。这使得状态管理非常简单直接. vue.js 实现双向绑定就是使用数据劫持 结合 观察者watcher - 订阅者Dep我们来着重解释一下这3个名词
数据劫持 Object.defineProperty()
这是一个挂载在Object原型上的一个方法,他的兼容性较好(功能上有一点缺陷),这也是为什么尤大大在v2的时候采用他做数据劫持而不是Proxy的原因.
这个方法有三个参数,第一个是你要定义的对象Object 第二个是你要修饰/描述的参数 前2个参数比较简单,我们着重介绍第三个参数,第三个参数是一个对象,包含6个属性.
1.value 属性的值 默认值是undefined(可以通过这里给属性赋值) 2.writable: 值是否可以重写。 true|false 默认为false(能否改变属性的值) 3.enumerable: 目标属性是否可以被枚举。true|false 默认为flase(属性能不能被遍历) 4.configurable: 目标属性是否可以被删除或是可以再次修改特性 true|false 默认为false(属性能不能被删除) 5.getter:一个当属性被访问到就会触发的回调,在这里我们就可以知道这个属性/数据有没有被用到 6.setter:一个当属性被修改是就会触发的回到,这这里我们就可以知道哪个数据被修改了,我们就能执行相应的操作.
观察者 Watcher
Vue 中定义一个 Watcher 类来表示观察订阅依赖。我们在响应式数据可能在很多地方用到,比如模板,或者一个watch,那么我们每一次用到的地方就要生成这个数据的一个watcher的实例记录相关的依赖,当数据改变的时候我们通知这些watcher去更新视图.
订阅者 Dep
订阅者dep是用来收集依赖和通知依赖更新的类,每一个相应式数据都有一个对应的dep实例,他有方法dep.add/remove 将依赖收集进sub数组,当数据发送改变时可以通过dep.notify去通知依赖更新.
发布者 Observe
Observe类有个方法就是definereactive函数,遍历递归data数据,给每一个属性添加getter和setter属性,使其变成响应式数据
数组处理
由于Object.defineProperty不能劫持数组的改变,所以vue中对数据做了单独的处理,数组在截取了Array.prototype上的方法,并对改变数组的7个方法,shift,unshift,pop,push,reserve,sort,splice进行改写,在这几个方法中重新添加响应式,最后重新挂载到Array原型上.
整体流程
在vue组件初始化的时候,调用Observe的definereactive函数,遍历递归data数据,给每一个属性添加getter和setter属性,在getter中调用dep.depend将watcher收集依赖,在setter中使用dep.notify去遍历wactcher数组,通知依赖更新.
缺点
1.Object.defineProperty是对 对象的属性进行劫持,所以对对象新增的属性新增和删除是无法观察到的,所以需要调用其他api完成.
2.对于数组的下标操作和length操作无法观察到,因为数组改写没有加入下标和length的方法 ,所以也有调用其他api完成响应式.
3.Object.defineProperty是es5的 所以对es6的Map,Set,Class数据类型不能观察到
4由于数组和对象实现方法不统一,所以在后期复杂项目的可维护行上可能会存在问题