Vue2.0和Vue3.0的响应式原理

vue2:

01、Object.defineProperty(obj, prop, descriptor)

参数1:obj: 要在其上定义属性的对象。

参数2:prop: 要定义或修改的属性的名称。

参数3:descriptor: 将被定义或修改的属性的描述符(包含数据描述符和存取描述符)。

数据描述符:

let obj1 = {};
Object.defineProperty(obj1, "key", {
  //该属性对应的值,默认为 undefined。
  value: 1,
  //属性的 writable 为 true 时,该属性才能被赋值运算符改变。默认为 false。
  writable: true,
  //属性的 enumerable 为 true 时,
  //该属性才能够出现在对象的枚举属性中。默认为 false。
  enumerable: true,
  //属性的configurable 为 true 时,
  //该属性描述符才能够被改变,也能够被删除。默认为 false。
  configurable: true,
});

console.log(obj1);

 

存取描述符:

let obj2 = {};
let value;
Object.defineProperty(obj2, "key", {
  // 数据描述符.....
  get: function () {
    console.log("执行了获取操作");

    return value;
  },
  set: function (newValue) {
    console.log("执行了设置操作");

    value = newValue + "真帅!!!!";
  },
});
//执行get
console.log(obj2.key);
//执行set
obj2.key = "铁蛋儿";

02.defineProperty监听数据的变化

// 监听的数据
let obj = {
    name: '铁蛋儿',
    age: 1,
    arr: ['张三', '李四', '王五'],
    job: {
        code: 'FE'
    }
}

// 封装监听数据变化的函数
function defineProperty(obj, key, val) {
    observer(val)
    Object.defineProperty(obj, key, {
        get() {
            // 读取方法
            console.log('读取', key, '成功')
            return val
        },
        set(newval) {
            // 赋值监听方法
            if (newval === val) return
            // 遍历监听数据的每一项 
            observer(newval)
            console.log('监听赋值成功', newval)
            val = newval
            // 可以执行渲染操作
        }
    })
}

function observer(obj) {
    if (typeof obj !== 'object' || obj == null) {
        return
    }
    for (const key in obj) {
        // 给对象中的每一项都设置响应式
        defineProperty(obj, key, obj[key])
    }
}

observer(obj)


// obj.name = '小白龙'

// obj.job.code = 'server'

// obj.name = {
//     sname: '欧巴'
// }
// obj.name.sname = '欧巴铁蛋儿'

obj.arr[3] = 1
// obj.arr.push([1,[2,[3]]])

03:Object.defineProperty 是对象的方法监听不到数组的变更的 Vue2.x的做法是重写数组的7个方法

// 封装监听数据变化的函数
let obj = {
    name: '铁蛋儿',
    age: 1,
    arr: ['张三', '李四', '王五'],
    job: {
        code: 'FE'
    }
}
const arrayMethods = Array.prototype;
// 先克隆一份Array的原型出来
const arrayProto = Object.create(arrayMethods);
const methodsToPatch = [
    'push',
    'pop',
    'shift',
    'unshift',
    'splice',
    'sort',
    'reverse'
]
methodsToPatch.forEach(method => {
    arrayProto[method] = function () {
        // 执行原始操作
        arrayMethods[method].apply(this, arguments)
        console.log('监听赋值成功', method)
    }
})

function defineProperty(obj, key, val) {
    observer(val)
    Object.defineProperty(obj, key, {
        get() {
            // 读取方法
            console.log('读取', key, '成功')
            return val
        },
        set(newval) {
            // 赋值监听方法
            if (newval === val) return
            // 遍历监听数据的每一项 
            observer(newval)
            console.log('监听赋值成功', newval)
            val = newval
            // 可以执行渲染操作
        }
    })
}

