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进行记录对象访问

  • 针对那些重度依赖资源,执行缓慢或是频繁使用的方法或接口,统计它们的使用或是性能

  • 可以记录各种各样的信息而不用修改应用程序的代码或是阻塞代码执行。并且只需要在这些代码的基础上稍事修改就可以记录特性函数的执行性能

  • 以上例子就是一个监听函数执行的代理,可以将其进行扩展为打点函数

  • 这里面 Proxytrap 为什么使用 get 而不是 apply ? 答案

四:普通函数与构造函数的兼容

  • 构造函数调用没有使用new关键字来调用的话,Class对象会直接抛出异常
  • 使用 Proxy 进行封装让构造函数也能够直接进行函数调用

五:深层取值判断(看代码)

  • 需要解决的几个问题

    1. 获取数据进行拦截
    2. xxx.xxx.xxx...无论 undefined 出现在哪里都不能报错
    3. Proxyget() 传入的参数必须是对象
  • 传统方式深层取值繁琐,利用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 性能问题

6. Proxy 兼容性差

  • Vue 3.0 中放弃了对于IE的支持(以为 Vue 3.0 中会对不兼容的浏览器进行向下兼容,但是经过查看资料和源码发现尤大压根没做兼容)
  • 目前并没有一个完整支持 Proxy 所有拦截方法的 Polyfill 方案,有一个 google 编写的 proxy-polyfill 也只支持了 get/set/apply/construct 四种拦截

多说一嘴 Decorator

  • ES7 中实现的 Decorator,相当于设计模式中的装饰器模式。
  • 如果简单地区分 ProxyDecorator 的使用场景,可以概括为:Proxy 的核心作用是控制外界对被代理者内部的访问,Decorator 的核心作用是增强被装饰者的功能。

写在最后

  • 如果你觉得这篇文章对你有益,烦请点赞以及分享给更多需要的人!

快到碗里来!百度校招还有HC!甩简历来!

极速直接内推【字节跳动】&【百度】&【猿辅导】&【京东】

欢迎关注微信公众号【全栈道路】,获取更多科技相关知识及免费书籍。

更多好文

几行代码教你解决微信生成海报及二维码

冷门的HTML - tabindex 的作用

[万字长文]百度和好未来面试经含答案

[前端面试]前端缓存问题看这篇,让面试官爱上你

记一次惨痛的Vue-cli + VueX + SSR经历

[三分钟小文]前端性能优化-HTML、CSS、JS部分

[三分钟小文]前端性能优化-页面加载速度优化

[三分钟小文]前端性能优化-网络传输层优化

