Vue数据响应式-vue对data做了什么

对应Vue文档深入响应式原理。

ES6 getter和setter

  • gettersetter就是不加括号的函数

举个栗子一,主要打印出log1

let obj1 = {
    姓:“金”,
    名:“泰熙”,
    姓名(){
      return this.姓 + this.名;
    }
    age:18
};
console.log("需求一:" + obj1.姓名())  // 需求一:金泰熙

尝试去掉姓名()里面的小括号:

let obj2 = {
    姓:“金”,
    名:“泰熙”,
    get 姓名(){   // getter 就是不加括号的函数
      return this.姓 + this.名;
    }
    age:18
};
console.log("需求二:" + obj2.姓名)  // 需求二:金泰熙
  • 尝试修改属性:
let obj3 = {
    姓:“金”,
    名:“泰熙”,
    get 姓名(){   // get 就是不加括号的函数
      return this.姓 + this.名;
    },
    set 姓名(xxx){
        this.姓 = xxx[0]
        this.名 = xxx.substring(1)
    },
    age:18
};
obj3.姓名 = “刘诗诗”  // 触发了setter函数
console.log(`需求三:姓 $(obj3.姓), 名$(obj3.名`)  // 需求三:姓 刘 名诗诗
  • log1输出结果:

image.png

举个栗子二,主要打印出log2log3

const myData = {
    n:0
}
console.log(myData)  // log2

const vm = new Vue({
    data:myData,
    template:`
      <div>{{n}}<button @click="add">+10</button></div>
    `,
    methods:{
        add(){
          this.n += 10
        }
    }
}).$mount("#app");

setTimeout(()=>{
    myData.n += 10
    console.log(myData) // log3
}, 3000)
  • log2输出结果:

image.png

  • log3输出结果:

image.png

小结:

  • 栗子一结论:输出的姓名:(...)。可以通过get 姓名set 姓名属性和属性进行读和写,但是并不存在一个叫做姓名的一个属性;
  • 栗子二结论:一开始是{n:0},传给new Vue之后立马变成{n:(...)},并不存在一个叫做n的属性,而是通过get nset n来模拟对n的某些操作.

疑问?为什么要变成get nset n呢?

Object.defineProperty

作用

  1. 可以给对象添加属性 value
  2. 可以给对象添加 getter/setter,用于对属性的读写进行监控
  • 在定义完对象的属性之后,可通过Object.defineProperty额外添加新的属性,再举个栗子:
let obj3 = {
    姓:“金”,
    名:“泰熙”,
    get 姓名(){   // get 就是不加括号的函数
      return this.姓 + this.名;
    },
    set 姓名(xxx){
        this.姓 = xxx[0]
        this.名 = xxx.substring(1)
    },
    age:18
};

var _xxx = 0  // 存放xxx的值
// 第一个参数是要define哪个对象,第二个参数是要define的属性或方法名
Object.defineProperty(obj3, 'xxx', {
    get(){
      return _xxx
    },
    set(value){
      _xxx = value
    }
})
obj3.姓名 = “刘诗诗”  // 触发了setter函数
console.log(`需求三:姓 $(obj3.姓), 名$(obj3.名`)  // 需求三:姓 刘 名诗诗

代理(设计模式)和监听

  • 需求一:用 Object.defineProperty 定义 n:
let data0 = { n:0 }
let data1 = {}
Object.defineProperty(data2, 'n', {
    get(){ return this._n }
    set(value){
      if(value < 0) return
      this._n = value
    }
})
console.log(`需求一:${data1.n}`) // 需求一:0
  • 需求二:n 不能小于0
let data2 = {}
data2._n = 0 // _n 用来存储 n 的值
Object.defineProperty(data1, 'n', {value:0})
console.log(`需求二:${data2.n}`) // 需求二:0

data2.n = -1
console.log(`需求二:${data2.n} 设置 -1 失败`) // 需求二:0 设置 -1 失败

data2.n = 1
console.log(`需求二:${data2.n} 设置 1 成功`) // 需求二:1 设置 1 成功
  • 需求三:使用代理,不让别人访问 _n(房屋中介租房代理)
// 小括号里面的是匿名对象{n:0},无法访问和修改
let data3 = proxy({ data:{n:0} }) 

function proxy(options){  // options 可以替换为析构赋值 {data}
    const {data} = options;
    const obj = {}
    Object.defineProperty(obj, 'n', {
      // 对 obj.n 做什么就同时对 data.n 做什么
      get(){ return data.n },
      set(value){
        if(value<0)return
        data.n = value
      }
    })
    return obj // obj 就是代理
}
// data3 就是 obj
console.log(`需求三:${data3.n}`) // 需求三:0

data3.n = -1
console.log(`需求三:${data3.n} 设置 -1 失败`) // 需求三:0 设置 -1 失败

data3.n = 1
console.log(`需求三:${data3.n} 设置 1 成功`) // 需求三:1 设置 1 成功
  • 需求四:绕过代理,定义变量myData引用 n
let myData = {n:0}
let data4 = proxy({ data:myData })
console.log(`'杠精':${data4.n}`) // 杠精:0
myData.n = -1
console.log(`'杠精':${data4.n}, 设置-1 失败了吗`) // 杠精:-1, 设置-1 失败了吗
  • 需求五:就算用户擅自修改 myData, 也要拦截他
let myData5 = {n:0}
let data5 = proxy2({ data:myData5 })
function proxy2({data}){
    // 以下为监听逻辑
    let value = data.n
    Object.defineProperty(data, 'n', {
      // 对 obj.n 做什么就同时对 data.n 做什么
      get(){ return value },
      set(newValue){
        if(newValue<0)return
        value = newValue
      }
    })
    
    const obj = {}
    Object.defineProperty(obj, 'n', {
      get(){ return data.n },
      set(value){
        if(value<0)return
        data.n = value
      }
    })
    return obj //obj 就是代理
}
console.log(`需求五:${data5.n}`) // 需求五:0

data5.n = -1
console.log(`需求五:${data5.n} 设置 -1 失败`) // 需求五:0 设置 -1 失败

data5.n = 1
console.log(`需求五:${data5.n} 设置 1 成功`) // 需求五:1 设置 1 成功

小结:需求五

let data5 = proxy2({ data:myData5 }) // 相当于下行代码
const vm = new Vue({ data:{n:0} })

vue 对 data 做了什么

const vm = new Vue({ data:myData })

上行代码做了以下两件事:

  • 会让vm成为myData的代理 proxy,可以通过this访问到vm
  • 会对myData的所有属性进行监控

监控的原因是为了防止myData的属性变了,vm不知道;

只有vm知道属性变了就可以调用render(data)去渲染UI

Vue的data是响应式

  • 修改vm.n,那么UI中的n就会响应

  • Vue 2 通过Object.defineProperty 来实现数据响应式

  • Vue 3 使用Proxy代替Object.defineProperty 来实现数据响应式

Object.defineProperty的bug:

  • 对象中新增的key,Vue没有办法事先监听和代理
    解决办法:使用Vue.setthis.$set来新增key,且创建监听和代理,更新UI
  • 数组中新增的key,对象可以提前把属性都写出来,数组做不到不新增key
    解决办法:也用Vue.setthis.$set来新增key,且创建监听和代理,更新UI;vue作者篡改了数组的7个API自动处理监听和代理。

Vue.setthis.$set

  • 作用:新增key、自动创建代理和监听(如果没有创建过)、触发UI更新(但不会立刻更新)
  • 例子:
this.$set(this.object, 'm', 100)
© 版权声明
THE END
喜欢就支持一下吧
点赞8 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容