弱マップを用いたVanillajsにおける依存性注入



あなたは角度のような現代のフレームワークのおかげで依存関係の注入alotの用語に遭遇します.ほとんどの実装では、DEVSはパラメータ型を取得するためにリフレクションを使用します.でもreflect-metadata NPMパッケージ自体は、外部コードの任意の1行なしで300 KBを量ります.また、採点の場合、反射は働かない.ボード上のすべてのこれらの問題で、単一の外部依存関係なしでビルトインのJavaScript機能を使用している単純なDIを実行しましょう.
十分な話はいくつかのコードを書くことから始めましょう.
一般的に現代のフレームワーク用語では、依存関係はProvider or Service . 単純さのために、我々は我々のFucntionとして我々を呼びましょうService .
//service.js
const Service = (klass) => {
  const instance = new klass();
  console.log(instance);
}
export { Service };
簡単なpeasy.次のように実行します.
// index.js
import { Service } from './service';

class ServiceA {
  greet() {
    console.log('hello world');
  }
}

Service(ServiceA);
Serviceaのインスタンスを表示します.何も空想.
現在、本物のものは絵に来ます.
このインスタンスを後でアクセスするために必要な場所は?
可能性を見ましょう
  • クラス名を識別子として使う.これは動作しますが、Minified後に動作しません.それで、あなたは束サイズを犠牲にしなければなりません.
  • インスタンスを保存する識別子として文字列を使用するMap() . よく、これはうまくいきます、しかし、それはあまりに多くのコーディングを終えます.そしてMap 何も消費しないが、オブジェクトを保存します.
  • では、解決策は?

    弱点へようこそ
    はい、インスタンスを保存するために弱マップを活用します.では、Injector クラスウィズregister , getService and clear メソッド.
    // injector.js
    const Injector = new class {
      register() {}
    
      getService() {}
    
      clear() {}
    }
    export { Injector };
    
    恐ろしい.これをサービスファイルにインポートしましょう.
    //service.js
    import { Injector } from './injector';
    
    const Service = (klass) => {
      const instance = new klass();
      console.log(instance);
      Injector.register();
    }
    export { Service };
    
    ハフインジェクターは、インスタンスを保存していません.registerメソッドを実装しましょう.
    // injector.js
    const Injector = new class {
      #weakmap = new WeakMap()
      register(klass, instance) {
        if(!this.#weakmap.has(klass)) {
          this.#weakmap.set(klass, instance);
        }
      }
    
      getService() {}
    
      clear() {}
    }
    export { Injector };
    
    わかりました.説明しましょう.みんな知ってるWeakMap() キーとしてオブジェクトを必要としないMap() . したがって、ここでのキーはクラスのクラスと値です.きちんとした?インスタンスを保存する名前を避ける方法を参照してください?クールライト😎
    では、サービスファイルに戻りましょう.
    //service.js
    import { Injector } from './injector';
    
    const Service = (klass) => {
      const instance = new klass();
      Injector.register(klass, instance);
    }
    export { Service };
    
    グレート.次に、依存性注入はどこにあります.Woahhは、続けます.私はその部分に来ます.
    つのクラスを作成しましょう.
    // index.js
    class ServiceA {
       getGreeting() {
         return 'hello world';
       }
    }
    
    class ServiceB {
      serviceA;
      constructor(_serviceA) {
         this.serviceA = _serviceA;
      }
    
      greet() {
        return this.serviceA.getGreeting();
      }
    }
    
    Service(ServiceA);
    Service(ServiceB);
    
    あなたが上記のロジックを実行して、ログserviceB.greet() あなたはServiceaが言っているエラーを得るでしょうundefined .
    はい.次に、依存関係としてサービスを提供する方法.我々のサービス実施を再訪問しましょう.
    //service.js
    import { Injector } from './injector';
    
    const DEFAULT_SERVICE_OPTIONS = {
       deps: []
    }
    
    const Service = (...args) => {
      let options = DEFAULT_SERVICE_OPTIONS;
      let klass;
      if(args[0].hasOwnProperty('deps')) {
         options = args[0];
         klass = args[1];
      } else {
         klass = args[0];
      }
      const instance = new klass();
      Injector.register(klass, instance);
    }
    export { Service };
    
    それは一口だ.OK私たちがしようとしているのは、サービスの実装の引数をチェックすることです
  • Service(someClassA) 依存関係がない場合.
  • Service({ deps: [someClassA] }, someClassB) 依存関係が記載されている場合.
  • あなたが第2のユースケースを観察するならば、我々は実はSoleclassa自体の青写真を文字列でないか、クラスを使用していないかを通過しています.名前.クラスの青写真を使用してインスタンスを保存していることを覚えておいてください.それがDEPSに青写真を渡している理由です.
    大丈夫.依存関係を持ちましたが、ターゲットクラスのインスタンスを作成する前にインスタンスを取得しなければなりません.上記インジェクターで.我々が残したJSファイルgetService 空白としてのメソッド.ちょっとばかにしましょう.
    // injector.js
    const Injector = new class {
      #weakmap = new WeakMap()
      register(klass, instance) {
        if(!this.#weakmap.has(klass)) {
          this.#weakmap.set(klass, instance);
        }
      }
    
      getService(klass) {
        return this.#weakmap.get(klass);
      }
    
      clear() {}
    }
    export { Injector };
    
    我々が議論したように、我々はクラスの青写真をインジェクタに渡し、そのインスタンスを取得します.今すぐ戻ってサービスに来る.js
    //service.js
    import { Injector } from './injector';
    
    const DEFAULT_SERVICE_OPTIONS = {
       deps: []
    }
    
    const Service = (...args) => {
      let options = DEFAULT_SERVICE_OPTIONS,
          klass,
          instance;
      const dependencies = [];
    
      if(args[0].hasOwnProperty('deps')) {
         options = args[0];
         klass = args[1];
      } else {
         klass = args[0];
      }
    
      // loop all the deps if any and get their instance from Injector.
      // else simply create new instance.
      if(options.deps.length) {
          for(const dependency of options.deps) {
              dependencies.push(Injector.getService(dependency));
          }
          instance = new klass(...dependencies);
      } else {
        instance = new klass();
      }
      Injector.register(klass, instance);
    }
    export { Service };
    
    当社のサービスで.JSファイルは、我々が通過しているすべての依存関係をループしており、それらのインスタンスをインジェクタから取得しています.ではインデックスを見てみましょう.js
    // index.js
    import { Service } from './service';
    import { Injector } from './injector';
    
    class ServiceA {
       getGreeting() {
         return 'hello world';
       }
    }
    
    class ServiceB {
      serviceA;
      constructor(_serviceA) {
         this.serviceA = _serviceA;
      }
    
      greet() {
        return this.serviceA.getGreeting();
      }
    }
    
    Service(ServiceA);
    Service({ deps: [ServiceA] }, ServiceB);
    console.log(Injector.getService(ServiceB).greet());
    
    コンソールの前の最後の行を観察しましたか.正しい.上で述べたように、ServiceaのBluePrintをServiceBに依存しているのです.今、我々がこれを実行するとき、我々はServiceaに保存されたServicebから「Hello World」メッセージを見るでしょう.
    今我々は、インジェクタのすべてのサービスインスタンスをクリアすることができますclear メソッド.
    // injector.js
    const Injector = new class {
      #weakmap = new WeakMap()
      register(klass, instance) {
        if(!this.#weakmap.has(klass)) {
          this.#weakmap.set(klass, instance);
        }
      }
    
      getService(klass) {
        return this.#weakmap.get(klass);
      }
    
      clear() {
        this.#weakmap = new WeakMap();
      }
    }
    export { Injector };
    
    そうです.私たちは依存インジェクションのためにどんな外部ライブラリも使用しませんでした、また、反射を使用して、識別子としてどんなストリングも使用しませんでした.汚れたきれいな権利?あなたはさらにこれを改善することができます.
    以下にDIの実装を示します.
    タイプスクリプトでは、リフレクションを使用してコンストラクタパラメーターの型を読み取ることができます.しかし、それはesbuild、vite、小包などのような新しいバンドルと素敵に再生されません.このメタデータを保存しないでください.また、プロパティ名/クラス名をすべて削除します.したがって、メタデータ/クラス名に頼ることは賢明ではありません.
    希望これはVanillajsであなた自身のDIを作成するのに役立ちます.Vanillajs私たちはめったに使用しない機能がたくさんあります.最新のECMAScript機能を持っているより多くのブラウザーの出現で、フレームワークはより冗長になっています.同じロジックを使用してtypescriptを使用すると、タイプチェックの腕前を与える.
    この実装は、ミニミニ化されたバージョンにかかわらず動作します.既存のバンドルのいずれかで安全に使用できます.そして、おそらくこれはJavaScriptの弱マップを使用する方法で最高のusecaseです😉).
    私は個人的に自分のフレームワークで上記のDIロジックを使用しましたPlumeJS WebComponentとTypesScriptの上に構築されます.あなたがそれに貢献できるかどうかを確認してください😉
    閉じるこの動画はお気に入りから削除されています.😊
    次の記事でお会いしましょう.
    キラン👋