当前位置: 首页 > news >正文

Vue3.0里为什么要用 Proxy API 替代 defineProperty API

一、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 = newValupdate()}}})
}

调用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 = newValupdate()}}})
}

当给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,也没有 polyfill, defineProperty 能支持到IE9

相关文章:

Vue3.0里为什么要用 Proxy API 替代 defineProperty API

一、Object.defineProperty 定义:Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象 为什么能实现响应式 通过defineProperty 两个属性,get及set get 属性的 getter 函…...

c++初阶------类和对象(下)

作者前言 🎂 ✨✨✨✨✨✨🍧🍧🍧🍧🍧🍧🍧🎂 ​🎂 作者介绍: 🎂🎂 🎂 🎉🎉&#x1f389…...

PMP考试:如何高效学习PMBOK?

PMBOK(项目管理知识体系指南)是PMP考试的核心教材,学习PMBOK对于备考PMP考试至关重要。那么我将分享一些高效学习PMBOK的方法和技巧,帮助同学们更好地掌握项目管理知识。 一、制定学习计划 在学习PMBOK之前,制定一个详…...

个人博客网站前端页面的实现

博客网站前端页面的实现 博客登录页 相关代码 login.html <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><…...

Kotlin Retrofit 网络请求

一、添加依赖&#xff1a; //Retrofit 网络请求implementation("com.squareup.retrofit2:retrofit:2.3.0")implementation("com.squareup.retrofit2:converter-gson:2.3.0")//json转换 二、创建单例类&#xff1a; package com.example.buju.httpimport …...

pyside6 pytq PyDracula QVideoWidget视频只有声音没有画面

解决方案&#xff1a; 先不使用框架&#xff0c;纯pyside6代码&#xff0c;如果添加视频有画面有声音&#xff0c;那可以排除是硬件问题&#xff0c;如果没有画面只有声音&#xff0c;可能是视频解码器无法解码&#xff0c;换个格式的视频文件如果只有使用PyDracula 出问题&am…...

Python爬网页,不确定网页的编码,不需要用第三方库

Python爬网页&#xff0c;不确定网页的编码&#xff0c;不需要用第三方库&#xff0c;自己写个判断&#xff0c;乱拳打死老师傅 detect试了&#xff0c;不好用 apparent_encoding试了&#xff0c;不好用 encoding试了&#xff0c;不好用 headers里get试了&#xff0c;不好用…...

Web测试的基础流程(外加测试过程需要的注意5点)

前言 在Web工程过程中&#xff0c;基于Web系统的测试、确认和验收是一项重要而富有挑战性的工作。基于Web的系统测试与传统的软件测试不同&#xff0c;它不但需要检查和验证是否按照设计的要求运行&#xff0c;而且还要测试系统在不同用户的浏览器端的显示是否合适。 重要的是…...

项目解决方案:视频监控接入和录像系统设计方案(下)

目 录 1.概述 2. 建设目标及需求 2.1建设总目标 2.2 需求描述 ​2.3 需求分析 3.设计依据与设计原则 3.1设计依据 3.2 设计原则 4.建设方案设计 4.1系统方案设计 4.2组网说明 5.产品介绍 5.1视频监控综合资源管理平台介绍 5.2视频录像服务器和存储 5.2.…...

Python爬虫-使用Prefect框架实现一个可视化爬虫项目

前言 本文是该专栏的第19篇,后面会持续分享python爬虫干货知识,记得关注。 相信有的同学,在处理爬虫项目的时候,有时也会需要你将爬虫项目进行一个可视化展示,方便管理者能及时详细的了解当前爬虫任务的执行进度以及执行情况,甚至需要做一个爬虫监控预警的可视化任务。 …...

[hive面试真题]-基础理论篇

hive的工作流程 hive中分区表,分桶表 工作中hive分区表的应用示例 发现hive分区中的数据不对怎么处理 hive出现code 1 2 3 什么原因 ,怎么处理 工作中hive常见的文件格式 .压缩格式 工作时常用的hive函数 谈谈对窗口函数的理解 hive中如果出现数据倾斜 ,怎么发现 ,怎么…...

【其他】sd卡的照片在相机上能看到在电脑上却看不到

sd卡的照片在相机上能看到在电脑上却看不到 前情提要&#xff1a;太长不看版解决办法&#xff1a;思路&#xff1a;一、首先考虑恢复数据二、 解决文件后缀是exe的问题 前情提要&#xff1a; 在相机里可以看到照片和视频&#xff0c;但是SD卡通过读卡器插入电脑看不到&#x…...

Linux 之六:系统性能监控和挂载

系统性能 Linux系统中&#xff0c;有许多命令用于监测和分析性能指标。以下是一些常用的Linux性能分析命令&#xff1a; top&#xff1a;实时查看并监控CPU、内存以及各个进程的资源占用情况。htop&#xff08;需要安装&#xff09;&#xff1a;一个增强版的 top 命令&#x…...

【Web】浅聊Java反序列化之C3P0——JNDI注入利用

目录 简介 原理分析 EXP 前文&#xff1a;【Web】浅聊Java反序列化之C3P0——URLClassLoader利用 【Web】浅聊Java反序列化之C3P0——不出网Hex字节码加载利用 简介 出网的情况下&#xff0c;这个C3P0的Gadget可以和fastjson&#xff0c;Snake YAML , JYAML,Yamlbeans , …...

Java项目:基于Springboot+vue实现的付费自习室系统设计与实现(源码+数据库+毕业论文)附含微信小程序端代码

一、项目简介 本项目是一套基于Springbootvue实现的付费自习室系统 包含&#xff1a;项目源码、数据库脚本等&#xff0c;该项目附带全部源码可作为毕设使用。 项目都经过严格调试&#xff0c;eclipse或者idea 确保可以运行&#xff01; 该系统功能完善、界面美观、操作简单、…...

