面试官:Vue3.0里为什么要用 Proxy API 替代 defineProperty API ?

news2024/10/2 14:22:17

 🎬 岸边的风:个人主页

 🔥 个人专栏 :《 VUE 》 《 javaScript 》

⛺️ 生活的理想,就是为了理想的生活 !

在这里插入图片描述

 

目录

一、Object.defineProperty

为什么能实现响应式

小结

二、proxy

三、总结


一、Object.defineProperty

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

为什么能实现响应式

通过defineProperty 两个属性,getset

  • get

属性的 getter 函数,当访问该属性时,会调用此函数。执行时不传入任何参数,但是会传入 this 对象(由于继承关系,这里的this并不一定是定义该属性的对象)。该函数的返回值会被用作属性的值

  • set

属性的 setter 函数,当属性值被修改时,会调用此函数。该方法接受一个参数(也就是被赋予的新值),会传入赋值时的 this 对象。默认为 undefined

下面通过代码展示:

定义一个响应式函数defineReactive

function update() {
    app.innerText = obj.foo
}

function defineReactive(obj, key, val) {
    Object.defineProperty(obj, key, {
        get() {
            console.log(`get ${key}:${val}`);
            return val
        },
        set(newVal) {
            if (newVal !== val) {
                val = newVal
                update()
            }
        }
    })
}

调用defineReactive,数据发生变化触发update方法,实现数据响应式

const obj = {}
defineReactive(obj, 'foo', '')
setTimeout(()=>{
    obj.foo = new Date().toLocaleTimeString()
},1000)

在对象存在多个key情况下,需要进行遍历

function observe(obj) {
    if (typeof obj !== 'object' || obj == null) {
        return
    }
    Object.keys(obj).forEach(key => {
        defineReactive(obj, key, obj[key])
    })
}

如果存在嵌套对象的情况,还需要在defineReactive中进行递归

function defineReactive(obj, key, val) {
    observe(val)
    Object.defineProperty(obj, key, {
        get() {
            console.log(`get ${key}:${val}`);
            return val
        },
        set(newVal) {
            if (newVal !== val) {
                val = newVal
                update()
            }
        }
    })
}

当给key赋值为对象的时候,还需要在set属性中进行递归

set(newVal) {
    if (newVal !== val) {
        observe(newVal) // 新值是对象的情况
        notifyUpdate()
    }
}

上述例子能够实现对一个对象的基本响应式,但仍然存在诸多问题

现在对一个对象进行删除与添加属性操作,无法劫持到

const obj = {
    foo: "foo",
    bar: "bar"
}
observe(obj)
delete obj.foo // no ok
obj.jar = 'xxx' // no ok

当我们对一个数组进行监听的时候,并不那么好使了

const arrData = [1,2,3,4,5];
arrData.forEach((val,index)=>{
    defineProperty(arrData,index,val)
})
arrData.push() // no ok
arrData.pop()  // no ok
arrDate[0] = 99 // ok

可以看到数据的api无法劫持到,从而无法实现数据响应式,

所以在Vue2中,增加了setdelete API,并且对数组api方法进行一个重写

还有一个问题则是,如果存在深层的嵌套对象关系,需要深层的进行监听,造成了性能的极大问题

小结

  • 检测不到对象属性的添加和删除
  • 数组API方法无法监听到
  • 需要对每个属性进行遍历监听,如果嵌套对象,需要深层监听,造成性能问题

二、proxy

Proxy的监听是针对一个对象的,那么对这个对象的所有操作会进入监听操作,这就完全可以代理所有属性了

ES6系列中,我们详细讲解过Proxy的使用,就不再述说了

下面通过代码进行展示:

定义一个响应式方法reactive

function reactive(obj) {
    if (typeof obj !== 'object' && obj != null) {
        return obj
    }
    // Proxy相当于在对象外层加拦截
    const observed = new Proxy(obj, {
        get(target, key, receiver) {
            const res = Reflect.get(target, key, receiver)
            console.log(`获取${key}:${res}`)
            return res
        },
        set(target, key, value, receiver) {
            const res = Reflect.set(target, key, value, receiver)
            console.log(`设置${key}:${value}`)
            return res
        },
        deleteProperty(target, key) {
            const res = Reflect.deleteProperty(target, key)
            console.log(`删除${key}:${res}`)
            return res
        }
    })
    return observed
}

测试一下简单数据的操作,发现都能劫持

const state = reactive({
    foo: 'foo'
})
// 1.获取
state.foo // ok
// 2.设置已存在属性
state.foo = 'fooooooo' // ok
// 3.设置不存在属性
state.dong = 'dong' // ok
// 4.删除属性
delete state.dong // ok

再测试嵌套对象情况,这时候发现就不那么 OK 了

const state = reactive({
    bar: { a: 1 }
})

// 设置嵌套对象属性
state.bar.a = 10 // no ok

如果要解决,需要在get之上再进行一层代理