function observer(obj) {
    if (typeof obj !== 'object' || obj == null) {
        return
    }
    if (Array.isArray(obj)) {
        // 如果是数组, 重写原型
        obj.__proto__ = arrayProto
        // 传入的数据可能是多维度的,也需要执行响应式
        for (let i = 0; i < obj.length; i++) {
            observer(obj[i])
        }
    } else {
        for (const key in obj) {
            // 给对象中的每一个方法都设置响应式
            defineProperty(obj, key, obj[key])
        }
    }
}

observer(obj)

obj.arr.push(4)

vue3:

04、Proxy

let obj = {
    name: '铁蛋儿',
    age: 30
}
let handler = {
    get(target, key, receiver) {
        console.log('get', key)
        return Reflect.get(target, key, receiver)
    },
    set(target, key, value, receiver) {
        console.log('set', key, value)
        return Reflect.set(target, key, value, receiver)
    }
}
let proxy = new Proxy(obj, handler)
proxy.name = '小白龙' // set name 小白龙
proxy.age = 18 // set age 18

let pro = new Proxy(target, handler);

new Proxy相当于创建了一个Proxy实例,

target为所要拦截的目标对象,

handler也是一个对象, 里面定义的是对拦截对象所要进行的拦截方法。

针对对象: 针对整个对象,而不是对象的某个属性

支持数组: 不需要对数组的方法进行重载,省去了重写数组的方法

嵌套支持: get里面递归调用Proxy并返回

不需要对keys 进行遍历。这解决Object.defineProperty() 的第二个问题.Proxy 是针对整个 obj 的。

所以 obj 内部包含的所有的 key ,都可以走进 set。(省了一个 Object.keys() 的遍历)

另外 Reflect.get 和 Reflect.set 可以理解为类继承里的super,即调用原来的方法

Reflect.get():获取对象身上某个属性的值,类似于 target[name]。

Reflect.set():将值分配给属性的函数,返回一个Boolean,如果更新成功,则返回true

05.Proxy支持监控数组

let arr = [1, 2, 3]
let proxy = new Proxy(arr, {
    get(target, key, receiver) {
        console.log('get', key)
        return Reflect.get(target, key, receiver)
    },
    set(target, key, value, receiver) {
        console.log('set', key, value)
        return Reflect.set(target, key, value, receiver)
    }
})
proxy.push(4)
// 能够打印出很多内容
// get push     (寻找 proxy.push 方法)
// get length   (获取当前的 length)
// set 3 4      (设置 proxy[3] = 4)
// set length 4 (设置 proxy.length = 4)

06.Proxy嵌套的支持

// Proxy 也是不支持嵌套的, 这点和 Object.defineProperty() 是一样的。
// 因此也需要通过逐层遍历来解决。 Proxy 的写法是在 get 里面递归调用 Proxy 并返回
let obj = {
    info: {
        name: '铁蛋儿',
        blogs: ['webpack', 'gulp', 'babel']
    }
}
let handler = {
    get(target, key, receiver) {
        console.log('get', key)
        // 递归创建并返回
        if (typeof target[key] === 'object' && target[key] !== null) {
            return new Proxy(target[key], handler)
        }
        return Reflect.get(target, key, receiver)
    },
    set(target, key, value, receiver) {
        console.log('set', key, value)
        return Reflect.set(target, key, value, receiver)
    }
}
let proxy = new Proxy(obj, handler)
// 以下两句都能够进入 set
proxy.info.name = '小白龙'

07.优劣势

优势:

Proxy 的第二个参数可以有 13 种拦截方法,

比 Object.defineProperty() 要更加丰富,

Proxy 作为新标准受到浏览器厂商的重点关注和性能优化,

相比之下 Object.defineProperty() 是一个已有的老方法。

Proxy返回的是一个新对象,我们可以只操作新的对象达到目的,

而Object.defineProperty只能遍历对象属性直接修改。

劣势:

Proxy 的兼容性不如 Object.defineProperty() 可是使用 polyfill 来处理兼容性

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值