• <fieldset id="8imwq"><menu id="8imwq"></menu></fieldset>
  • <bdo id="8imwq"><input id="8imwq"></input></bdo>
    最新文章專題視頻專題問答1問答10問答100問答1000問答2000關鍵字專題1關鍵字專題50關鍵字專題500關鍵字專題1500TAG最新視頻文章推薦1 推薦3 推薦5 推薦7 推薦9 推薦11 推薦13 推薦15 推薦17 推薦19 推薦21 推薦23 推薦25 推薦27 推薦29 推薦31 推薦33 推薦35 推薦37視頻文章20視頻文章30視頻文章40視頻文章50視頻文章60 視頻文章70視頻文章80視頻文章90視頻文章100視頻文章120視頻文章140 視頻2關鍵字專題關鍵字專題tag2tag3文章專題文章專題2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章專題3
    問答文章1 問答文章501 問答文章1001 問答文章1501 問答文章2001 問答文章2501 問答文章3001 問答文章3501 問答文章4001 問答文章4501 問答文章5001 問答文章5501 問答文章6001 問答文章6501 問答文章7001 問答文章7501 問答文章8001 問答文章8501 問答文章9001 問答文章9501
    當前位置: 首頁 - 科技 - 知識百科 - 正文

    初探Vue3.0 中的一大亮點Proxy的使用

    來源:懂視網 責編:小采 時間:2020-11-27 22:02:52
    文檔

    初探Vue3.0 中的一大亮點Proxy的使用

    初探Vue3.0 中的一大亮點Proxy的使用:前言 不久前,也就是11月14日-16日于多倫多舉辦的 VueConf TO 2018 大會上,尤雨溪發表了名為 Vue3.0 Updates 的主題演講,對 Vue3.0 的更新計劃、方向進行了詳細闡述,表示已經放棄使用了 Object.defineProperty,而選擇了使用更快的原生 P
    推薦度:
    導讀初探Vue3.0 中的一大亮點Proxy的使用:前言 不久前,也就是11月14日-16日于多倫多舉辦的 VueConf TO 2018 大會上,尤雨溪發表了名為 Vue3.0 Updates 的主題演講,對 Vue3.0 的更新計劃、方向進行了詳細闡述,表示已經放棄使用了 Object.defineProperty,而選擇了使用更快的原生 P

    前言

    不久前,也就是11月14日-16日于多倫多舉辦的 VueConf TO 2018 大會上,尤雨溪發表了名為 Vue3.0 Updates 的主題演講,對 Vue3.0 的更新計劃、方向進行了詳細闡述,表示已經放棄使用了 Object.defineProperty,而選擇了使用更快的原生 Proxy !!

    這將會消除了之前 Vue2.x 中基于 Object.defineProperty 的實現所存在的很多限制:無法監聽 屬性的添加和刪除、數組索引和長度的變更,并可以支持 Map、Set、WeakMap 和 WeakSet!

    做為一個 “前端工程師” ,有必要安利一波 Proxy !!

    什么是 Proxy?

    MDN 上是這么描述的——Proxy對象用于定義基本操作的自定義行為(如屬性查找,賦值,枚舉,函數調用等)。

    官方的描述總是言簡意賅,以至于不明覺厲...

    其實就是在對目標對象的操作之前提供了攔截,可以對外界的操作進行過濾和改寫,修改某些操作的默認行為,這樣我們可以不直接操作對象本身,而是通過操作對象的代理對象來間接來操作對象,達到預期的目的~

    什么?還沒表述清楚?下面我們看個例子,就一目了然了~

     let obj = {
     a : 1
     }
     let proxyObj = new Proxy(obj,{
     get : function (target,prop) {
     return prop in target ? target[prop] : 0
     },
     set : function (target,prop,value) {
     target[prop] = 888;
     }
     })
     
     console.log(proxyObj.a); // 1
     console.log(proxyObj.b); // 0
    
     proxyObj.a = 666;
     console.log(proxyObj.a) // 888

    上述例子中,我們事先定義了一個對象 obj , 通過 Proxy 構造器生成了一個 proxyObj 對象,并對其的 set(寫入) 和 get (讀取) 行為重新做了修改。

    當我們訪問對象內原本存在的屬性時,會返回原有屬性內對應的值,如果試圖訪問一個不存在的屬性時,會返回0 ,即我們訪問 proxyObj.a 時,原本對象中有 a 屬性,因此會返回 1 ,當我們試圖訪問對象中不存在的 b 屬性時,不會再返回 undefined ,而是返回了 0 ,當我們試圖去設置新的屬性值的時候,總是會返回 888 ,因此,即便我們對 proxyObj.a 賦值為 666 ,但是并不會生效,依舊會返回 888!

    語法

    ES6 原生提供的 Proxy 語法很簡單,用法如下:

    let proxy = new Proxy(target, handler);

    參數 target 是用 Proxy 包裝的目標對象(可以是任何類型的對象,包括原生數組,函數,甚至另一個代理), 參數 handler 也是一個對象,其屬性是當執行一個操作時定義代理的行為的函數,也就是自定義的行為。

    Proxy 的基本用法就如同上面這樣,不同的是 handler 對象的不同,handler 可以是空對象 {} ,則表示對 proxy 操作就是對目標對象 target 操作,即:

     let obj = {}
     
     let proxyObj = new Proxy(obj,{})
     
     proxyObj.a = 1;
     proxyObj.fn = function () {
     console.log('it is a function')
     }
    
     console.log(proxyObj.a); // 1
     console.log(obj.a); // 1
     console.log(obj.fn()) // it is a function

    但是要注意的是,handler 不能 設置為 null ,會拋出一個錯誤——Cannot create proxy with a non-object as target or handler!

    要想 Proxy 起作用,我們就不能去操作原來對象的對象,也就是目標對象 target (上例是 obj 對象 ),必須針對的是 Proxy 實例(上例是 proxyObj 對象)進行操作,否則達不到預期的效果,以剛開始的例子來看,我們設置 get 方法后,視圖繼續從原對象 obj 中讀取一個不存在的屬性 b , 結果依舊返回 undefined :

     console.log(proxyObj.b); // 1
     console.log(obj.b); // undefined

    對于可以設置、但沒有設置攔截的操作,則對 proxy 對象的處理結果也同樣會作用于原來的目標對象 target 上,怎么理解呢?還是以剛開始的例子來看,我們重新定義了 set 方法,所有的屬性設置都返回了 888 , 并沒有對某個特殊的屬性(這里指的是 obj 的 a 屬性 )做特殊的攔截或處理,那么通過 proxyObj.a = 666 操作后的結果同樣也會作用于原來目標對象(obj 對象)上,因此 obj 對象的 a 的值也將會變為 888 !

     proxyObj.a = 666;
     console.log( proxyObj.a); // 888
     console.log( obj.a); // 888

    API

    ES6 中 Proxy 目前提供了 13 種可代理操作,下面我對幾個比較常用的 api 做一些歸納和整理,想要了解其他方法的同學可自行去官網查閱 :

    --handler.get(target,property,receiver)

    用于攔截對象的讀取屬性操作,target 是指目標對象,property 是被獲取的屬性名 , receiver 是 Proxy 或者繼承 Proxy 的對象,一般情況下就是 Proxy 實例。

    let proxy = new Proxy({},{
     get : function (target,prop) {
     console.log(`get ${prop}`);
     return 10;
     }
    })
     
    console.log(proxy.a) // get a
     // 10

    我們攔截了一個空對象的 讀取get操作, 當獲取其內部的屬性是,會輸出 get ${prop} , 并返回 10 ;

    let proxy = new Proxy({},{
     get : function (target,prop,receiver) {
     return receiver;
     }
     })
    
    console.log(proxy.a) // Proxy{}
    console.log(proxy.a === proxy) //true
    
    

    上述 proxy 對象的 a 屬性是由 proxy 對象提供的,所以 receiver 指向 proxy 對象,因此 proxy.a === proxy 返回的是 true。

    要注意,如果要訪問的目標屬性是不可寫以及不可配置的,則返回的值必須與該目標屬性的值相同,也就是不能對其進行修改,否則會拋出異常~

    let obj = {};
    Object.defineProperty(obj, "a", {
     configurable: false,
     enumerable: false,
     value: 10,
     writable: false
    });
    
    let proxy = new Proxy(obj,{
     get : function (target,prop) {
     return 20;
     }
    })
    
    console.log(proxy.a) // Uncaught TypeError

    上述 obj 對象中的 a 屬性不可寫,不可配置,我們通過 Proxy 創建了一個 proxy 的實例,并攔截了它的 get 操作,當我們輸出 proxy.a 時會拋出異常,此時,如果我們將 get 方法的返回值修改跟目標屬性的值相同時,也就是 10 , 就可以消除異常~

    --handler.set(target, property, value, receiver)

    用于攔截設置屬性值的操作,參數于 get 方法相比,多了一個 value ,即要設置的屬性值~

    在嚴格模式下,set方法需要返回一個布爾值,返回 true 代表此次設置屬性成功了,如果返回false且設置屬性操作失敗,并且會拋出一個TypeError。

    let proxy = new Proxy({},{
     set : function (target,prop,value) {
     if( prop === 'count' ){
     if( typeof value === 'number'){
     console.log('success')
     target[prop] = value;
     }else{
     throw new Error('The variable is not an integer')
     }
     }
     }
    })
     
     proxy.count = '10'; // The variable is not an integer
     
     proxy.count = 10; // success

    上述我們通過修改 set方法,對 目標對象中的 count 屬性賦值做了限制,我們要求 count 屬性賦值必須是一個 number 類型的數據,如果不是,就返回一個錯誤 The variable is not an integer,我們第一次為 count 賦值字符串 '10' , 拋出異常,第二次賦值為數字 10 , 打印成功,因此,我們可以用 set 方法來做一些數據校驗!

    同樣,如果目標屬性是不可寫及不可配置的,則不能改變它的值,即賦值無效,如下:

    let obj = {};
    Object.defineProperty(obj, "count", {
     configurable: false,
     enumerable: false,
     value: 10,
     writable: false
    });
    
    let proxy = new Proxy(obj,{
     set : function (target,prop,value) {
     target[prop] = 20;
     }
    })
    
    proxy.count = 20 ;
    console.log(proxy.count) // 10
    
    

    上述 obj 對象中的 count 屬性,我們設置它不可被修改,并且默認值,我們給定為 10 ,那么即使給其賦值為 20 ,結果仍舊沒有變化!

    --handler.apply(target, thisArg, argumentsList)

    用于攔截函數的調用,共有三個參數,分別是目標對象(函數)target,被調用時的上下文對象 thisArg 以及被調用時的參數數組 argumentsList,該方法可以返回任何值。

    target 必須是是一個函數對象,否則將拋出一個TypeError;

    function sum(a, b) {
     return a + b;
    }
    
    const handler = {
     apply: function(target, thisArg, argumentsList) {
     console.log(`Calculate sum: ${argumentsList}`); 
     return target(argumentsList[0], argumentsList[1]) * 10;
     }
    };
    
    let proxy = new Proxy(sum, handler);
    
    console.log(sum(1, 2)); // 3
    console.log(proxy(1, 2)); // Calculate sum:1,2
     // 6
    
    

    實際上,apply 還會攔截目標對象的 Function.prototype.apply() 和 Function.prototype.call(),以及 Reflect.apply() 操作,如下:

    console.log(proxy.call(null, 3, 4)); // Calculate sum:3,4
     // 14
    
    console.log(Reflect.apply(proxy, null, [5, 6])); // Calculate sum: 5,6
     // 22
    
    

    --handler.construct(target, argumentsList, newTarget)

    construct 用于攔截 new 操作符,為了使 new 操作符在生成的 Proxy對象上生效,用于初始化代理的目標對象自身必須具有[[Construct]]內部方法;它接收三個參數,目標對象 target ,構造函數參數列表 argumentsList 以及最初實例對象時,new 命令作用的構造函數,即下面例子中的 p。

    let p = new Proxy(function() {}, {
     construct: function(target, argumentsList, newTarget) {
     console.log(newTarget === p ); // true
     console.log('called: ' + argumentsList.join(', ')); // called:1,2
     return { value: ( argumentsList[0] + argumentsList[1] )* 10 };
     }
    });
    
    console.log(new p(1,2).value); // 30
    
    

    另外,該方法必須返回一個對象,否則會拋出異常!

    var p = new Proxy(function() {}, {
     construct: function(target, argumentsList, newTarget) {
     return 2
     }
    });
    
    console.log(new p(1,2)); // Uncaught TypeError
    
    

    --handler.has(target,prop)

    has方法可以看作是針對 in 操作的鉤子,當我們判斷對象是否具有某個屬性時,這個方法會生效,典型的操作就是 in ,改方法接收兩個參數 目標對象 target 和 要檢查的屬性 prop,并返回一個 boolean 值。

    let p = new Proxy({}, {
     has: function(target, prop) {
     if( prop[0] === '_' ) {
     console.log('it is a private property')
     return false;
     }
     return true;
     }
    });
    
    console.log('a' in p); // true
    console.log('_a' in p ) // it is a private property
     // false

    上述例子中,我們用 has 方法隱藏了屬性以下劃線_開頭的私有屬性,這樣在判斷時候就會返回 false,從而不會被 in 運算符發現~

    要注意,如果目標對象的某一屬性本身不可被配置,則該屬性不能夠被代理隱藏,如果目標對象為不可擴展對象,則該對象的屬性不能夠被代理隱藏,否則將會拋出 TypeError。

    let obj = { a : 1 };
    
    Object.preventExtensions(obj); // 讓一個對象變的不可擴展,也就是永遠不能再添加新的屬性
    
    let p = new Proxy(obj, {
     has: function(target, prop) {
     return false;
     }
    });
    
    console.log('a' in p); // TypeError is thrown
    
    

    數據綁定

    上面介紹了這么多,也算是對 Proxy 又來一個初步的了解,那么我們就可以利用 Proxy 手動實現一個極其簡單數據的雙向綁定(Object.defineProperty() 的實現方式可以參考我上篇文章的末尾有涉及到)~

    主要看功能的實現,所以布局方面我就隨手一揮了~

    頁面結構如下:

    <!--html-->
    <div id="app">
     <h3 id="paragraph"></h3>
     <input type="text" id="input"/>
    </div>
    

    主要還是得看邏輯部分:

    //獲取段落的節點
    const paragraph = document.getElementById('paragraph');
    //獲取輸入框節點
    const input = document.getElementById('input');
     
    //需要代理的數據對象
    const data = {
     text: 'hello world'
    }
    
    const handler = {
     //監控 data 中的 text 屬性變化
     set: function (target, prop, value) {
     if ( prop === 'text' ) {
     //更新值
     target[prop] = value;
     //更新視圖
     paragraph.innerHTML = value;
     input.value = value;
     return true;
     } else {
     return false;
     }
     }
    }
    
    //添加input監聽事件
    input.addEventListener('input', function (e) {
     myText.text = e.target.value; //更新 myText 的值
    }, false)
    
    //構造 proxy 對象
    const myText = new Proxy(data,handler);
    
    //初始化值
    myText.text = data.text; 
    
    

    上述我們通過Proxy 創建了 myText 實例,通過攔截 myText 中 text 屬性 set 方法,來更新視圖變化,實現了一個極為簡單的 雙向數據綁定~

    總結

    說了這么多 , Proxy 總算是入門了,雖然它的語法很簡單,但是要想實際發揮出它的價值,可不是件容易的事,再加上其本身的 Proxy 的兼容性方面的問題,所以我們實際應用開發中使用的場景的并不是很多,但不代表它不實用,在我看來,可以利用它進行數據的二次處理、可以進行數據合法性的校驗,甚至還可以進行函數的代理,更多有用的價值等著你去開發呢~

    況且,Vue3.0 都已經準備發布了,你還不打算讓學習一下?

    聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com

    文檔

    初探Vue3.0 中的一大亮點Proxy的使用

    初探Vue3.0 中的一大亮點Proxy的使用:前言 不久前,也就是11月14日-16日于多倫多舉辦的 VueConf TO 2018 大會上,尤雨溪發表了名為 Vue3.0 Updates 的主題演講,對 Vue3.0 的更新計劃、方向進行了詳細闡述,表示已經放棄使用了 Object.defineProperty,而選擇了使用更快的原生 P
    推薦度:
    • 熱門焦點

    最新推薦

    猜你喜歡

    熱門推薦

    專題
    Top
    主站蜘蛛池模板: 国产成人综合久久精品尤物| 天天爽夜夜爽夜夜爽精品视频| 国产午夜亚洲精品国产成人小说 | 99精品福利国产在线| 亚洲国产另类久久久精品黑人 | 午夜精品久视频在线观看| 亚洲AV无码久久精品蜜桃| 精品精品国产理论在线观看| 欧美日韩在线亚洲国产精品| 99爱在线精品免费观看| 人妻少妇偷人精品无码| 亚洲精品亚洲人成在线观看下载| 久热精品人妻视频| 久久91这里精品国产2020| 国产精品永久免费| 成人午夜精品亚洲日韩 | 大伊香蕉精品视频在线导航| 国语精品一区二区三区| 日韩人妻无码精品一专区| 最新国产在线精品观看| 天天视频国产精品| 拍国产乱人伦偷精品视频| 精品人妻少妇一区二区三区| 国产精品高清在线| 国产精品内射婷婷一级二| 91精品婷婷国产综合久久| 国产精品精品自在线拍| 精品一区二区三区波多野结衣| 亚洲精品岛国片在线观看| 久久无码人妻精品一区二区三区 | 一本一本久久a久久精品综合麻豆| 国产精品久久久久乳精品爆| 热久久这里只有精品| 亚洲国产精品无码中文字| 亚洲av永久无码精品古装片| 无码精品前田一区二区| 久久精品国产一区二区电影| 国内精品久久久久久久影视麻豆| 国产精品五月天强力打造| 日韩精品无码久久一区二区三 | 四虎影视永久在线观看精品|