Vue的数据响应式原理

338 阅读3分钟

Vue的响应式原理是一个老生长谈的问题了。很多人至今都搞不懂响应式原理,接下来我将用例子一步步带大家了解Vue的响应式原理。

首先我们需要用到getter/setter的设计模式,理由是这种模式,能够不加括号将函数调用,一旦外界改动值,可以自动获取变化后的属性

let obj1 = {
	姓:"张",
    名:"三",
    age:18,
    get 姓名(){
    	return this.姓 + this.名
    },
    set 姓名(xxx){
    	this.姓 = xxx[0]
        this.名 = xxx.substring(1)
    }
}

Vue的响应应式是由Object.defineProperty这个API实现的。下面了解一下这个API Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。

Object.defineProperty(obj, prop, descriptor)

  • obj
    • 要定义属性的对象。
  • prop
    • 要定义或修改的属性的名称或 Symbol 。
  • descriptor
    • 要定义或修改的属性描述符。

Vue 将遍历此对象所有的property,并使用 Object.defineProperty 把这些 property 全部转为 getter/setter。下面用最简例子讲解原理。

//使用代理
let data2 = proxy({data:{n:0}})//括号里面为匿名对象,无法访问
function proxy({data}){
	//const {data} = options    //解构赋值
    const obj = {} //此obj成为代理,充当中间人,取值,改值都是通过obj对data执行的,data本身是无法自己修改的,因为它没有名称
    Object.defineProperty(obj,'n',{
    	get(){
        	return data.n
        },
        set(newVale){
        	data.n = newVale
        }
    })
    return obj //obj是代理,声名的data2也就是代理 
}

上面的代码有一个bug,就是如果它用变量引用对象,绕过了对象,就很头疼了,又可以私自的查改对象里的键值对了,见下面例子

let myData = {n:0} //没想到吧,我弄个引用,将对象可以暴露出来可以肆意改动了,然而Vue却不知道
let data3 =proxy({data:myData}) //感觉似曾相识呢!
let vm = new Vue({data:myData}) //芜湖!!!,和上式结构上有点像啊!
//我们接着往下看 到底是个什么情况

proxy函数要怎么解决问题呢???

function proxy({data}){
	//这里只写一个属性,实际上要遍历每个属性,所有的值都监听起来,这里方便讲解,故简单化
	let value = data.n //将原来的值监听起来
    Object.defineProperty(data,'n',{ // 这边重新装一个n属性
    	get(){
        	return value
        },
        set(newValue){
        	//小知识,改值还可以设置条件的
        	value = newValue
        }
    })
    //就加了上面的这几句话,就会监听data
    
    const obj = {}
    Object.defineProperty(obj,'n',{
    	get(){
        	return data.n
        },
        set(newValue){
      		data.n = newValue 
        }
    })
    return obj
}

由此我们可以知道vm就是那个代理.

那为什么要监控myData的所有属性???

答:为了防止myData的属性变了,vm不晓得嘛

vm知道了又能咋的嘛???

答:知道了属性变了就可以调用render(data)呀!,UI就会发生改变了,芜湖!!!

PS:同理,Vue对methods,computed也有处理,若data有多个属性 n , m , k,那就有get/set n , get/set m ,get/set k,


Vue的响应式的bug

这是Object.defineProperty的问题,Object.defineProperty(obj,'n',{...}),必须有个'n',才能监听&代理 obj.n

若是没有给'n',那此时Vue会给出一个警告,Vue只会检查第一层属性

解决之法:

  • 把key都声明好,后面不再加属性不就行了嘛
  • 使用 Vue.set 或者 this.$set

Vue.set 和 this.$set 作用:

  • 新增key
  • 自动创建代理和监听(如果没有创建过)
  • 触发UI更新(但不会立刻更新)

data中有数组咋办???

没有办法提前声明所有的key,数组的长度可以一直增加,下标就是key;没办法把数组的key都声明出来,Vue也不能检测对你新增的下标,难道每次改数组都得Vue.setthis.$set

尤大的做法: 他改写了数组的api,见变更方法章节;这7个API被Vue改写了,调用会触发视图更新.

  • push()
  • pop()
  • shift()
  • unshift()
  • splice()
  • sort()
  • reverse()