9
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

ラクスAdvent Calendar 2021

Day 12

フロントエンドでDIするときのインスタンス管理方法について調べてみた

Last updated at Posted at 2021-12-11

この記事はラクスAdvent Calendar 2021 12日目の記事です。

はじめに

Vue.js のプロジェクトをリファクタリングする際、DIする機会がありました。
バックエンドだと SpringBoot でのDIは経験がありますが、フロントエンドでは初めてで、インスタンスのライフタイム、特にシングルトンのインスタンスはいつリセットされるのか(ページをリロードするまで?ブラウザキャッシュを捨てるまで?)、気になったので検証してみました。

正直やる前からなんとなく思っていたとおりの結果だったので溜め込んでいたのですが、アドベントカレンダーはいい機会なので供養します。

環境

DIコンテナライブラリには TSyringe を利用しました。
主要なライブラリは以下のバージョンで検証しています。

  • TSyringe: 4.6.0
  • Vue.js: 2.6.14
  • TypeScript: 4.1.5

検証

他のDIコンテナライブラリ、例えば SpringBoot ではsingletonprototyperequestsessionのスコープを指定できますが、ここではもともとの興味の対象であるシングルトンに絞って検証します。
シングルトンでカウンタを生成し、カウントが引き継がれるかどうかで確認します。

検証コード

検証に利用したコードを記載します。
詳しい説明は省略しますが、TSyringe では以下のようにシングルトンなインスタンスを取得します。

  • クラスに@singleton()アノテーションをつける
  • container.resolve()でDIコンテナよりインスタンスを取得
import { singleton } from "tsyringe";

@singleton()
export class Counter {
  count: number;
  constructor() {
    this.count = 0;
  }

  countUp(): void {
    this.count++;
  }
}
<template>
  <div>
    <div>
      <div>count1: {{ instance1.count }}</div>
      <div>&nbsp;</div>
      <button @click="countUp1">countUp</button>
    </div>
    <div>
      <div>count2: {{ instance2.count }}</div>
      <div>&nbsp;</div>
      <button @click="countUp2">countUp</button>
    </div>
  </div>
</template>

<script lang="ts">
import Vue from "vue";
import { container } from "tsyringe";
import { Counter } from "@/di/tsyringe";

export default Vue.extend({
  data() {
    return {
      instance1: container.resolve(Counter),
      instance2: container.resolve(Counter),
    };
  },
  methods: {
    countUp1() {
      this.instance1.countUp();
    },
    countUp2() {
      this.instance2.countUp();
    },
  },
});
</script>

結果

検証コードによる動作で以下の内容が確認できました。

  • instance1, instance2 のカウントが同じ(=シングルトンになっている)
  • instance1, instance2 ともに、ページリロードするとカウントがリセットされる

リロードでリセットされることから、おそらくオンメモリでインスタンスを管理しているのだろうと思って TSyringe のソースを読むと シンプルに Map で実装されていました。
https://github.com/microsoft/tsyringe/blob/master/src/registry-base.ts

補足:バックエンドで検証

ちなみにバックエンドではどうなるのか、オンメモリでインスタンスを管理していることから結果は見えていますが、 Express でサーバーを立てて検証してみました。検証には以下のコードを使いました。

import { container } from "tsyringe";
import { Counter } from "@/di/tsyringe";

router.get('/counts/tsyringe', (req: express.Request, res: express.Response, next: express.NextFunction) => {
    const counter = container.resolve(Counter);
    counter.countUp();
    res.json({"count": counter.count});
});

検証コードによる動作確認では、やはり想定通りの挙動になっていました。

  • サーバ起動中はアクセスするたびにカウントが増える(=シングルトンになっている)
  • サーバ再起動するとカウントがリセットされる

まとめ

TSyringe のDIコンテナはオンメモリなので、シングルトンのインスタンスは「フロントエンドだとページリロード」、「バックエンドだとサーバ再起動」でリセットされる。
また他のDIコンテナライブラリ(injection-jsInversifyJS)もざっとソースを読んだ限りでは、TSyringe 同様オンメモリで管理しているようでした。

9
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?