Vueでのprovide/injectの応用について

5051 ワード

周知のように,コンポーネント式開発において,最大の痛みはコンポーネント間の通信にある.Vueでは、基本的なprops/$emitから兄弟コンポーネント通信用のEventBus、グローバルデータ管理用のVuexまで、さまざまなコンポーネント通信方式が提供される.
このような多くのコンポーネント通信方式ではprovide/injectは非常にアカリンに見える(存在感がない).しかし、provide/injectにもその活躍の場がある.今日は、Vueのprovide/injectの応用についてお話しします.
Provide/injectとは
Provide/injectはVueが2.2.0バージョンで追加したAPIで、公式サイトでは以下のように紹介されています.
このオプションは、コンポーネント階層がどれだけ深く、上下関係が成立した時間内に常に有効になるかにかかわらず、祖先コンポーネントがすべての子孫子孫に依存を注入できるように、一緒に使用する必要があります.Reactに詳しい場合は、Reactのコンテキスト特性に似ています.
公式サイトの解釈はとても人を困惑させて、それでは私はこのいくつかの言叶を訳します:
Provideは、祖先コンポーネントで子孫コンポーネントに提供したいデータまたはメソッドを指定できますが、どの子孫コンポーネントでもinjectを使用してprovideが提供したデータまたはメソッドを受信できます.
公式サイトを挙げます.
//        'foo'
var Provider = {
  provide: {
    foo: 'bar'
  },
  // ...
}

//       'foo'
var Child = {
  inject: ['foo'],
  created () {
    console.log(this.foo) // => "bar"
  }
  // ...
}

親コンポーネントが提供するfoo変数は、コンポーネントが正常に受信され、使用されていることがわかります.
Provide/injectが何であるかを理解したら、provide/injectを使用します.
provide/injectを使用したグローバルステータス管理
日常の开発の中で、私达はいつもVuexを使って状态の管理をして、しかし、私个人はずっとVuexを使うのが好きではありませんて、原因はVuexが状态を维持するために追迹されることができて、使うのが面倒すぎるためです;私が以前参加したプロジェクトは、多くの人が協力していませんでした.この機能は私にとって、意味がありません.私はVuexでグローバルな状態を提供する機能だけが必要です.
では、グローバルな状態を簡単かつ迅速に実現する方法はありますか?もちろんありますが、これがprovide/injectというブラックテクノロジーAPIの使い方です.
ルートコンポーネントで変数を入力し、子孫コンポーネントで使用する方法を考える人が多いかもしれません.
//                   
export default {
  provide () {
    return {
      text: 'bar'
    }
  }
}

//        'app'


  export default {
    inject: ['text'],
    created() {
      this.text = 'baz' //     ,     'bar'
    }
  }

この考えは、正しいと言っても正しいし、間違っても間違っていない.原因はprovideの特殊性にある.
公式サイトのドキュメントでprovide/injectについて次のようなヒントがあります.
ヒント:provideおよびinjectバインディングは応答可能ではありません.これはわざとやったのだ.しかし、リスニング可能なオブジェクトが送信された場合、そのオブジェクトのプロパティは応答可能です.
すなわち,Vueはprovide中の変数を応答的に処理しない.したがって,injectが受け入れる変数が応答式であるためにはprovideが提供する変数自体が応答式である必要がある.
コンポーネント内部の様々な状態が応答可能であるため,我々は直接ルートコンポーネントにコンポーネント自体をprovideに注入するが,この場合,子孫コンポーネントの中でルートコンポーネントのすべての状態に任意にアクセスでき,ルートコンポーネントはグローバル状態のコンテナとなり,Reactのcontextに似ているのではないかとよく考える.
コードは次のとおりです.
//                
export default {
  provide () {
    return {
      app: this
    }
  },
  data () {
    return {
      text: 'bar'
    }
  }
}

//        'app'


  export default {
    inject: ['app'],
    created() {
      this.app.text = 'baz' //     ,   'baz'
    }
  }
   

