概要:
Vue实例化对象挂载到根元素后,生成全局Vue对象实例,Vue对象在实例化过程中会传入配置对象(options),options中包括data、methods、computed、watch等等。至于Vue是如何对data中数据的变化进行监测的,原理大致如下:
一、普通数据监测原理及步骤
1. data对象加工成_data对象,并存入Vue实例
data对象的加工处理过程:递归为每一个非对象类型的数据添加响应式的get、set方法(reactive getter、reactive setter)(数据劫持:指的是在访问或者修改对象的某个属性时,通过一段代码拦截这个行为,进行额外的操作或者修改返回结果。vue中将data经过加工处理变为_data,data中含有getter、setter的这一过程就是数据劫持。)
非对象类型:
data:{ key1:value1, //key1为非对象类型数据 key2:{ //key2为对象类型数据 key3:value3, //key3为非对象类型数据 key4:{ //key4为对象类型数据 key5: value5, //key5为非对象类型数据 }, } }
如何添加 reactiveGetter() (reactive响应式) 和reactiveSetter(newVlaue): 使用数据代理(所谓数据代理,就是通过一个对象代理对另一个对象中属性的操作【读/写】。数据代理是使用原生JS的Object.defineProperty()方法实现的。)
下面这段代码只是一个简单的vue的响应式getter和setter的实现原理,这段代码只能实现单层属性的响应式getter和setter,一但data中存在上面一段中的嵌套结构,则不能为第二层、第三层等属性添加getter()和setter()。在实际的vue中要比这段代码更为复杂,vue在底层中写了一个递归,data有多少层,vue就会向下找多少层,直到最后是非对象类型为止。
let data = { key1:value1, key2:value2, } // 创建一个观察者实例对象,用于监测data中属性的变化 const obs = new Observer(data) //准备一个实例对象vm const vm = {} vm._data = data = obs function Observer(obj){ // 汇总obj中所有属性,形成一个数组keys const keys = Object.keys(obj) // 遍历keys数组(此处为简化示例,未体现属性是一个对象或一个数组进行递归的情况) keys.forEach((key)=>{ //这里的 this 指向的是new的实例对象obs,给Observer的实例对象添加属性 Object.defineProperty(this,key,{ get(){ return obj[key] }, set(value){ obj[key] = value } }) }) }
2. vue全局实例对象代理_data对象,实现对_data中属性的直接操作
对data加工生成_data对象,并存入vue实例对象后,vue实例对象会通过数据代理的方式,对_data对象进行代理,为每个对象设置getter和setter,从而实现通过this.key1 = new_value1
以及 const value = this.key1
的形式,直接对_data中的key1进行修改和读取。
3. 数据变化监测效果
每个具有reactiveSetter()的数据发生变化时,都会调用这个reactiveSetter(),而这个reactiveSetter() 被调用时,会触发重新解析模板、生成新的虚拟DOM,新旧虚拟DOM对比,更新内容映射到真实DOM,重新渲染这一套流程。
二、动态新增可被监测的属性 Vue.set()或this.$set()
由于JavaScript的限制,Vue无法检测到后续在data的数组和对象中添加新的属性的变化,因此也不会触发视图更新。
1.对于对象
Vue 无法监测data中不存在的属性的添加或移除。由于 Vue 会在初始化实例时对 data的属性执行 getter/setter 转化,所以属性必须在 data 对象上存在才能让 Vue 将它转换为响应式的。
var vm = new Vue({
data:{
a:1
}
})
vm.a=2
v.a // 是响应式的
vm.b = 2
vm.b // 是非响应式的,vue监测不到
使用方法: Vue.set(target,key,value) 或 this.$set(target,key,value)
注意:Vue不允许动态添加根级响应式属性。【 Vue.set()只能给data中的某一对象追加属性,而不能给data追加属性。】
2.对于数组
Vue不会为数组元素添加响应式的getter和setter,(即使data中原来存在的数组也不会为其添加getter和setter),所以通过数组的索引值更改数组数据无法被Vue监测到。
(1) 只有调用了数组对应的7个更改数组的方法:
push()
、pop()
、shift()
、unshift()
、splice()
、reverse()
、sort()
这7个改变原数组本身的API,才会引起Vue的响应。(2) Vue.set(target,key,value) 或 this.$set(target,key,value)