InversifyJSをシンプルに使ってみる

2022.03.10

InversifyJSはおそらくJS/TSで一番使われているDIコンテナライブラリだと思います。

こちら、かなり機能が多いのですが、

  • toConstantValue()
  • toDynamicValue()
  • toDynamicValue().inSingleScope()

を押さえて使い分ければ大体のユースケースに対応できるのでは?という気がしたので紹介します。

なお、個人的な好みによりDecoratorは使わない方向とします。

環境

  • node 16.14.0
  • inversify 6.0.1

toConstantValue()

toConstantValue()は一番シンプルに値を登録する方法です。

import { Container } from 'inversify'

const container = new Container()

container.bind('CONSTANT').toConstantValue('Something')

const gotValue = container.get('CONSTANT')
console.log(gotValue) // => 'Something' を出力

'CONSTANT' というキーに 'Something' という文字列の値を登録して取得する例です。メソッドの名前の通り定数をセットするような感じです。

toDynamicValue()

toDynamicValue()は値を返す関数を登録します。登録した関数は値を取得する際に実行されるため、遅延評価の登録方法といえそうです。

import { Container } from 'inversify'

const container = new Container()

container.bind('DYNAMIC').toDynamicValue(() => {
  return 'Something'
})

const gotValue = container.get('DYNAMIC')
console.log(gotValue) // => 'Something' を出力

また、toDynamicValue()は他の登録値を取得して値を返すといったことができます。引数の contextcontainer プロパティがあるので、そこから get() メソッドを使用することができます。

container.bind('DYNAMIC_1').toDynamicValue(() => {
  return 'Something'
})

container.bind('DYNAMIC_2').toDynamicValue((context) => {
  return context.container.get('DYNAMIC_1')
})

このようにDYNAMIC_1に依存するDYNAMIC_2の値を解決することができます。

そして、toDynamicValue()は取得される度に関数が実行されます。次に例を示します。

container.bind('DYNAMIC').toDynamicValue(() => {
  console.log('Run toDynamicValue callback') // デバッグのために追加
  return 'Something'
})

const gotValue = container.get('DYNAMIC') // => 'Run toDynamicValue callback' を出力
console.log(gotValue) // => 'Something' を出力

container.get('DYNAMIC') // => 'Run toDynamicValue callback' を出力
container.get('DYNAMIC') // => 'Run toDynamicValue callback' を出力

デバッグのために関数内に console.log('Run toDynamicValue callback') を仕込みました。この状態で container.get('DYNAMIC') を3度行うと 'Run toDynamicValue callback' が3行出力されます。つまり3回関数が実行されています。

この点、1度目の取得で値をキャッシュして、2度目の取得以降はキャッシュ値を返したいニーズもあると思います。そのような場合は次に紹介するtoDynamicValue().inSingleScope()が使えます。

toDynamicValue().inSingleScope()

上のサンプルにinSingletonScope()を追加したコードが下記です。

import { Container } from 'inversify'

const container = new Container()

container
  .bind('DYNAMIC')
  .toDynamicValue(() => {
    console.log('Run toDynamicValue callback')
    return 'Something'
  })
  .inSingletonScope() // 追加

const gotValue = container.get('DYNAMIC') // => 'Run toDynamicValue callback' を出力
console.log(gotValue) // => 'Something' を出力

container.get('DYNAMIC') // => 出力なし
container.get('DYNAMIC') // => 出力なし

このようにしてあげると、2度目の取得以降は 'Run toDynamicValue callback' を出力しないため、関数が一度しか実行されていないことがわかります。

まとめ

3つのAPIを紹介しましたが、この中でも特に toDynamicValue().inSingleScope() を多用すれば

  • 不要なインスタンス生成を減らせる
  • 2度目の取得以降はキャッシュを返す

という点からパフォーマンス面で良いのではないかと思いました。

参考になれば幸いです。

参考