初识Proxy

初识Proxy写入默认值 日常开发经常碰到 ReferenceErr xxx is not defined 这种错误 这里我们可以代理 当属性不存在时 不报错 而设置一个默认值

欢迎大家来到IT世界,在知识的湖畔探索吧!

最近在复习Vue,不可避免的会接触到vue3,所以也不可避免的会思考这些问题

  1. vue3实现响应式为什么要使用proxy替换Object.defineProperty?Proxy对比Object.defineProperty有啥优缺点?
  2. 怎么通过Proxy实现响应式?

本文会回答这两个问题,通过这些问题探讨Proxy,以及Proxy在日常开发中的应用场景。

认识Proxy

Proxy意思翻译过来就是代理,外界对目标对象的访问都会被Proxy拦截,从而可以实现基本操作的拦截和自定义。

用法

let proxy = new Proxy(target,handler)

欢迎大家来到IT世界,在知识的湖畔探索吧!

  • target: 所要拦截的目标对象
  • handler: handler是一个包含你要拦截和处理的对象,当对象被代理时,handler通过捕捉器(trap)实现对各种行为的拦截

目前proxy支持13种行为的拦截

handler方法

何时触发

get

读取属性

set

写入属性

has

in操作符

deleteProperty

delete操作符

apply

函数调用

construct

new操作符

getPrototypeOf

Object.getPrototypeOf

setPrototypeOf

Object.setPrototypeOf

isExtensible

Object.isExtensible

preventExtensions

Object.preventExtensions

defineProperty

Object.defineProperty,
Object.defineProperties

getOwnPropertyDescriptor

Object.getOwnPropertyDescriptor,
for…in,
Object.keys/values/entries

ownKeys

Object.getOwnPropertyNames,
Object.getOwnPropertySymbols,
for…in,
Object.keys/values/entries


Reflect

reflect翻译过来是映射的意思,在MDN上是这样定义的

Reflect 是一个内置的对象,它提供拦截 JavaScript 操作的方法。

每个可用的代理捕捉器(trap)都有一个对应的同名Reflect函数,并能产生相同的行为。

欢迎大家来到IT世界,在知识的湖畔探索吧!let obj = { a: 10, name: 'oyc' } let newTarget = new Proxy(obj, { set(target, key, val) { console.log(`Set ${key}=${val} `); } }) // newTarget.a = 20; //Set a=20 // Reflect.set(newTarget, 'a', 20); //Set a=20 newTarget.name = 'oyq'; //Set name=oyq Reflect.set(newTarget, 'name', 'oyq'); //Set name=oyq

从这可以看出,Reflect和trap表现出来的行为是相同的。所以当你为如何去触发trap而烦恼的时候,也许这个Reflect可以帮到你。

两个问题

大致学习完proxy的内容后,再来尝试解答下页头提到的两个问题。

vue3实现响应式为什么要使用proxy替换Object.defineProperty?优缺点?

优点

  • 性能更好,Object.defineProperty只能劫持对象的属性,所以如果有嵌套对象,初始化时需要遍历data中的每个属性,在vue3中,proxy可以代理对象,不需要像vue2那样对属性进行遍历的操作
//vue2 function reactive(obj) { // 遍历对象 for (const item in obj) { if (obj[item] && typeof obj[item] == 'object') { // 递归,又重新遍历 reactive(obj[item]) } else { defineReactive(obj, item, obj[item]) } } } function defineReactive(obj, key, val) { Object.defineProperty(obj, key, { //set,get操作 }) } //vue3 let newTarget = new Proxy(obj, { // set,get操作 })
  • 自动代理新增属性,数组,Object.defineProperty的实现是对属性进行劫持,所以当新增属性时,需要重新遍历,对新增的重新进行劫持。所以需要vue2对新增的属性,以及数组进行 $set 才能保证属性是响应式的,这个过程是手动的。
