この記事はラクス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 ではsingleton
・prototype
・request
・session
のスコープを指定できますが、ここではもともとの興味の対象であるシングルトンに絞って検証します。
シングルトンでカウンタを生成し、カウントが引き継がれるかどうかで確認します。
検証コード
検証に利用したコードを記載します。
詳しい説明は省略しますが、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> </div>
<button @click="countUp1">countUp</button>
</div>
<div>
<div>count2: {{ instance2.count }}</div>
<div> </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-js、InversifyJS)もざっとソースを読んだ限りでは、TSyringe 同様オンメモリで管理しているようでした。