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

一、Object.defineProperty
定义:Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象
为什么能实现响应式
通过defineProperty 两个属性,get及set
- 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中,增加了set、delete 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可以直接监听数组的变化(push、shift、splice)
const obj = [1,2,3]
const proxtObj = reactive(obj)
obj.psuh(4) // ok
Proxy有多达13种拦截方法,不限于apply、ownKeys、deleteProperty、has等等,这是Object.defineProperty不具备的
正因为defineProperty自身的缺陷,导致Vue2在实现响应式过程需要实现其他的方法辅助(如重写数组方法、增加额外set、delete方法)
// 数组重写
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
参考文献
- https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty
相关文章:
Vue3.0里为什么要用 Proxy API 替代 defineProperty API ?
一、Object.defineProperty 定义:Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象 为什么能实现响应式 通过defineProperty 两个属性,get及set get 属性的 getter 函…...
pytest利用request fixture实现个性化测试需求详解
这篇文章主要为大家详细介绍了pytest如何利用request fixture实现个性化测试需求,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起了解一下− 前言 在深入理解 pytest-repeat 插件的工作原理这篇文章中,我们看到pytest_repeat源码中有这样一段 import pyt…...
算法练习16——O(1) 时间插入、删除和获取随机元素
LeetCode 380 O(1) 时间插入、删除和获取随机元素 实现RandomizedSet 类: RandomizedSet() 初始化 RandomizedSet 对象 bool insert(int val) 当元素 val 不存在时,向集合中插入该项,并返回 true ;否则,返回 false 。 …...
实时数据更新与Apollo:探索GraphQL订阅
前言 「作者主页」:雪碧有白泡泡 「个人网站」:雪碧的个人网站 「推荐专栏」: ★java一站式服务 ★ ★ React从入门到精通★ ★前端炫酷代码分享 ★ ★ 从0到英雄,vue成神之路★ ★ uniapp-从构建到提升★ ★ 从0到英雄ÿ…...
VMware Workstation里面安装ubuntu20.04的流程
文章目录 前言一、获取 desktop ubuntu20.04 安装镜像二、VMware Workstation下安装ubuntu20.041. VMware Workstation 创建一个新的虚拟机2. ubuntu20.04的安装过程3. 登录ubuntu20.044. 移除 ubuntu20.04 安装镜像总结参考资料前言 本文主要介绍如何在PC上的虚拟机(VMware W…...
pnpm的环境安装以及安装成功后无法使用的问题
文章目录 前言1、使用npm 安装2、安装后的注意点3、遇到问题4、配置path的环境变量(1)找到环境变量(2)找到并双击path的系统变量(3)复制第1步中使用npm安装的红框部分的路径(4)将第&…...
华为eNSP配置专题-浮动路由及BFD的配置
文章目录 华为eNSP配置专题-浮动路由及BFD的配置0、参考文档1、前置环境1.1、宿主机1.2、eNSP模拟器 2、基本环境搭建2.1、基本终端构成和连接2.2、基本终端配置 3、浮动路由配置3.1、浮动路由的基本配置3.2、浮动路由的负载均衡问题3.3、浮动路由的优先级调整 4、BFD的配置4.1…...
光储并网直流微电网simulink仿真模型,光伏采用mppt实现最大功率输出研究
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...
面试题-React(十六):理解Redux及其工作原理
在现代前端开发中,状态管理是一个关键的问题。Redux是一个广泛使用的状态管理库,可以帮助开发者更有效地管理应用的状态。 一、什么是Redux? Redux是一个JavaScript状态管理库,用于管理应用中的状态(state࿰…...
Crypto(4)NewStarCTF 2023 week2 Crypto Rotate Xor
题目代码: # 导入所需的库和从secret模块加载"flag" from secret import flag from os import urandom from pwn import xor from Cryptodome.Util.number import *# 生成两个随机的 64 位素数,分别存储在变量 k1 和 k2 中 k1 getPrime(64) k2 getPrim…...
小程序-uni-app:将页面(html+css)生成图片/海报/名片,进行下载 保存到手机
一、需要描述 本文实现,uniapp微信小程序,把页面内容保存为图片,并且下载到手机上。 说实话网上找了很多资料,但是效果不理想,直到看了一个开源项目,我知道可以实现了。 本文以开源项目uniapp-wxml-to-can…...
Vue非单文件组件
组件就是用来实现局部特定功能效果的代码集合,为的就是复用编码,简化项目编码,提高运行效率。 组件分为非单文件组件和单文件组件,这里介绍的是非单文件组件。 一、创建组件 创建组件的语法格式如下: const 组件名 …...
批量xls转换为xlsx
import win32com.client as win32 import os# 另存为xlsx的文件路径 xlsx_file r"F:\志丹\1020Excel汇总\成果表备份\xlsx" xls_file r"F:\志丹\1020Excel汇总\成果表备份" for file in os.scandir(xls_file):suffix file.name.split(".")[-1…...
行情分析——加密货币市场大盘走势(10.20)
大饼昨日迅猛上涨,并在今日依然上涨,目前处在蓝色上涨趋势线,上涨趋势依然在。中长线可以考虑过几天止损或者继续持有。目前MACD日线呈现绿色实心5天,预计明后天可能会绿色空心,注意后续空头的到来,注意多单…...
https证书配置(nginx)
HTTPS 是什么 HTTPS 是一种应用层协议,是一种透过计算机网络进行安全通信的传输协议,HTTPS 经由 HTTP 进行通信,但是在 HTTP 的基础上引入了一个加密层,使用 SSL/TLS 来加密数据包,HTTPS 开发的主要目的,是…...
Go方法特性详解:简单性和高效性的充分体现
一、简介 在软件开发的世界里,理解并掌握编程语言的各种特性是至关重要的。Go(又称Golang)作为一种现代的编程语言,以其简洁的语法和出色的性能吸引了大量的开发者。然而,Go的方法(Methods)这一…...
Cesium Vue(四)— 物体(Entity)的添加与配置
1. 添加标签和广告牌 // 添加文字标签和广告牌var label viewer.entities.add({position: Cesium.Cartesian3.fromDegrees(113.3191, 23.109, 750),label: {text: "广州塔",font: "24px sans-serif",fillColor: Cesium.Color.WHITE,outlineColor: Cesium.…...
洗地机哪个好用?2023年洗地机推荐指南
说到提高家庭幸福生活的家电,洗地机肯定是少不了的,特别对于现在快节奏的生活来说,高效率的解决家务活,而且能够大幅度的提高生活质量。在市场上,消费者面临着选择合适洗地机的难题,因为有各种型号、功能和…...
螺旋矩阵(C++解法)
题目 给你一个 m 行 n 列的矩阵 matrix ,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。 示例 1: 输入:matrix [[1,2,3],[4,5,6],[7,8,9]] 输出:[1,2,3,6,9,8,7,4,5]示例 2: 输入:matrix [[…...
【Java 进阶篇】深入了解 Bootstrap 栅格系统
在网页开发中,创建响应式的布局是至关重要的,因为不同设备和屏幕尺寸需要不同的布局来呈现内容。Bootstrap 提供了一个强大的栅格系统,使开发者能够轻松创建适应不同屏幕的网页布局。本文将深入介绍 Bootstrap 栅格系统,面向初学者…...
vscode里如何用git
打开vs终端执行如下: 1 初始化 Git 仓库(如果尚未初始化) git init 2 添加文件到 Git 仓库 git add . 3 使用 git commit 命令来提交你的更改。确保在提交时加上一个有用的消息。 git commit -m "备注信息" 4 …...
JavaScript 中的 ES|QL:利用 Apache Arrow 工具
作者:来自 Elastic Jeffrey Rengifo 学习如何将 ES|QL 与 JavaScript 的 Apache Arrow 客户端工具一起使用。 想获得 Elastic 认证吗?了解下一期 Elasticsearch Engineer 培训的时间吧! Elasticsearch 拥有众多新功能,助你为自己…...
五年级数学知识边界总结思考-下册
目录 一、背景二、过程1.观察物体小学五年级下册“观察物体”知识点详解:由来、作用与意义**一、知识点核心内容****二、知识点的由来:从生活实践到数学抽象****三、知识的作用:解决实际问题的工具****四、学习的意义:培养核心素养…...
MODBUS TCP转CANopen 技术赋能高效协同作业
在现代工业自动化领域,MODBUS TCP和CANopen两种通讯协议因其稳定性和高效性被广泛应用于各种设备和系统中。而随着科技的不断进步,这两种通讯协议也正在被逐步融合,形成了一种新型的通讯方式——开疆智能MODBUS TCP转CANopen网关KJ-TCPC-CANP…...
Spring AI与Spring Modulith核心技术解析
Spring AI核心架构解析 Spring AI(https://spring.io/projects/spring-ai)作为Spring生态中的AI集成框架,其核心设计理念是通过模块化架构降低AI应用的开发复杂度。与Python生态中的LangChain/LlamaIndex等工具类似,但特别为多语…...
C++ Visual Studio 2017厂商给的源码没有.sln文件 易兆微芯片下载工具加开机动画下载。
1.先用Visual Studio 2017打开Yichip YC31xx loader.vcxproj,再用Visual Studio 2022打开。再保侟就有.sln文件了。 易兆微芯片下载工具加开机动画下载 ExtraDownloadFile1Info.\logo.bin|0|0|10D2000|0 MFC应用兼容CMD 在BOOL CYichipYC31xxloaderDlg::OnIni…...
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决 问题背景 在一个基于 Spring Cloud Gateway WebFlux 构建的微服务项目中,新增了一个本地验证码接口 /code,使用函数式路由(RouterFunction)和 Hutool 的 Circle…...
初学 pytest 记录
安装 pip install pytest用例可以是函数也可以是类中的方法 def test_func():print()class TestAdd: # def __init__(self): 在 pytest 中不可以使用__init__方法 # self.cc 12345 pytest.mark.api def test_str(self):res add(1, 2)assert res 12def test_int(self):r…...
AI,如何重构理解、匹配与决策?
AI 时代,我们如何理解消费? 作者|王彬 封面|Unplash 人们通过信息理解世界。 曾几何时,PC 与移动互联网重塑了人们的购物路径:信息变得唾手可得,商品决策变得高度依赖内容。 但 AI 时代的来…...
掌握 HTTP 请求:理解 cURL GET 语法
cURL 是一个强大的命令行工具,用于发送 HTTP 请求和与 Web 服务器交互。在 Web 开发和测试中,cURL 经常用于发送 GET 请求来获取服务器资源。本文将详细介绍 cURL GET 请求的语法和使用方法。 一、cURL 基本概念 cURL 是 "Client URL" 的缩写…...
