阅读 193

详细分析Vue的数据响应式

详细分析Vue的数据响应式

响应响应,就是我叫你一声你敢不敢应?如果你回应了。那么这个过程就叫响应式了。

1.jpg

Vue的响应式原理主要通过三个属性来实现:

  • get
  • set
  • Object.defineProperty

我们注意来对他们进行讲解。

get

get xxx(){} 是一个用来得到当前属性值的回调函数

它的精髓在于读取对象属性值。

举例说明:

let obj = {
  姓: "孙",
  名: "悟空",
  age: 18
};
复制代码

以上我们得到了一个obj对象,如果我们想得到它的姓名,一般我们会这样做:

let obj1 = {
  姓: "孙",
  名: "悟空",
  姓名() {
    return this.姓 + this.名;
  },
  age: 18
};

console.log("俺叫:" + obj1.姓名());// 俺叫:孙悟空
复制代码

由于 姓名()是个函数,所以我们不能直接去掉它的 ( ),所以我们使用get来实现。

let obj2 = {
  姓: "孙",
  名: "悟空",
  get 姓名() {
    return this.姓 + this.名;
  },
  age: 18
};

console.log("需求二:" + obj2.姓名);

复制代码

总结:get 就是这样用的。使得函数不用加括号,你暂时可以这么理解。

set

set xxx(){} 主要用来监视当前对象属性值变化,并提供修改方法。

set的精髓就是修改属性值。

同样用代码说明:

// 需求:使得姓名可以被修改

let obj3 = {
  姓: "孙",
  名: "悟空",
  get 姓名() {
    return this.姓 + this.名;
  },
  set 姓名(xxx){
    this.姓 = xxx[0]
    this.名 = xxx.slice(1)
  },
  age: 18
};

obj3.姓名 = '猪八戒'

console.log(`需求三:姓 ${obj3.姓},名 ${obj3.名}`)
//结果为: 需求三:姓 猪,名 八戒
复制代码

总结:set 就是这样用的。用 = xxx 触发 set 函数,会改变属性值。

Object.defineProperty

Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。

如果你定义完了一个对象,希望给他额外加属性,那么可以用这个来加。

语法:Object.defineProperty(obj, prop, descriptor)

obj
复制代码

要定义属性的对象。

prop
复制代码

要定义或修改的属性的名称或Symbal

descriptor
复制代码

要定义或修改的属性描述符。注意,他最终会变成一个值并返回。

这就是Object.defineProperty()原理,我们现在用代码,通过set和get结合的方式进行理解:

//需求:在声明完一个空对象后,尝试增加它的属性。我们要求里面的值(value)必须大于0
let daddy= {}

daddy.x= 0 //用x来存储'n'的值

Object.defineProperty(daddy, 'n', {
  get(){
    return this.x
  },
  set(value){
    if(value < 0) return //一旦value<0,data就输出默认值
    this.x = value
  } //这里set和get相当于为daddy的属性n进行了监控
})

daddy.n = -1 
console.log(`${daddy.n} `)
//这里daddy.n返回的值是0,由于n没有满足条件,所以直接返回get所得到的默认值

daddy.n = 1
console.log(`${daddy.n}`)
//这里daddy.n返回的值是1,由于n满足条件,set成功执行。
复制代码

总结 Object.defineProperty

  • 可以给对象添加属性value
  • 可以给对象添加getter / setter
  • getter / setter 用于对属性的读写进行监控

代理与监听

代理

直接上代码吧~

let Data= {}
data.x= 0
Object.defineProperty(data, 'n', {
  get(){
    return this.x
  },
  set(value){
    if(value < 0) return
    this.x = value
  }
})
复制代码

这是上一小节的代码,其实它是有问题的。因为如果有人将data.x的赋值直接改掉,比如重新赋值 data.x = -1 ,那么 -1 这个值变成默认值进入Object.defineProperty后,set的监控就失效了。我们设置的value>0 的需求没有被实现。

我们需要找个中介作为 data 这个对象的代理,然后使 data.x 变成一个匿名函数,这让它的初始值就无法访问了。

let Data = proxy({ data:{n:0} }) // 括号里是匿名对象,无法访问。此时,data.x作为储存'n'的值的意义消失了。我们不用再想它了。

function proxy({data}){
  const obj = {} // 我们请个代理人大哥过来,现在{}里面变成了data
  Object.defineProperty(obj, 'n', { 
    get(){
      return data.n
    },
    set(value){
      if(value<0)return
      data.n = value
    }
  })
  return obj // 这里的obj就是Data的代理对象
}
复制代码

​ 这回总没办法改了吧? 我们把data锁定了,并通过代理的形式使代码顺利运行。不过人生总是充满惊喜。

​ 假如因为某种需要,代码变成以下模式:

let myData = {n:0}
let Data = proxy({ data:myData })
复制代码

​ 既然data变成匿名函数无法访问,那么把数据:{n:0} 调出来赋值给对对象myData,只要篡改myData,还是可以使set失效。

​ 为了防止这种情况,我们动用绝招,形成一个怎么也无法被破解的结构。这时候就需要用到监听了。

监听

上代码!

let myData = {n:0}
let Data = proxy({ data:myData}) 

function proxy2({data}){
  let value = data.n
  Object.defineProperty(data, 'n', {
    get(){
      return value
    },
    set(newValue){
      if(newValue<0)return
      value = newValue
    }
  })//这里使新加的代码,无论后面怎么被篡改,都要经过它的监听,只要不合格就会被恢复成原始的 n:0 。


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

这里加入了监听代码后,只要进入这段监听代码,都会被get、set监听恢复。

new Vue( )

让我们看看vue是怎么实现的:

3.png

如上图,vm就是data的代理人。

假设data被篡改,这个new Vue就会开始进行监听了。

其监听代理过程如下:

  1. 为data创建监听,并且将n的值用value获取,先寄放在它这里。
  2. 获取后,会由Object.definePropertydata创建一个新的 'n'属性(此处,原来的n属性被干掉了,这里的新'n'代替的原来的n)。
  3. get(读取)此属性,将value(即原先n的值)返回。
  4. set (设置)一个新值V,即后面可能被重新赋值的n(比如后面有人把data重新赋值了)。此时判断这个值合不合规。(判断方法可能是将 V 与之前get到的value进行对比)
  5. 如果合规就将其赋值给value,并将它丢给代理,通过代理赋值给vm
  6. 不合规则调用render(data) ,对UI页面进行重新渲染,UI中的n也会重新渲染。

再拿代码举例

const myData = {
  n: 0
}
new Vue({
  data: myData,
  template: `
    <div>{{n}}</div>
  `
}).$mount("#app");

setTimeout(()=>{
  myData.n += 10
},3000)
复制代码

new Vue()的作用与前面三要素的组成作用是一样的。

这里的myData,无论后面怎么被篡改,只要进入了new Vue,都会被get、set监听恢复。vm 起到的作用就是成为 myData 的代理的角色,对其属性进行代理监控,这个过程 Object.defineProperty 全程参与。

文章分类
前端
文章标签