Vue 数据响应式

130 阅读3分钟

Vue 对 data 做了什么

当我们把一个JavaScript对象传入Vue实例作为data选项时
Vue将遍历此对象所有属性,使用Object.defineProperty转为 getter/setter

getter 和setter

get 姓名(){
  return this.姓 + this.名
}
get 姓名()是一个计算属性而不是函数,只不过是以函数形式定义的
这种写法叫getter,用于获取一个值; getter用法:在函数前加get,调用时不加()

set 姓名(xxx){
  this.姓 = xxx[0]
  this.名 = xxx.substing(1)  
}
obj3.姓名 = '郭碧婷'

setter接收一个新的值,触发set函数; 通过计算属性,设置原始属性

姓名:(...) 可以对姓名读和写,但是并不存在姓名这个属性
姓名是通过get,set来模拟对n的读写操作,getter和setter用于对属性的读写和监控

Object.defineProperty()

可以给对象添加属性 value
可以给对象添加getter和setter

var _xxx = 0
Object.defineProperty(obj, 'xxx',{
  get(){return _xxx},
  set(value){_xxx = value}
})

定义完一个对象之后,想在它身上额外的增加新的get、set
通过Object.defineProperty(),定义的get、set是虚拟的

let data2 = {}
data2._n = 0 
Object.defineProperty(data2,'n',{
  get(){
    return this._n
  },
  set(value){
    if(value < 0) return
    this._n =value
  }
})

代理

let data3 = proxy({data:{n:0}})

function proxy(options)
const {data} = options

function proxy({data})

const obj ={}
Object.defineProperty(obj, 'n',{
  get(){return data.n}
  set(value){
    if(value<0) return                    
    data.n = value
  }
})

当设置obj的n,就设置data的n;不管你对obj做什么,他都把事情对data做一遍,obj就是代理

let maData5 = {n:0}
let data5 = proxy2({data:maData5})
function proxy2({data}){
  let value = data.n 
  Object.defineProperty(data, 'n', {
    get(){return value}
    set(newValue){
      if(newValue < 0) return
      value = newValue
      }
  })
}

总结

Object.defineProperty
可以给对象添加属性value
可以给对象添加getter,setter
getter,setter用于对属性的读写进行监控

代理(设计模式)
对myData 对象属性的读写,全权由另一个对象vm负责
vm就是myData的代理
比如不用myData.n不使用,反而使用vm.n来操作myData.n

vm = new Vue({data: myData})
会让vm 成为 myData的代理(proxy)
会对myData的所有属性进行监控
防止myData的属性变了,vm不知道
vm知道之后,就可以调用render(data)
UI=render(data)

全程都在修改data:{n:0},没有生成新的{n:0},都是在{n:0}这个对象上修改的
然后得到一个被修改的对象,在此基础上产生一个代理,最后只剩代理对象和被修改的原始对象

数据响应式

options.data
会被vue监听,data会被篡改,本来的n变成get(n)set(n)
会被vue实例代理,const vm = new Vue({})   vm就是代理
每次对data的读写都会被vue监控,不管是data本身,还是它的代理都会被监控
vue会在data变化时更新UI

Vue.set和this.$set

作用:新增key
自动创建代理和监听   如果没有创建过
触发UI更新           但不会立即更新

this.$set(this.obj, 'm',100)

因为Object.defineProperty(obj, 'n', {...})
必须有一个'n'才能监听代理obj.n,如果没写,vue会给出一个警告
vue只会检查第一层属性;写了n,修改n2不会显示n2的内容,vue无法监听一开始不存在的n2

data中的数组怎么声明所有key

class VueArray extends Array{
  push(...args){
    const oldLength = this.length
    super.push(...args)
    for(let i = oldLength; i<this.length; i++){
      Vue.set(this, i, this[i])
    }
  }
}

如果调用第一层push,我就去调用我下一层的push,调用父类上的push,多加一层属性

7个变异方法

push()        新增
pop()         弹出
shift()       弹出第几个
unshift()     在第一个处新增
splice()      从中间删除某一项
sort()        对所有数据重新排序
reverse()     倒序

详细资料点击:响应式原理