Vue3.0 响应式数据原理:ES6 Proxy
Vue3.0 开始用 Proxy 代替 Object.defineProperty了,这篇文章结合实例教你如何使用Proxy
本篇文章同时收录【前端知识点】中,链接直达
阅读本文您将收获
JavaScript
中的Proxy
是什么?能干什么?Vue3.0
开始为什么用Proxy
代替Object.defineProperty
Proxy
是什么
解释参考MDN,链接直达
名词解释
- Proxy 对象用于定义基本操作的自定义行为(如属性查找、赋值、枚举、函数调用等)
- Proxy 用于修改某些操作的默认行为,也可以理解为在目标对象之前架设一层拦截,外部所有的访问都必须先通过这层拦截,因此提供了一种机制,可以对外部的访问进行过滤和修改
语法
const p = new Proxy(target, handler)
target
: 要使用 Proxy 包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)handler
: 对该代理对象的各种操作行为处理(为空对象的情况下,基本可以理解为是对第一个参数做的一次浅拷贝)
- 简而言之:
target
就是你想要代理的对象;而handler
是一个函数对象,其中定义了所有你想替target
代为管理的操作对象,包含了:- *
handler.has(target, prop)
:in
操作符的捕捉器,拦截HasProperty操作 - *
handler.get(target, prop)
: 属性读取操作的捕捉器 - *
handler.set(target, prop, value)
: 属性设置操作的捕捉器 - *
handler.apply(target, object, args)
: 函数调用操作的捕捉器,拦截函数的调用、call和apply操作 handler.getPrototypeOf()
:Object.getPrototypeOf
方法的捕捉器handler.setPrototypeOf()
:Object.setPrototypeOf
方法的捕捉器handler.isExtensible()
:Object.isExtensible
方法的捕捉器handler.preventExtensions()
:Object.preventExtensions
方法的捕捉器handler.getOwnPropertyDescriptor()
:Object.getOwnPropertyDescriptor
方法的捕捉器handler.defineProperty()
:Object.defineProperty
方法的捕捉器handler.deleteProperty()
:delete
操作符的捕捉器handler.ownKeys()
:Object.getOwnPropertyNames
方法和Object.getOwnPropertySymbols
方法的捕捉器handler.construct()
: new 操作符的捕捉器
- *
- 注意:如果一个属性
不可配置
||不可写
,则该属性不可被代理,通过Proxy
访问该属性会报错。 *
标记的trap为本文都要涉及到的
Proxy
能干什么?
当你想进行以下操作时proxy模式通常会很有用:
- 拦截或控制对某个对象的访问
- 通过隐藏事务或辅助逻辑来减小方法/类的复杂性
- 防止在未经验证/准备的情况下执行重度依赖资源的操作
一:javascript中真正的私有变量/拦截has...in...操作/给出提示信息或是阻止特定操作
传统方法私有变量可获取可修改
Proxy 设置私有变量
- 针对私有变量,可以使用一个proxy来截获针对某个属性的请求并作出限制或是直接返回
undefined
- 还可以使用
has
trap 来掩盖这个属性的存在
has
方法拦截的是hasProperty
操作,不是hasOwnProperty
,所以has...in
方法不判断一个属性是自身属性还是继承的属性注意:
has...in
可以拦截到,for...in
拦截不到阻止其他人删除属性,想让调用方法的人知道该方法已经被废弃,或是想阻止其他人修改属性
- 注意: 要是
Proxy
代理起作用,必须针对Proxy
的实例进行操作,而不是针对目标对象进行操作
二:数据校验(看代码)
- 利用 Proxy 代理进行简单数据校验
- 校验逻辑直接加在代理处理函数中过于繁重,我们可以把校验模块直接抽离出来,只需要去处理校验的逻辑,代理层面后续不需要改动
三:利用proxy进行记录对象访问
针对那些重度依赖资源,执行缓慢或是频繁使用的方法或接口,统计它们的使用或是性能
可以记录各种各样的信息而不用修改应用程序的代码或是阻塞代码执行。并且只需要在这些代码的基础上稍事修改就可以记录特性函数的执行性能
以上例子就是一个监听函数执行的代理,可以将其进行扩展为打点函数
这里面
Proxy
的trap
为什么使用get
而不是apply
? 答案
四:普通函数与构造函数的兼容
- 构造函数调用没有使用new关键字来调用的话,Class对象会直接抛出异常
- 使用
Proxy
进行封装让构造函数也能够直接进行函数调用
五:深层取值判断(看代码)
需要解决的几个问题
- 获取数据进行拦截
xxx.xxx.xxx...
无论undefined
出现在哪里都不能报错Proxy
的get()
传入的参数必须是对象
传统方式深层取值繁琐,利用Proxy可以简化不必要代码
但是当
target[prop]
是undefined
的时候,Proxy get()
的入参变成了undefined
,但Proxy
第一个入参必须为对象需要对 obj 为
undefined
的时候进行特殊处理,为了能够深层取值,所以使用一个空函数进行设置拦截,利用apply
trap 进行处理
我们理想中的应该是,如果属性为
undefined
就返回undefined
,但仍要支持访问下级属性,而不是抛出错误顺着这个思路来的话,很明显当属性为
undefined
的时候也需要用Proxy
进行特殊处理
所以我们需要一个具有下面特性的get()
方法
getData(undefined)() === undefined; // true
getData(undefined).xxx.yyy.zzz(); // undefined
- 这里完全不需要注意
get(undefined).xxx
是否为正确的值,因为想获取值必须要执行才能拿到 - 那么只需要对所有
undefined
后面访问的属性都默认为undefined
就好了,所以我们需要一个代理了undefined
后的返回对象 - 同时为了解决无限循环执行的问题,当第一次检测到出现
undefined
的时候,停止执行
六:日志上报
Vue 3.0 的 Proxy & Object.defineProperty
Proxy
- 劫持方式:代理整个对象,只需做一层代理就可以监听同级结构下的所有属性变化,包括新增属性和删除属性
- 本质:
Proxy
本质上属于元编程非破坏性数据劫持,在原对象的基础上进行了功能的衍生而又不影响原对象,符合松耦合高内聚的设计理念
Object.defineProperty
- 劫持方式:只能劫持对象的属性,不能直接代理对象
- 流程:get中进行依赖收集,set数据时通知订阅者更新
- 存在的问题:虽然
Object.defineProperty
通过为属性设置getter/setter
能够完成数据的响应式,但是它并不算是实现数据的响应式的完美方案,某些情况下需要对其进行修补或者hack,这也是它的缺陷,主要表现在两个方面:- 无法检测到对象属性的新增或删除
- 不能监听数组的变化
1. Object.defineProperty 无法监听新增加的属性
- 解决方式:提供方法重新手动Observe,需要监听的话使用
Vue.set()
重新设置添加属性的响应式
2. Object.defineProperty 无法一次性监听对象所有属性,如对象属性的子属性
- 解决方式: 通过递归调用来实现子属性响应式
3. Object.defineProperty 无法响应数组操作
- 解决方式:通过遍历和重写Array数组原型方法操作方法实现,但是也只限制在
push/pop/shift/unshift/splice/sort/reverse
这七个方法,其他数组方法及数组的使用则无法检测到,也无法监听数组索引的变化和长度的变更
4. Proxy 拦截方式更多, Object.defineProperty 只有 get 和 set
5. Proxy 性能问题
Proxy
的性能比Promise
还差Proxy
作为新标准,从长远来看,JS 引擎会继续优化Proxy
- Thoughts on ES6 Proxies Performance
- ES6 Proxy 性能之我见
6. Proxy 兼容性差
Vue 3.0
中放弃了对于IE的支持(以为Vue 3.0
中会对不兼容的浏览器进行向下兼容,但是经过查看资料和源码发现尤大压根没做兼容)- 目前并没有一个完整支持
Proxy
所有拦截方法的Polyfill
方案,有一个google
编写的proxy-polyfill
也只支持了get/set/apply/construct
四种拦截
多说一嘴 Decorator
- ES7 中实现的
Decorator
,相当于设计模式中的装饰器模式。 - 如果简单地区分
Proxy
和Decorator
的使用场景,可以概括为:Proxy
的核心作用是控制外界对被代理者内部的访问,Decorator
的核心作用是增强被装饰者的功能。
写在最后
- 如果你觉得这篇文章对你有益,烦请点赞以及分享给更多需要的人!
快到碗里来!百度校招还有HC!甩简历来!
极速直接内推【字节跳动】&【百度】&【猿辅导】&【京东】
欢迎关注微信公众号【全栈道路】,获取更多科技相关知识及免费书籍。
更多好文
Vue3.0 响应式数据原理:ES6 Proxy的更多相关文章
- Vue3.0工程创建 && setup、ref、reactive函数 && Vue3.0响应式实现原理
1 # 一.创建Vue3.0工程 2 # 1.使用vue-cli创建 3 # 官方文档: https://cli.vuejs.org/zh/guide/creating-a-project.html# ...
- vue2与vue3实现响应式的原理区别和提升
区别: vue2.x: 实现原理: 对象类型:Object.defineProperty()对属性的读取,修改进行拦截(数据劫持): 数组类型:通过重写更新数组的一系列方法来进行拦截(对数组的变更方法 ...
- Vue3.0响应式原理
Vue3.0的响应式基于Proxy实现.具体代码如下: 1 let targetMap = new WeakMap() 2 let effectStack = [] //存储副作用 3 4 const ...
- Vue3.0响应式实现
基于Proxy // 弱引用映射表 es6 防止对象不能被回收 let toProxy = new WeakMap(); // 原对象: 代理过得对象 let toRaw = new WeakMap( ...
- vue2.0与3.0响应式原理机制
vue2.0响应式原理 - defineProperty 这个原理老生常谈了,就是拦截对象,给对象的属性增加set 和 get方法,因为核心是defineProperty所以还需要对数组的方法进行拦截 ...
- Vue 2.0 与 Vue 3.0 响应式原理比较
Vue 2.0 的响应式是基于Object.defineProperty实现的 当你把一个普通的 JavaScript 对象传入 Vue 实例作为 data 选项,Vue 将遍历此对象所有的 prop ...
- Vue实现双向绑定的原理以及响应式数据
一.vue中的响应式属性 Vue中的数据实现响应式绑定 1.对象实现响应式: 是在初始化的时候利用definePrototype的定义set和get过滤器,在进行组件模板编译时实现water的监听搜集 ...
- 简单对比vue2.x与vue3.x响应式及新功能
简单对比vue2.x与vue3.x响应式 对响应方式来讲:Vue3.x 将使用Proxy ,取代Vue2.x 版本的 Object.defineProperty. 为何要将Object.defineP ...
- vue源码之响应式数据
分析vue是如何实现数据响应的. 前记 现在回顾一下看数据响应的原因. 之前看了vuex和vue-i18n的源码, 他们都有自己内部的vm, 也就是vue实例. 使用的都是vue的响应式数据特性及$w ...
随机推荐
- linux修改环境变量后无法登录
在登陆界面按Ctrl+Alt+F1(F1~F6), 进入 tty 后登陆账号. 执行以下命令: /usr/bin/sudo /usr/bin/vi /etc/environment 将PATH的值复原 ...
- plsql查询中文乱码
1.查看数据库字符集 select userenv('language') from dual 查看数据库字符集 2.在环境变量中添加并设置变量 变量名:NLS_LANG: 变量值:第一步查询的数据库 ...
- JMeter性能测试工具使用入门
目录 安装 下载安装包 解压 添加到环境变量 启动 切换中文 打开日志查看 使用示例 创建线程组 添加HTTP接口 添加察看结果树 运行测试 添加断言 添加自定义变量 JMeter是一款强大的性能测试 ...
- JS里的小细节,持续更新
判断把值定为 false 集合 JavaScript里把 null.undefined.0.''.NaN 都视为false,而其他值一概为 true Map Map是一组键值对的结构,具有极快的查找速 ...
- 女朋友看了我的博客,说太LOW了,于是我搞了一天~
持续原创输出,点击上方蓝字关注我 原创博客+1,点击左下角阅读原文进入 目录 前言 如何下载? 配置文件的分类 基本信息配置 修改主题 Next主题样式设置 添加动态背景 修改链接的样式 添加文章搜索 ...
- JDK源码阅读-------自学笔记(二十五)(java.util.Vector 自定义讲解)
Vector 向量 Vector简述 1).Vector底层是用数组实现的List 2).虽然线程安全,但是效率低,所以并不是安全就是好的 3).底层大量方法添加synchronized同步标记,sy ...
- sqlServer数据库中的日期转换
今天开发过程中涉及到 sqlServer数据库数据同步至mysql数据,所以对日期格式转换需求,查到了一些关于sqlServer 的日期转换内容: 一般存入数据库中的时间格式为yyyy-mm-ddhh ...
- 开源!一款功能强大的高性能二进制序列化器Bssom.Net
好久没更新博客了,我开源了一款高性能的二进制序列化器Bssom.Net和新颖的二进制协议Bssom,欢迎大家Star,欢迎参与项目贡献! Net开源技术交流群 976304396,禁止水,只能讨论技术 ...
- SpringMVC找不到js等文件,有两种方式可以解决这个问题。
(1)当你选择不过滤任何文件时,必须去springmvc.xml去设置默认加载. (2)如果你在web.xml中设置的过滤请求那么你就不用设置默认加载,但请求的url必须符合格式.
- 在充电桩联网部署方案中4G DTU的优势是什么
充电桩作为电动汽车充电生态链的一环,具有非常重要的作用,成都远向电子为电动车充电桩.充电站提供专业的无线通信组网产品与技术解决方案,协助充电桩厂家.充电桩运营商.商业充电服务商实现:充电桩设备工作状态 ...