Vue3.0 响应式数据原理:ES6 Proxy

Vue3.0 开始用 Proxy 代替 Object.defineProperty了,这篇文章结合实例教你如何使用Proxyjavascript

本篇文章同时收录【前端知识点】中,连接直达html

阅读本文您将收获

  • 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 方法不判断一个属性是自身属性仍是继承的属性vue

  • 注意: has...in 能够拦截到,for...in 拦截不到java

  • 阻止其余人删除属性,想让调用方法的人知道该方法已经被废弃,或是想阻止其余人修改属性git

  • 注意: 要是 Proxy 代理起做用,必须针对 Proxy 的实例进行操做,而不是针对目标对象进行操做

二:数据校验(看代码)

  • 利用 Proxy 代理进行简单数据校验
  • 校验逻辑直接加在代理处理函数中过于繁重,咱们能够把校验模块直接抽离出来,只须要去处理校验的逻辑,代理层面后续不须要改动

三:利用proxy进行记录对象访问

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

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

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

  • 这里面 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部分

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

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

相关文章
相关标签/搜索