Vue 3.0のProxyを簡単に理解


目次
  • Proxy
  • Vue 2.0使用Object.defineProperty()データ応答の実現
  • Vue 3.0使用Proxy
  • Proxyの他のアプリケーション
  • Proxy
    エージェントとは、オブジェクトの前に「ブロック」を設定し、そのオブジェクトがアクセスされると、このレイヤを通過しなければならないと理解できます.このブロックでさまざまな操作を行うことができることを意味します.たとえば、このレイヤブロックで元のオブジェクトを処理し、戻りたいデータ構造を返すことができます.
    ES 6は、Proxyコンストラクション関数を原生的に提供し、MDNでは、Proxyオブジェクトが基本操作を定義するためのカスタム動作(属性検索、付与、列挙、関数呼び出しなど)として解釈されます.
    まず使い方を見てみましょう.
    const p = new Proxy(target, handler);
  • target:ブロックする対象オブジェクト(オリジナル配列、関数、さらには別のエージェントを含む任意のタイプのオブジェクト)
  • handler:1つのオブジェクト、ブロックする行為を定義する
  • const p = new Proxy({}, {
        get(target, propKey) {
            return '  ,      ';
        }
    });
    
    console.log(p.name);
    //   ,      

    注意Proxyはオブジェクトを操作するために使用されます.エージェントの目的は、オブジェクトの能力を拡張することです.
    もう1つの例を見ると、オブジェクトのnameプロパティを外部で変更することはできません.
    const p = new Proxy({}, {
        set(target, propKey, value) {
            if (propKey === 'name') {
                throw new TypeError('name       ');
            }
            //    name   ,    
            target[propKey] = value;
        }
    });
    p.name = 'proxy';
    // TypeError: name       
    p.a = 111;
    console.log(p.a); // 111

    babelは、新しいAPI(Array.from、Array.prototype.includesなど)のように、構文を変換するために使用されます.[core-js/stable]()や[regenerator-runtime/runtime](PS:babel 7.x以降@babel/polyfillは推奨されていません)など、追加のパッケージをインストールしてサポートする必要があります.その後、API(String#normalize、Proxy、fetchなど)もあります.core-js中はしばらくpolyfillを提供していないので、具体的には公式文書を見ることができます
    core-js#missing-polyfills . Proxyサポートされているブロック操作は全部で13種類あり、詳細は閲覧可能MDN.
    vue 2.xはどのようにデータの応答を実現しますか?
    dataのデータを再帰的に巡回し、Object.defineProperty()getterとsetterをハイジャックし、getterでデータ依存収集処理を行い、setterでデータの変化を傍受し、現在のデータを購読する場所を通知する.部分ソースsrc/core/observer/index.js#L 156-L 193、バージョン2.6.11は以下の通り
    let childOb = !shallow && observe(val)
     //   data          ,             
      Object.defineProperty(obj, key, {
        enumerable: true,
        configurable: true,
        get: function reactiveGetter () {
          const value = getter ? getter.call(obj) : val
          if (Dep.target) {
             //       
            dep.depend()
            if (childOb) {
              childOb.dep.depend()
              if (Array.isArray(value)) {
                //                    ,           ,   。
                dependArray(value)
              }
            }
          }
          return value
        },
        set: function reactiveSetter (newVal) {
          const value = getter ? getter.call(obj) : val
          /* eslint-disable no-self-compare */
          if (newVal === value || (newVal !== newVal && value !== value)) {
            return
          }
          /* eslint-enable no-self-compare */
          if (process.env.NODE_ENV !== 'production' && customSetter) {
            customSetter()
          }
          if (getter && !setter) return
          if (setter) {
            setter.call(obj, newVal)
          } else {
            val = newVal
          }
          //          observe,       
          childOb = !shallow && observe(newVal)
          //              
          dep.notify()
        }
      })

    このようにするのはどんな問題がありますか.
  • オブジェクト属性の追加と削除が検出されない:オブジェクトに属性を追加した場合newProperty現在追加されているこの属性は、vue検出データ更新のメカニズムを追加していない(初期化後に追加されたため).vue.$setあなたが属性を追加したことをvueに知らせることができます.それはあなたに処理します.$set内部も呼び出しです.Object.defineProperty()処理します.
  • 配列の下付き文字の変化をモニタできず、直接配列の下付き文字で配列に値を設定し、リアルタイムで応答できない.
  • data中のデータが多く、階層が深い場合、data中のすべてのデータを遍歴して応答式に設定するため、パフォーマンスに問題があります.

  • vue 3.0はProxyを使用しています
    vue 3.0はまだ正式にリリースされていませんが、vue-nextの関連コードはすでにオープンソースで、現在Alphaバージョンにあります.
    どうしてProxyを使って上記の問題を解決できるのですか?主にProxyはブロック対象であるため、 に対して「ブロック」を行い、外部からのアクセスは、まずこのレベルでブロックしなければならない.オブジェクトにアクセスするプロパティにかかわらず、以前に定義されたプロパティも追加されたプロパティもブロックに移動します.
    簡単な
    以下、それぞれObject.defineProperty() Proxyで簡単なデータ応答を実現
    使用Object.defineProperty()実装:
    class Observer {
        constructor(data) {
            //     data   ,    this 
            for(let key of Object.keys(data)) {
                if(typeof data[key] === 'object') {
                    data[key] = new Observer(data[key]);
                }
                Object.defineProperty(this, key, {
                    enumerable: true,
                    configurable: true,
                    get() {
                        console.log('    ' + key);
                        return data[key]; //               ,       ;
                    },
                    set(newVal) {
                        console.log('    ' + key);
                        console.log('  ' + key + '=' + newVal);
                        if(newVal === data[key]) {
                            return;
                        }
                        data[key] = newVal;
                    }
                })
            }
        }
    }
    
    const obj = {
        name: 'app',
        age: '18',
        a: {
            b: 1,
            c: 2,
        },
    }
    const app = new Observer(obj);
    app.age = 20;
    console.log(app.age);
    app.newPropKey = '   ';
    console.log(app.newPropKey);

    上のコードの実行結果は
    //    obj      age   
        age
      age=20
        age
    20
    //         
       

    オブジェクトに属性が追加され、内部では傍受されていないことがわかります.追加された属性は手動で再使用する必要がありますObject.defineProperty()傍受を行います.その理由vue 2.xではオブジェクト属性の追加と削除が検出されないため、内部提供$setは呼び出しObject.defineProperty()で処理される.
    以下、使用Proxy代替Object.defineProperty()実装
    const obj = {
        name: 'app',
        age: '18',
        a: {
            b: 1,
            c: 2,
        },
    }
    const p = new Proxy(obj, {
        get(target, propKey, receiver) {
            console.log('    ' + propKey);
            return Reflect.get(target, propKey, receiver);
        },
        set(target, propKey, value, receiver) {
            console.log('    ' + propKey);
            console.log('  ' + propKey + '=' + value);
            Reflect.set(target, propKey, value, receiver);
        }
    });
    p.age = '20';
    console.log(p.age);
    p.newPropKey = '   ';
    console.log(p.newPropKey);

    次の出力が表示されます
    //       age  
        age
      age=20
        age
    20
    
    //       
        newPropKey
      newPropKey=   
        newPropKey
       
    Proxyオブジェクトに対する操作であり、オブジェクトにアクセスすればProxyの論理に進むため、新しい属性は、応答式処理を再追加する必要はないことがわかります.
    Reflect(ES 6導入)は、JavaScript操作をブロックする方法を提供する内蔵オブジェクトです.Objectオブジェクトのいくつかを明らかに言語の内部の方法に属します(例えばObject.defineProperty())Reflect対象上.いくつかのObjectメソッドの戻り結果を修正し、より合理的にします.Object操作を関数動作にします.詳細の表示
    MDN
    Proxyのその他の応用
    まもなくリリースされるvue 3.0他に、どのようなライブラリが使用されているProxyでしょうか.
  • dobjs/dobmobxをproxyで書き換える一案です.
  • immer可変データ型を実現する.immerのやり方はstateを内部で維持し、すべての操作をハイジャックし、内部で変化があるかどうかを判断し、最終的にどのように戻るかを決定することであり、具体的な内容はmmer.jsの概要とソースコードの概要この文章を見ることができる.

  • いずれも、オブジェクトを読み書きブロックし、読み書きに追加の判断と操作を行うために使用されます.
    まとめ
  • Proxyオブジェクトを操作するための、オブジェクトを広げる能力です.Object.defineProperty()オブジェクト属性の操作です.
  • vue2.x使用Object.defineProperty()データの応答式を実現するがObject.defineProperty()オブジェクト属性に対する操作であるため、オブジェクトに対して深さ遍歴して属性を操作する必要がある.
  • vue3.0Proxyオブジェクトをブロック操作し、オブジェクトに対してどのような操作をしてもProxyの処理ロジックに進む
  • vue3.0dobjs/dobimmerなどのライブラリは現在使用されているProxy対象を読み書きブロックし、追加の処理を行う.

  • リファレンス
  • しんとうおうとうしきのげんり
  • リストレンダリング
  • ECMAScript 6入門-Proxy
  • MDN-proxy
  • 面接官:双方向バインドProxyの実現はdefinepropertyより優劣はどうですか。
  • 申し訳ありませんが、Proxyをマスターするのは本当にやりたいことができます。
  • immer.jsの概要とソースコードの概要