欢迎大家来到IT世界,在知识的湖畔探索吧!let obj = { a: 10, name: 'oyc' } //vue2 this.$set(this.obj, 'age', 18); //每次新增都需要进行这个操作 //vue3 //自动代理 let newTarget = new Proxy(obj, { get(target, key) { return Reflect.get(target, key); }, set(target, key, val) { return Reflect.set(target, key, val); } })
  • Proxy支持13中拦截操作,Object.defineProperty无法比拟
  • Proxy是新标准,后续也会优先优化,Object.defineProperty的setter,getter后续应该优化的优先级较低

缺点

显而易见的,Proxy的兼容性相较于Object.defineProperty,是较低的,不支持IE浏览器。不过以目前的市场份额来看,IE浏览器的市场份额也不多,目前微软也将ie换成了chrome内核的edge,所以激进点的项目是完全可以使用proxy的。

初识Proxy



欢迎大家来到IT世界,在知识的湖畔探索吧!

怎么通过Proxy实现响应式?

let obj1 = { a: 10, name: 'John', list: [1, 2, 3], obj2: { obj3: 'oo', obj4: { name: 'oyc' } } } // 判断是否是对象 const isObj = (obj) => typeof obj === 'object' && obj !== null; const render = (key, val) => { console.log(`Render ${key}=${val}`); } function reactive(obj) { if (!isObj(obj)) { return obj; } const handler = { get(target, key) { // 对嵌套对象遍历 if (isObj(target[key])) { // 递归 return reactive(target[key]); } return Reflect.get(target, key); }, set(target, key, val) { // 渲染 render(key, val); return Reflect.set(target, key, val); } } const targetProxyObj = new Proxy(obj, handler); return targetProxyObj } let myObj = reactive(obj1); myObj.a = 20; // Render a=20 myObj.b = 30; //新增属性 Render b=30 myObj.list = [1, 2, 5, 6]; //修改数组 //Render list=1,2,5,6 myObj.obj2.obj4.name = 'oyq'; //修改嵌套对象 //Render name=oyq

Proxy应用场景

  • 写入默认值,日常开发经常碰到 ReferenceError: xxx is not defined 这种错误,这里我们可以代理,当属性不存在时,不报错,而设置一个默认值
欢迎大家来到IT世界,在知识的湖畔探索吧!let obj = { name: 'oyq' } let proxyObj = new Proxy(obj, { get(target, key) { if (Reflect.has(target, key)) { return target[key]; } else { return 'OYC'; } }, }) console.log(proxyObj.age);//OYC
  • 用Proxy来包装fetch,让fetch更易用
let handlers = { get (target, property) { if (!target.init) { // 初始化对象 ['GET', 'POST'].forEach(method => { target[method] = (url, params = {}) => { return fetch(url, { headers: { 'content-type': 'application/json' }, mode: 'cors', credentials: 'same-origin', method, ...params }).then(response => response.json()) } }) } return target[property] } } let API = new Proxy({}, handlers) await API.GET('XXX') await API.POST('XXX', { body: JSON.stringify({name: 1}) })
  • 检验表单
欢迎大家来到IT世界,在知识的湖畔探索吧!let formData = { name: '', age: '' } let proxyObj = new Proxy(formData, { set(target, key, val) { if (key === 'age' && typeof val !== 'number') { console.log('age must be number'); } if (key === 'name' && typeof val !== 'string') { console.log('name must be string'); } } }) proxyObj.age = 'oyc'; //age must be number proxyObj.name = 18; //name must be string
  • 负索引数组
let arr = [1, 2, 3, 4, 5] let proxyArray = new Proxy(arr, { get(target, key) { const index = key < 0 ? target.length + Number(key) : key; return Reflect.get(target, index) } }) console.log(proxyArray[-1]); //5

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://itzsg.com/101133.html

(0)
上一篇 1天前
下一篇 1天前

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

联系我们YX

mu99908888

在线咨询: 微信交谈

邮件:itzsgw@126.com

工作时间:时刻准备着!

关注微信