function reactive(obj) {
    if (typeof obj !== 'object' && obj != null) {
        return obj
    }
    // Proxy相当于在对象外层加拦截
    const observed = new Proxy(obj, {
        get(target, key, receiver) {
            const res = Reflect.get(target, key, receiver)
            console.log(`获取${key}:${res}`)
            return isObject(res) ? reactive(res) : res
        },
    return observed
}

三、总结

Object.defineProperty只能遍历对象属性进行劫持

function observe(obj) {
    if (typeof obj !== 'object' || obj == null) {
        return
    }
    Object.keys(obj).forEach(key => {
        defineReactive(obj, key, obj[key])
    })
}
Proxy直接可以劫持整个对象,并返回一个新对象,我们可以只操作新的对象达到响应式目的

function reactive(obj) {
    if (typeof obj !== 'object' && obj != null) {
        return obj
    }
    // Proxy相当于在对象外层加拦截
    const observed = new Proxy(obj, {
        get(target, key, receiver) {
            const res = Reflect.get(target, key, receiver)
            console.log(`获取${key}:${res}`)
            return res
        },
        set(target, key, value, receiver) {
            const res = Reflect.set(target, key, value, receiver)
            console.log(`设置${key}:${value}`)
            return res
        },
        deleteProperty(target, key) {
            const res = Reflect.deleteProperty(target, key)
            console.log(`删除${key}:${res}`)
            return res
        }
    })
    return observed
}
 

Proxy可以直接监听数组的变化(pushshiftsplice

const obj = [1,2,3]
const proxtObj = reactive(obj)
obj.psuh(4) // ok

Proxy有多达13种拦截方法,不限于applyownKeysdeletePropertyhas等等,这是Object.defineProperty不具备的

正因为defineProperty自身的缺陷,导致Vue2在实现响应式过程需要实现其他的方法辅助(如重写数组方法、增加额外setdelete方法)

// 数组重写
const originalProto = Array.prototype
const arrayProto = Object.create(originalProto)
['push', 'pop', 'shift', 'unshift', 'splice', 'reverse', 'sort'].forEach(method => {
  arrayProto[method] = function () {
    originalProto[method].apply(this.arguments)
    dep.notice()
  }
});

// set、delete
Vue.set(obj,'bar','newbar')
Vue.delete(obj),'bar')

Proxy 不兼容IE,也没有 polyfilldefineProperty 能支持到IE9

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1024663.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vue打印、vue-print-nb插件的基本使用

今天做项目碰到一个打印的需求,只打印一个表格,去网上找了些方法总结一下 打印的方法最常见的就是window.print(),这是浏览器自带的打印方法,方便快捷无需安装插件,但相应的自定义化也差无法打印页面局部,去网上查找资…

薄盒借周杰伦IP卖藏品 车翻在奈雪的茶

在瑞幸联名茅台、喜茶联名FENDI、茶百道联名米哈游后,奈雪的茶搭上了周杰伦。9月14日,在《范特西》专辑发行22周年之际,奈雪的茶推出“范特西音乐宇宙”主题的奶茶与周边。 周杰伦系IP加持,奈雪的茶卖爆了,范特西Styl…

淘宝天猫商品全网搜索接口,关键词搜索商品列表数据接口,淘宝API接口申请指南

淘宝搜索接口是一种提供更便捷的淘宝商品搜索服务的工具。通过该接口,用户可以更加快速地找到自己需要的商品,节省时间和精力。 淘宝关键字搜索接口主要用于以下几个方面的业务应用: 商品搜索。用户可以根据关键字搜索他们想要购买的商品。…

生信学院|09月20日《在线焊件建模——xFrame》

课程主题:在线焊件建模——xFrame 课程时间:2023年09月20日 14:00-14:30 主讲人:武旭 生信科技 售后服务工程师 1、3DEXPERIENCE设计平台介绍 2、xFrame设计工具使用 3、Q&A 请安装腾讯会议客户端或APP,微信扫描海报中的…

【深度学习】clip-interrogator clip docker 容器启动过程

文章目录 dockerfile备忘ENTRYPOINT ["bash", "/app/startProject.sh"]常用docker指令web服务脚本访问接口文件 给一张图片,输出图片描述。 dockerfile备忘 只有从dockerfile制作的镜像才有分层结构,加速传输,故第一步…

Linux内核源码分析 (B.2)深入理解 Linux 物理内存管理

Linux内核源码分析 (B.2)深入理解 Linux 物理内存管理 文章目录 Linux内核源码分析 (B.2)深入理解 Linux 物理内存管理[TOC] 1\. 前文回顾2\. 从 CPU 角度看物理内存模型2.1 FLATMEM 平坦内存模型2.2 DISCONTIGMEM 非连续内存模型2.3 SPARSEMEM 稀疏内存模型2.3.1 物理内存热插…

MySQL常见面试题(一)

😀前言 在数据库管理系统中,存储引擎起着核心的角色,它决定了数据管理和存储的方式。MySQL作为一个领先的开源关系型数据库管理系统,提供了多种存储引擎来满足不同的需求和优化不同的应用。除了选择合适的存储引擎,数据…

拉格朗日乘子法思路来源

核心思路:由果索因 一. 直观理解 1. 问题描述 对于如"图1"式(等式约束优化问题, 可行域是边界), 转化成拉格朗日乘子法的思路来源: 图1: 拉格朗日乘子法问题描述图 如"图2",f为曲面.c为平面, 黑色加粗线是f和c的交线.(约束就是限制自变量的变化范围). …

Llama2-Chinese项目:2.1-Atom-7B预训练

虽然Llama2的预训练数据相对于第一代LLaMA扩大了一倍,但是中文预训练数据的比例依然非常少,仅占0.13%,这也导致了原始Llama2的中文能力较弱。为了能够提升模型的中文能力,可以采用微调和预训练两种路径,其中&#xff1…

聚观早报|蔚来汽车首颗自研芯片;中式汉堡正打破“麦门永存”

【聚观365】9月19日消息 蔚来汽车首颗自研芯片 中式汉堡正在打破“麦门永存” 三星Galaxy S24系列入网 特斯拉电动皮卡预订量已超过200万辆 郭明錤称iPhone 15 Pro Max需求强劲 蔚来汽车首颗自研芯片 蔚来汽车正在进行自研芯片布局,蔚来汽车硬件副总裁白剑上个…

慢SQL治理经验总结

在过去两年的工作中,我们团队曾负责大淘宝技术的慢SQL治理工作,作为横向的数据安全治理平台,如何快速准确地发现部门内所有应用的慢SQL,并进行高效的推动治理,同时覆盖多个开发、生产环境,是一个很大的挑战…

机器人掀起“智能热潮”:揭秘中国机器人市场的新风貌

原创 | 文 BFT机器人 中国的机器人市场和自动化形势从未像今年这样令人兴奋。去年,全球超过一半的工业机器人在中国销售。2023年上半年,中国机器人需求趋势仍在继续上升,根据估算,在此期间销售的工业机器人数量约为14.5万台&…

用Python分析文本数据的词频并词云图可视化(文末送书)

🤵‍♂️ 个人主页:艾派森的个人主页 ✍🏻作者简介:Python学习者 🐋 希望大家多多支持,我们一起进步!😄 如果文章对你有帮助的话, 欢迎评论 💬点赞&#x1f4…

广播风暴的分析和解决方法(STP配置)

目录 1.广播风暴 2.解决方法&#xff1a;配置STP 1.广播风暴 以下图为例 配置交换机LSW1 <Huawei>sys Enter system view, return user view with CtrlZ. [Huawei]sysname LSW1 [LSW1]stp Sep 14 2023 05:35:15-08:00 LSW1 DS/4/DATASYNC_CFGCHANGE:OID 1.3.6.1.4.1.…

SAP中销售订单运达方导致销项税错误问题实例

近期财务同事反映SAP中一笔国内客户的销售发票会计凭证中显示码是X0&#xff0c;代出的销项税是0。 进一步检查销售发票billing&#xff0c;发现发票价格条件中销税税条件取值确实是0。 基于税码决定的系统逻辑&#xff0c;前面博客中也曾专门分析过。下面在追踪问题时就会按这…

九、蜂鸣器

九、蜂鸣器 蜂鸣器介绍蜂鸣器播放提示音 蜂鸣器介绍 音符频率对照表 蜂鸣器播放提示音 #include <REGX52.H> #include <INTRINS.H> //蜂鸣器端口&#xff1a; sbit BuzzerP1^5; void Buzzer_Delay500us() //12.000MHz {unsigned char i;_nop_();i 247;while (-…

【计算机组成原理】读书笔记第三期:内存和磁盘的关系

目录 写在开头 内存与磁盘的关系 基本关系 磁盘缓存 虚拟内存 节约内存的编程方法 通过DLL文件实现函数共有 通过调用_stdcall来降低文件程序的大小 磁盘的物理结构 结尾 写在开头 本文继续阅读总结《程序是怎样跑起来的》这本书&#xff08;作者&#xff1a;矢泽…

基于Java的流浪动物救助及领养管理设计与实现

前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌&#x1f497; &#x1f447;&#x1f3fb;…

机器人制作开源方案 | 随叫随到的智能垃圾桶

作者&#xff1a;卢智浩 尹宗岱 胡文珺 付文智 陈星 单位&#xff1a;江汉大学 指导老师&#xff1a;侍中楼 李巍 本作品围绕探索者场景和应用主题&#xff0c;基于当今时代“智能家”的快速发展&#xff0c;智慧生活成为未来的一大发展趋势&#xff0c;因此我们设计了此款可…

基于Java网络书店商城设计实现(源码+lw+部署文档+讲解等)

博主介绍&#xff1a;✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专…