Vue3.0 响应式数据原理:ES6 Proxy的更多相关文章

  1. 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# ...

  2. vue2与vue3实现响应式的原理区别和提升

    区别: vue2.x: 实现原理: 对象类型:Object.defineProperty()对属性的读取,修改进行拦截(数据劫持): 数组类型:通过重写更新数组的一系列方法来进行拦截(对数组的变更方法 ...

  3. Vue3.0响应式原理

    Vue3.0的响应式基于Proxy实现.具体代码如下: 1 let targetMap = new WeakMap() 2 let effectStack = [] //存储副作用 3 4 const ...

  4. Vue3.0响应式实现

    基于Proxy // 弱引用映射表 es6 防止对象不能被回收 let toProxy = new WeakMap(); // 原对象: 代理过得对象 let toRaw = new WeakMap( ...

  5. vue2.0与3.0响应式原理机制

    vue2.0响应式原理 - defineProperty 这个原理老生常谈了,就是拦截对象,给对象的属性增加set 和 get方法,因为核心是defineProperty所以还需要对数组的方法进行拦截 ...

  6. Vue 2.0 与 Vue 3.0 响应式原理比较

    Vue 2.0 的响应式是基于Object.defineProperty实现的 当你把一个普通的 JavaScript 对象传入 Vue 实例作为 data 选项,Vue 将遍历此对象所有的 prop ...

  7. Vue实现双向绑定的原理以及响应式数据

    一.vue中的响应式属性 Vue中的数据实现响应式绑定 1.对象实现响应式: 是在初始化的时候利用definePrototype的定义set和get过滤器,在进行组件模板编译时实现water的监听搜集 ...

  8. 简单对比vue2.x与vue3.x响应式及新功能

    简单对比vue2.x与vue3.x响应式 对响应方式来讲:Vue3.x 将使用Proxy ,取代Vue2.x 版本的 Object.defineProperty. 为何要将Object.defineP ...

  9. vue源码之响应式数据

    分析vue是如何实现数据响应的. 前记 现在回顾一下看数据响应的原因. 之前看了vuex和vue-i18n的源码, 他们都有自己内部的vm, 也就是vue实例. 使用的都是vue的响应式数据特性及$w ...

随机推荐

  1. linux修改环境变量后无法登录

    在登陆界面按Ctrl+Alt+F1(F1~F6), 进入 tty 后登陆账号. 执行以下命令: /usr/bin/sudo /usr/bin/vi /etc/environment 将PATH的值复原 ...

  2. plsql查询中文乱码

    1.查看数据库字符集 select userenv('language') from dual 查看数据库字符集 2.在环境变量中添加并设置变量 变量名:NLS_LANG: 变量值:第一步查询的数据库 ...

  3. JMeter性能测试工具使用入门

    目录 安装 下载安装包 解压 添加到环境变量 启动 切换中文 打开日志查看 使用示例 创建线程组 添加HTTP接口 添加察看结果树 运行测试 添加断言 添加自定义变量 JMeter是一款强大的性能测试 ...

  4. JS里的小细节,持续更新

    判断把值定为 false 集合 JavaScript里把 null.undefined.0.''.NaN 都视为false,而其他值一概为 true Map Map是一组键值对的结构,具有极快的查找速 ...

  5. 女朋友看了我的博客,说太LOW了,于是我搞了一天~

    持续原创输出,点击上方蓝字关注我 原创博客+1,点击左下角阅读原文进入 目录 前言 如何下载? 配置文件的分类 基本信息配置 修改主题 Next主题样式设置 添加动态背景 修改链接的样式 添加文章搜索 ...

  6. JDK源码阅读-------自学笔记(二十五)(java.util.Vector 自定义讲解)

    Vector 向量 Vector简述 1).Vector底层是用数组实现的List 2).虽然线程安全,但是效率低,所以并不是安全就是好的 3).底层大量方法添加synchronized同步标记,sy ...

  7. sqlServer数据库中的日期转换

    今天开发过程中涉及到 sqlServer数据库数据同步至mysql数据,所以对日期格式转换需求,查到了一些关于sqlServer 的日期转换内容: 一般存入数据库中的时间格式为yyyy-mm-ddhh ...

  8. 开源!一款功能强大的高性能二进制序列化器Bssom.Net

    好久没更新博客了,我开源了一款高性能的二进制序列化器Bssom.Net和新颖的二进制协议Bssom,欢迎大家Star,欢迎参与项目贡献! Net开源技术交流群 976304396,禁止水,只能讨论技术 ...

  9. SpringMVC找不到js等文件,有两种方式可以解决这个问题。

    (1)当你选择不过滤任何文件时,必须去springmvc.xml去设置默认加载. (2)如果你在web.xml中设置的过滤请求那么你就不用设置默认加载,但请求的url必须符合格式.

  10. 在充电桩联网部署方案中4G DTU的优势是什么

    充电桩作为电动汽车充电生态链的一环,具有非常重要的作用,成都远向电子为电动车充电桩.充电站提供专业的无线通信组网产品与技术解决方案,协助充电桩厂家.充电桩运营商.商业充电服务商实现:充电桩设备工作状态 ...