数据响应式

1,114 阅读2分钟

options.data(Vue文档:深入响应式原理)

getter 和 setter

    let obj1 = {
        姓: '陈',
        名: '子川',
         get 姓名(){
            retrun this.姓 + this.名;
        },
        age: 18
    }
    console.log("姓名:" + obj1.姓名())
    
    let obj2 = {
        姓: '陈'
        名: '子川'
        get 姓名(){
            retrun this.姓 + this.名;
        },
        age: 18
    }
    console.log("姓名:" + obj2.姓名)
    
    let obj3 = {
        姓: '陈'
        名: '子川'
        get 姓名(){
            retrun this.姓 + this.名;
        },
        set 姓名(name){
            this.姓 = name[0]
            this.名 = name.substring[1]
        },
        age: 18
    }
    obj3.姓名("陈圆圆")
    console.log("姓 ${obj3.姓}, 名 ${obj3.名}}")
    

Object.defineProperty

声明完成实例后还想加入函数

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({data} /* 解构赋值 */){
    const obj{}
    // 这里的 'n' 写死了, 理论上应该遍历 data 的所有 key, 这里做了简化
    Object.defineProperty(obj, 'n', {
        get(){
            return data.n
        },
        set(value){
            if(value)return
            data.n = value
        }
    })
    return obj   // obj 就是代理
}

使用监听拦截

let myData4 = {n:0}
let data4 = proxy2({data:myData4})

function proxy2({data}){
    let value = data.n
        Object.defineProperty(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
}

小结

Object.defineProperty

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

啥是代理(设计模式)

  • 对 myData 对象的属性读写, 全权由另一个对象 vm 负责
  • n那么 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)

Vue data 有 BUG

Object.defineProperty 的问题

  • Object.defineProperty(obj, 'n', {...})
  • 必须要有一个 'n', 才能监听 & 代理 obj.n
  • 如果没有给 n 怎么办

解决办法

  • 方法一: 把 key 都声明好, 后面不再加属性
  • 方法二: 使用 Vue.set 或者 this.$set
Vue.config.productionTip = false;

new Vue({
    data: {
        obj: {
            a: 0,
        }
    },
    template: `
        <div>
            {{obj.b}}
            <button @click="setB">set b</button>
        </div>
    `,
    methods: {
        setB() {
            // this.obj.b = 1;
            Vue.set(this.obj, 'b', 1)
            this.$set(this.obj, 'b', 1)
        }    
    }
})

Vue.set 和 this.$set

作用

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

举例

  • this.$set(this.object, 'm', 100)

对象中新增的 key

  • Vue 没有办法事先监听和代理
  • 要使用 set 来新增 key, 创建监听和代理, 更新 UI
  • 最好提前把属性都写出来, 不要新增 key
  • 但是数组做不到 不新增 key

数组中新增的 key

  • 也可用 set 来新增 key, 更新 UI
  • 不过 Vue 篡改了7个 API 方便对数组进行增删
  • 这7个API会自动处理监听和代理, 并更新 UI