Vue2-数据响应式

本文详细解释了JavaScript中`Object.defineProperty()`在实现数据响应式中的作用,包括模拟对象属性的get/set操作,以及Vue.js中如何利用data代理、监听和Vue.set处理数组变异。通过实例展示了这些技术在实际开发中的应用和注意事项。
摘要由CSDN通过智能技术生成

1.Object.definedProperty() 

数据响应式文档:深入响应式原理 — Vue.js

Object.defineProperty文档:Object.defineProperty() - JavaScript | MDN

#get/set 模拟对象属性的读写操作
let obj = {
  姓: "高",
  名: "圆圆",
  get name() {
    return this.姓 + this.名;
  },
  set name(str){
    this.姓 = str[0]
    this.名 = str.substring(1)
  },
  age: 20
};

/* getter 使用方法是不加括号的函数 */
console.log("getter: " + obj.name);

/* setter 使用方法 properties = value,触发 set 函数 */
obj.name = '王明明'
console.log(`setter:姓 ${obj.姓},名 ${obj.名}`)

/* name(...)  obj并不存在name属性,但是可以通过 get/set 对name属性进行读写 */
console.log(obj)

#Object.defineProperty() 添加新的get/set属性
/* _id存储真实id属性值(id属性并不存在) */
var _id=0

Object.defineProperty(obj,"id",{
  get(){
    return _id
  },
  set(value){
    _id=value
  }
})

  

3.options.data 代理与监听

#直接定义属性n
let data0 = {
    n: 0
}


#用 Object.defineProperty 定义 n
let data1 = {}

Object.defineProperty(data1, 'n', {
    value: 0
})

console.log(`data1.n: ${data1.n}`)


#使用访问器属性 get/set 添加额外逻辑
/* 目标: 使得 n 不能小于 0, 即 data2.n = -1 无效;但 data2.n = 1 有效 */
let data2 = {}
/* _n 存储 n 的值 */
data2._n = 0 

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

console.log(`data2.n: ${data2.n}`)
data2.n = -1
console.log(`set data2.n: ${data2.n} ,设置为 -1 失败`)
data2.n = 1
console.log(`set data2.n: ${data2.n} ,设置为 1 成功`)


#使用代理, 用代理对象操作数据
let data3 = proxy({ data:{n:0} })
/* 为了防止 data2._n = -1 被直接修改的情况
*  传入匿名对象 { data:{n:0} } 进行数据读写,无法访问,避免被修改
*  匿名对象{} 中 data 属性存了 {n:0} 对象的地址
* */

function proxy({data}/* 解构赋值 */){
    /* 添加代理对象 obj */ 
    const obj = {}
    /* 理论上应该遍历 data 的所有 key,这里做了简化,写死只访问 'n' 这个属性 */ 
    Object.defineProperty(obj, 'n', {
        get(){
            return data.n
        },
        set(value){
            if(value<0) return
            data.n = value
        }
    })
    /* obj 就是 data3 */ 
    return obj 
} 

/* 代理对象 obj.n 的所有操作都是对 data.n 的读写 */ 
console.log(`data3.n: ${data3.n}`)
data3.n = -1
console.log(`代理 data3.n: ${data3.n},设置为 -1 失败`)
data3.n = 1
console.log(`代理 data3.n: ${data3.n},设置为 1 成功`)


#杠精: 给匿名对象命名, 通过直接更改对象名的属性值可以绕开代理
let myData = {n:0}
let data4 = proxy({ data:myData })

console.log(`杠精 data4.n: ${data4.n}`)
myData.n = -1
console.log(`杠精 data4.n: ${data4.n},设置为 -1 失败了吗!?`)


#添加监听, 拦截用户擅自修改 myData 引用绕开代理
let myData5 = {n:0}
let data5 = proxy2({ data:myData5 })

function proxy2({data}/* 解构赋值 */){
    /* 监听逻辑,监听 data */ 
    let value = data.n
    /* delete data.n 这句不用写,get/set 新添加的虚拟属性n代替 data.n */
    Object.defineProperty(data, 'n', {
        get(){
            return value
        },
        set(newValue){
            if(newValue<0) return
            value = newValue
        }
    })

    /* 代理逻辑,添加代理 obj */
    const obj = {}
    Object.defineProperty(obj, 'n', {
        get(){
            return data.n
        },
        set(value){
            data.n = value
        }
    })

    return obj // obj 就是代理对象
}

console.log(`data5.n: ${data5.n}`)
myData5.n = -1
console.log(`监听代理 data5.n: ${data5.n},设置为 -1 失败了`)
myData5.n = 1
console.log(`监听代理 data5.n: ${data5.n},设置为 1 成功了`)


# Vue 就是用类似的逻辑对 options.data 所有数据进行监听,再渲染:
# let data5 = proxy2({ data:myData5 })
# let vm = new Vue({data: myData})

3.Vue.set (this.$set) 与数组变异方法

#报错:n is not defined on the instance but referenced during render
new Vue({
    data: {},
    template: `
    <div>{{n}}</div>
  `
}).$mount("#app");


#绕开vue警告,存在第一层对象但操作不存在的嵌套数据,视图不更新(嵌套的数据不会自动变为响应式)
new Vue({
    data: {
        obj: {
            a: 0, // obj.a 会被 Vue 监听 & 代理
            /* [方法一: 提前声明key] b: undefined */
        }
    },
    template: `
    <div>
      {{obj.b}}
      <button @click="setB">set b</button>
      <button @click="add">+10</button>
    </div>
  `,
    methods: {
        setB() {
            this.obj.b = 1; // 页面中不会显示 1
            /* [方法二] 使用 Vue.set/this.$set 新增 key */
            /*
            * Vue.set(this.obj,'b',1)
            * this.$set(this.obj,'b',5)
            * console.log(Vue.set === this.$set) true
            *  */
        },
        add(){
            return this.obj.b += 10
        }
    }
}).$mount("#app");

数组变异方法文档:列表渲染 — Vue.js

#数组的更新检测,Vue数组变异方法mutator
#Vue无法检测直接索引修改数组元素 or 使用 length 属性修改数组长度的变化
new Vue({
    data: {
        array: ["a", "b", "c"]
    },
    template: `
    <div>
      {{array}}
      <button @click="setD">set d</button>
    </div>
  `,
    methods: {
        setD() {
            this.array[3] = "d"; // 页面中不会显示 'd'

            /* 添加单个数组元素 */
            /* this.$set(this.array,3,'d') */ 

            /* [Vue.Array.mutator()数组变异方法] 添加单个或多个数组元素 */ 
            /* 
            * this.array.push('d','e','f')
            * console.dir(this.array)
            * */
        }
    }
}).$mount("#app");

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值