Vue2数据双向绑定是通过采用数据劫持结合发布者-订阅者模式的方式来实现的。通过Object.defineProperty()来劫持各个属性的setter,getter。修改触发set方法赋值,获取触发get方法取值,在数据变动时发布消息给订阅者,触发相应的回调并通过数据劫持发布信息。
但是 Object.defineProperty() 只能对属性进行数据劫持,不能对整个对象进行劫持,同理无法对数组进行劫持。Vue 框架是通过遍历数组和递归遍历对象,从而达到利用 Object.defineProperty() 也能对对象和数组(部分方法的操作)进行监听。
Vue的数组响应式是如何实现的?它是以Array.prototype为原型,创建了一个arrayMethods对象,使用Object.setPrototypeOf()强制让数组指向arrayMethods,这样就可以触发我们在arrayMethods中的改写的数组操作方法。
缺陷:a、无法检测到对象属性的新增或删除。Vue2采用Vue.$set和Vue.$delete.
b、不能监听数组的变化。vue在实现数组的响应式时,它使用了一些hack,把无法监听数组的情况通过重写数组的部分方法来实现响应式,这也只限制在数组的push/pop/shift/unshift/splice/sort/reverse七个方法,其他数组方法及数组的使用则无法检测到。如直接修改值和获取数组长度。
Proxy,字面意思是代理,是ES6提供的一个新的API,用于修改某些操作的默认行为,可以理解为在目标对象之前做一层拦截,外部所有的访问都必须通过这层拦截,通过这层拦截可以做很多事情,比如对数据进行过滤、修改或者收集信息之类。
ES6原生提供的Proxy构造函数,用法如下:
var proxy =new Proxy(obj, handler)
其中obj为Proxy要拦截的对象,handler用来定制拦截的操作,返回一个新的代理对象proxy;
vue3中利用Proxy实现数据读取和设置时进行拦截,在拦截trap中实现数据的依赖收集以及触发视图更新操作。
1)diff算法优化
diff算法是虚拟DOM技术的必然产物,它会对新旧虚拟DOM作对比(即diff),然后将变化的地方更新在真实DOM上。
在Vue2.0当中,当数据发生变化,它就会新生成一个DOM树,并和之前的DOM树进行比较,找到不同的节点然后更新。但这比较的过程是全量的比较,也就是每个节点都会彼此比较。但其中很显然的是,有些节点中的内容是不会发生改变的,那我们对其进行比较就肯定消耗了时间。
所以在Vue3.0当中,就对这部分内容进行了优化:在创建虚拟DOM树的时候,会根据DOM中的内容会不会发生变化,添加一个静态标记。那么之后在与上次虚拟节点进行对比的时候,就只会对比这些带有静态标记(patch flag)的节点。
patchFlag的类型:
2)hoistStatic 静态提升
vue2中无论元素是否参与更新,每次都会重新创建,然后再渲染;
vue3对于不参与更新的元素,会做静态提升,只会被创建一次,在渲染时直接复用即可。
3)cacheHandlers 事件侦听器缓存
默认情况下onClick会被视为动态绑定,所以每次都会去追踪它的变化。
但是因为是同一个函数,所以没有追踪变化,直接缓存起来复用即可