iT邦幫忙

2022 iThome 鐵人賽

DAY 7
0

前情提要

Vue 2以DefineProperty 攔截數據達到監聽效果。(可以數據監聽的DefineProperty與Vue2-D05)
Vu3 Composition API的底層,改以Proxy 監聽數據和物件,
昨天先聊了常和Proxy 搭配的Refelct 今天殺入正題,看看Proxy到底怎麼用?
哪裡好?
到底為什麼要從DefineProperty 改成Proxy

什麼是Proxy?

Proxy 英文就是代理的意思,JS 的Proxy出現於ES6,可以代理物件,

來攔截或重新定義基本操作。


環遊非洲第07天:非洲101 之Q

非洲哪個國家使用手機支付比例奪冠?高達八成網際網路使用人口都在用!
A.肯亞
B.南非
C.奈及利亞
D.埃及


基本用法

Proxy是建構函數,傳進去的參數主要就是:

1.target 目標對象

2.handler 事件處理器決定你要攔截什麼或是要重新定義什麼 (要用物件來包喔)

handler裡面常用的方法也跟Object 很像,如setget...

下面就以set為例子,看Proxy如何和defineProperty一樣做到攔截

const myCountry = {name: 'Kenya'}
const handler = {
    get(){
        console.log('被讀取了');
        return myCountry.name
    }
}

const proxyCountry = new Proxy(myCountry,handler);
//當我們要讀取name這個屬性時,就可以攔截到,回傳我們設定的console.log
console.log(proxyCountry.name); //return '被讀取了', Kenya

搭配Reflect一起服用

Reflect对象经常和Proxy代理一起使用,原因有3:

1.Reflect的方法和Proxy第2個handle參數方法是一樣的。
2.Proxy get/set()方法需要的返回值正是Reflect的get/set方法的返回值,可以配合使用
3.Proxy和Reflect都有receiver參數,可以正確綁定this指向-->重要!!
4.操作目標報錯時返回 false

引用&修改自:Proxy是代理,Reflect是干嘛用的?

來看看兩個搭配如何做到攔截!

get()方法為例

MDN說Proxy的get是一個陷阱(trap)(好可愛的說法,還是其實有別的翻譯XD)

可以攔截以下方法:

  1. 原物件上的屬性讀取

  2. 讀取原型鍊上的屬性 Inherited property access: Object.create(proxy)[foo]

  3. Reflex.get

參數跟Reflec的一樣,get(target, property, receiver)

來看看用跟不用Reflect會怎麼樣:

const parentCountry = {
    name: 'Kenya',
    get value() {
        return this.name;
    },
};

const handler = {
    get(target, key, receiver) {
        //相當於targetp[key]
        return Reflect.get(target, key);
        // return Reflect.get(target, key, receiver);
    },
};

const proxy = new Proxy(parentCountry, handler);

const childCapital = {
    name: 'Nairobi',
};

// 設置childCapital繼承parentCountry代理對象proxy
Object.setPrototypeOf(childCapital, proxy);

console.log(childCapital.value);

猜猜Console.log出來的是什麼?

Kenya

我們的this指向在proxy的get陷阱中被重新指向了

這時候就要靠receiver上場啦,它可以幫我們把this指回繼承對象本身

const parentCountry = {
    name: 'Kenya',
    get value() {
        return this.name;
    },
};

const handler = {
    get(target, key, receiver) {
        //類似於targetp[key].call(receiver)
        return Reflect.get(target, key, receiver);
    },
};

const proxy = new Proxy(parentCountry, handler);

const childCapital = {
    name: 'Nairobi',
};

// 設置childCapital繼承parentCountry代理對象proxy
Object.setPrototypeOf(childCapital, proxy);

console.log(childCapital.value); //Nairobi

案例引用修改自:为什么Proxy一定要配合Reflect使用?

詳細也請去上面看看~

set()方法

回頭看一下defineProperty 做不到的事情:

1.有多Key-->要遍歷所有對象,很麻煩

2.Object的刪除和新增屬性-->無法監聽

3.Array添加數據或是移除(push & shift)-->無法監聽

來用Proxy實作,封裝一個reactive試試看

function reactive(obj) {
    const proxy = 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 proxy
}

來測試一下:

const myCountry = reactive({
    name: 'Kenya'
})

// 設置不存在的屬性
myCountry.capital = 'Nairobi' // success && console: 設置! capital:Nairobi
// 删除属性
delete myCountry.capital // success && console: true

const africa = ['Nigeria','South Africa','Kenya']
const proxyAfrica = reactive(africa)
proxyAfrica.push('Egypt') 
// console: 攔截! push:function push() { [native code] }
//攔截! length:3
//設置! 3:Egypt
//設置! length:4

案例引用修改自:Vue3.0里为什么要用 Proxy API 替代 defineProperty API ?

以上!

就是簡易版的Vue Proxy API 介紹,

開始來玩Vue3 Reactive API囉!


環遊非洲第07天:非洲101 之A

答案是~~肯亞!

網路使用者的八成都在使用手機支付,比例甚至高過歐洲。
非洲最大的手機支付APP-M-Pesa就來自肯亞,
他們稱每個月在非洲就有5000萬的活躍用戶。

其實手機支付可以成長這麼快的一個原因是,
大部分的人都沒有銀行帳號。(經濟弱勢的人很難申請銀行帳號)

來源:Mobile payment in Africa is more popular than you may think - here's why


參考


上一篇
為什麼 Vue 要使用Proxy 之 Reflect-D06
下一篇
一口氣看完 Vue composition API 語法-D08
系列文
分手前端菜雞之旅@非洲30天30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言