C++写食堂菜品管理系统

说明:本博文来自CSDN-问答板块,题主提问。 需要:学校拟开发一套食堂菜品管理系统,以便对菜品和同学们的评价进行管理,其中包含如下信息: 商户:商户名称、柜面位置、电话…… 菜品:菜品编号、菜品名称、价格、所属商户…… 学生:注册账号、昵称、电话…… 食堂里的商户…...

vue 在线预览word

1 mammoth 先找的是mammoth这个插件yarn add mammoth,版本是1,7.0 参考网上的示例使用如下&#xff1a; import mammoth from "mammoth"; const vHtml ref("") const readExcelFromRemoteFile (url) >{var xhr new XMLHttpRequest();xhr.open("…...

七彩虹八渐变 外贸建站公司wordpress模板

进出口水果wordpress外贸模板 漂亮水果wordpress外贸模板&#xff0c;做水果进出品生意的外贸公司自建站官网模板。 https://www.jianzhanpress.com/?p3516 玩具wordpress外贸模板 简洁玩具wordpress外贸模板&#xff0c;适合做跨境电商外贸公司使用的wordpres外贸s网站主题…...

[C++] C++生成随机数

一、简介 在C语言中常使用srand()random()的方式生成随机数&#xff0c;该方式并不是一个很好的随据说生成方法&#xff0c;一方面是因为其生成的随机数质量较低&#xff0c;另一方面其随机数范围也有所限制。在C11中推荐使用随机数引擎的方式生成随机数。 如何高效得生成高质…...

web开发——前端html、css、JavaScript学习总结(持续更新中.......)

目录模版 1 html:结构标签/属性文本标记: mark文本设置:删除线del / 下划线ins/ 加粗b / 强调的文本em / 重要的文本 strong超链接: a联系信息: addressdiv 定义文档中的分区或节: div行元素:spanhtml结构: main / section / articlenav表格:table html中各种标签/属性的英文扩…...

黑马Mybatis

Mybatis 表现层&#xff1a;页面展示 业务层&#xff1a;逻辑处理 持久层&#xff1a;持久数据化保存 在这里插入图片描述 Mybatis快速入门 ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/6501c2109c4442118ceb6014725e48e4.png //logback.xml <?xml ver…...

对WWDC 2025 Keynote 内容的预测

借助我们以往对苹果公司发展路径的深入研究经验&#xff0c;以及大语言模型的分析能力&#xff0c;我们系统梳理了多年来苹果 WWDC 主题演讲的规律。在 WWDC 2025 即将揭幕之际&#xff0c;我们让 ChatGPT 对今年的 Keynote 内容进行了一个初步预测&#xff0c;聊作存档。等到明…...

在鸿蒙HarmonyOS 5中使用DevEco Studio实现录音机应用

1. 项目配置与权限设置 1.1 配置module.json5 {"module": {"requestPermissions": [{"name": "ohos.permission.MICROPHONE","reason": "录音需要麦克风权限"},{"name": "ohos.permission.WRITE…...

【论文阅读28】-CNN-BiLSTM-Attention-(2024)

本文把滑坡位移序列拆开、筛优质因子&#xff0c;再用 CNN-BiLSTM-Attention 来动态预测每个子序列&#xff0c;最后重构出总位移&#xff0c;预测效果超越传统模型。 文章目录 1 引言2 方法2.1 位移时间序列加性模型2.2 变分模态分解 (VMD) 具体步骤2.3.1 样本熵&#xff08;S…...

SpringTask-03.入门案例

一.入门案例 启动类&#xff1a; package com.sky;import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.annotation.EnableCach…...

浅谈不同二分算法的查找情况

二分算法原理比较简单&#xff0c;但是实际的算法模板却有很多&#xff0c;这一切都源于二分查找问题中的复杂情况和二分算法的边界处理&#xff0c;以下是博主对一些二分算法查找的情况分析。 需要说明的是&#xff0c;以下二分算法都是基于有序序列为升序有序的情况&#xf…...

vue3+vite项目中使用.env文件环境变量方法

vue3vite项目中使用.env文件环境变量方法 .env文件作用命名规则常用的配置项示例使用方法注意事项在vite.config.js文件中读取环境变量方法 .env文件作用 .env 文件用于定义环境变量&#xff0c;这些变量可以在项目中通过 import.meta.env 进行访问。Vite 会自动加载这些环境变…...

ABAP设计模式之---“简单设计原则(Simple Design)”

“Simple Design”&#xff08;简单设计&#xff09;是软件开发中的一个重要理念&#xff0c;倡导以最简单的方式实现软件功能&#xff0c;以确保代码清晰易懂、易维护&#xff0c;并在项目需求变化时能够快速适应。 其核心目标是避免复杂和过度设计&#xff0c;遵循“让事情保…...

以光量子为例,详解量子获取方式

光量子技术获取量子比特可在室温下进行。该方式有望通过与名为硅光子学&#xff08;silicon photonics&#xff09;的光波导&#xff08;optical waveguide&#xff09;芯片制造技术和光纤等光通信技术相结合来实现量子计算机。量子力学中&#xff0c;光既是波又是粒子。光子本…...

Kafka入门-生产者

生产者 生产者发送流程&#xff1a; 延迟时间为0ms时&#xff0c;也就意味着每当有数据就会直接发送 异步发送API 异步发送和同步发送的不同在于&#xff1a;异步发送不需要等待结果&#xff0c;同步发送必须等待结果才能进行下一步发送。 普通异步发送 首先导入所需的k…...