$rootを使用してルートノードを取得できる場合、provide/injectを使用する必要はありません.
実際の開発では、1つのプロジェクトに複数の人が開発することが多く、一人一人が異なるグローバル変数を必要とする可能性があります.すべての人のグローバル変数がルートコンポーネントに統一的に定義されている場合、変数の衝突などの問題が発生します.
Provide/injectの異なるモジュールのエントリコンポーネントを使用して、それぞれの子孫コンポーネントに渡すことで、この問題を完璧に解決できます.
Provide/injectの使用を慎む
provide/injectがこんなに使いやすい以上、なぜVue公式は原生のAPIではなくVuexを使うことをお勧めしますか?
前述したように、Vuexとprovide/injectの最大の違いは、Vuexのグローバル状態の変更のたびに遡及を追跡できることであり、provide/injectの変数の変更は制御できません.言い換えれば、どのコンポーネントがこのグローバル状態を変更したのか分かりません.
Veeの設計理念はReactにおける一方向データストリームの原則(syncのような一方向データストリームを破壊するやつがいるが)を参考にしているが,provide/injectは一方向データストリームの原則を明らかに破壊している.複数の子孫コンポーネントが1つの祖先コンポーネントが提供するステータスに同時に依存している場合、1つのコンポーネントがステータスを変更すると、すべてのコンポーネントが影響を受けます.この態様は結合度を増加させ,一方,データの変化を制御できないようにした.複数の人が協力して開発すれば、悪夢になるだろう.
ここではprovide/injectを用いたグローバルステータス管理の原則を2つまとめました.
  • 複数人が協力する場合、役割ドメイン隔離
  • を行う.
  • は、できるだけ一括データをグローバル状態
  • として使用する.
    Provide/injectを使ってグローバルステータス管理をするのは危険そうですが、provide/injectの方が良い使い方はありますか?もちろんあります.それはprovide/injectを使用してコンポーネントを作成することです.
    Provide/injectを使用してコンポーネントを記述する
    Provide/injectを使用したコンポーネント開発は、Vueの公式ドキュメントで提唱されている方法です.
    私がよく知っているelementUIを例に挙げます.
    elementUIにはButton(ボタン)コンポーネントがあり、Form(フォーム)コンポーネントで使用すると、そのサイズは外層のFormItemコンポーネントと、より外層のFormコンポーネントのsizeプロパティの両方に影響されます.
    通常のシナリオであれば、propsを使用してFormから属性値を階層的に渡すことができます.2層を渡すだけでいいように見えますが、納得できます.しかし、Formの次のコンポーネントは必ずしもFormItemではなく、FormItemの次のコンポーネントはButtonではなく、他のコンポーネントをネストすることもできます.つまり、階層関係が不確定です.propsを使用すると、私たちが書いたコンポーネントが強く結合する場合があります.
    Provide/injectはこの問題を完璧に解決することができ、コンポーネント自体(コンテキスト)を子孫に注入するだけで、子孫コンポーネントでは階層を無視して祖先コンポーネントの状態に任意にアクセスすることができる.
    ソースの一部は次のとおりです.
    // Button       
    export default {
        name: 'ElButton',
        //    inject    elForm    elFormItem      
        inject: {
            elForm: {
                default: ''
            },
            elFormItem: {
                default: ''
            }
        },
        // ...
        computed: {
            _elFormItemSize() {
                return (this.elFormItem || {}).elFormItemSize;
            },
            buttonSize() {
                return this.size || this._elFormItemSize || (this.$ELEMENT || {}).size;
            },
            //...
        },
        // ...
    };

    まとめ
    実はVueの学習の中で、二八の法則に従って、私たちがよく使う20%のAPIは大部分の日常の問題を解決することができて、残りのAPIはあまり役に立たないと感じます.しかし、時間を割いてそれらの冷たいAPIを理解して、あなたはいくつかの普通ではない風景を発見することができて、あなたにいくつかの問題を解決する時、仕事が半分で倍になります.