Vue.js 的核心包括一套“响应式系统”。
“响应式”,是指当数据改变后,Vue 会通知到使用该数据的代码。例如,视图渲染中使用了数据,数据改变后,视图也会自动更新。
对于官网上关于响应式数据的描述,并不能让人短时间内明白其原理。下面我将按照我的理解分析一下Vue2.0响应式核心代码实现。
Vue中响应式数据分为:对象类型{}和数组类型[]
我们若想要实现响应式,需要以下类和方法:
数据data
数据监听器defineReactive
订阅者(更新视图)watcher
维护订阅者dep
状态active
实现原理: 对象内部通过defineReactive方法,使用Object.defineProperty将属性进行劫持(只会劫持已经存在的属性)。
假设页面上有容器app,data存放响应式变量,当data中的值改变时,容器内的数值也会发生变化。
watcher所执行的操作 将页面上的内容更新=>视图更新
将data中的属性依次增加get()和set()方法,这样当用户取值的时候,当作模版收集起来。待数据变化通知模版数据更新。
当定义watcher时,会依次执行(1)=>(2)=>(3)=>(4)。
每个属性都拥有自己的dep属性,存放它所存放的watcher,当属性变化后会同志自己对应的watcher去更新。
Vue2.0响应式用的是Object.definePropertyVue3.0响应式用的是proxy
当data中的数据存在多层嵌套的时候,如果用Object.defineProperty,内部会进行递归,影响性能。proxy提升性能,但是不兼容ie11。
数组考虑性能原因没有用defineProperty对数组的每一项进行拦截,而是选择对数组原型上的方法进行重写(push,pop,shift,unshift,splice,sort,reverse)只有这7种方法会重写数组
在Vue中修改数组的索引和长度是无法监控到的。需要通过以上7种变异方法修改数组才会触发数组对应的watcher进行更新。数组中如果是对象类型也会进行递归劫持。
如果想要更改索引,可以通过Vue.$set来进行处理,内部核心代码是splice方法