Vue.js メモリに大きなデータがあるのを解決する

Vueのバージョンは 2.7.15 です。

Vueで作っているところで、明らかにページに描画される部品が多くなると描画が遅くなると感じられるページがありました。

部品が多くなると、古いPCではページがフリーズしてしまうことも(>_<)

Chromeのデベロッパーツールで調査してみました。

Chrome デベロッパーツール でまずNetworkを確認し、サーバー側などで時間がかかっているわけではないのを確認します。(ここは省略)

Chromeの デベロッパーツール で メモリがどれぐらい使われているか確認します。

下記の画面の、一番左上の二重丸をクリックし、Heap Snapshotというのを取ります。

ででん!

遅いVueをChrome開発者ツールを使ってメモリを調査したところ
ん?配列がいっぱい…?

array というのが105万個もあります。( ゚Д゚) そして、これが約120Mもメモリを使っています。

なんだ…?こいつらッ

105万個という数にヤバさを感じますね!!

これ、クリックすると中身が見れるんですが、見るとあんまり関係ないのもあるので辛抱強く見ていくと、なんか見覚えのあるプロパティ名がずらっと出てたりもします。

で、いくつかのパターンでメモリを取得してみると、このarray というのが増えたり減ったりしていて(ある時は半分の50万個ぐらい)ページの中で繰り返し描画されているコンポーネントの関係があるのでは?と思われました。

で、Vueの$dataってリアクティブなので、こうやってメモリ上に確保されているのかなと思い当たり、繰り返しのあるComponentの$dataに大き目のオブジェクトがあったりしたので、それを使わないようにしてみたら、劇的に改善しました。

遅いVueをChrome開発者ツールを使ってメモリを改善したところ
arrayは175個に

arrayの数は 105万個 →175個になり、メモリも約135Kになりました!桁違いすぎる(笑)

array のすぐ上の、compiled codeというのも劇的に少なくなっています。

結果、ページ速度も改善し、動きがよくなりました!

Vueでメモリをいっぱい使っている、で検索すると、メモリリークの話ばっかり出てくるので、ここに書いておきます。

まれにこのChrome デベロッパーツール でメモリを測定する機能が壊れているというかなんというか、おかしくなる時があります。
そういう時は、Chromeを再起動してみましょう。

そんなにVue.jsに詳しいわけではないので、色々間違っていたらすみません。

WebStorm(PhpStorm)でNodeのあるはずのライブラリが読み込めない

Windows + Docker + Webstorm(PhpStorm)で開発をしていますが、私の個人の環境ではWebStrom上ではずっとmomentが読み込めないよ、というエラーが出たり、Lintのエラーが出たりしていて困ってました。

TS2307: Cannot find module  moment  or its corresponding type declarations.
npm ci 

しても同じ 。
実行は普通にできています。
隣で開発しているプログラマーは大丈夫なのに、なんでだ??

と思ってたんですが、 とはいえ、普通に開発は進められるので、まぁいっかと思いつつ日々を過ごしていました。

ある時意外なことで直ったのでメモしておきます。

SettingsのLanguages &Frameworks > Javascript > Libraries のライブラリに、プロジェクト以下のnode_modulesがマイナスみたいな記号で入ってたんですけど、(下記スクショの赤丸の部分)それを外したらmoment が読み込めるようになりました!

Webstormが参照しているnode_modulesがどこにあるのを使うかという設定がこれだったということでしょうか。

同じようなことでエラーに困っている人がいれば、ご参考までに。

Postman に自動でAPIのレスポンスが正しいかどうかのテストを作ってもらう

Postman、APIなどのテストで使っている方も多いと思います!

すばらしいツールです。いつもお世話になっております!!😿

さて、先日Postmanさんのやっているイベントに行きまして紹介されていた

「APIのテストを自動で作成する」

というのを実践でやってみましたので、手順など含めて書いておきます。

例として、次のようなJSONをPostするとします。

{
	"id": "abc",
	"company_id": 123,
	"requestor_id": "0",
	"events": [
		{
			"id": "def",
			"type": "add",
			"object": {
				"type": "Route",
				"company_id": 123,
				"date": "2023-12-07"
			}
		}
	]
}

上記のPostは本来あるべきdriver_idが欠損しているので、失敗して次のようなレスポンスが返ってくるとします。

{
    "id": "1234",
    "request_id": "abc",
    "status": "failed",
    "error_infos": [
        "driver_idがありません。"
    ]
}

一旦、Postmanから最初のリクエストをPostします。

一旦、送信してResponseが返ってきますと、「Postbot」というのが立ち上がります。立ち上がらない場合は、右下のPostbotというところをクリックしても立ち上がります。

で、Postbotのメニューに「Test for response」というのがありますので、それをクリックすると、テストを作ってくれます。

そして、テスト というタブをクリックすると、なんとこれでテストができています。ありがたすぎるー!!

とはいっても、これが完璧なわけでもなく、エラーメッセージなどのテストはなかったため、追加します。

Postbotさんが提案してくれる 「Add more tests」 というリンクをクリックしてみると、次のテストまで作ってくれます。

pm.test("Error_infos array contains only strings and no null or undefined values", function () {
    const responseData = pm.response.json();
    
    pm.expect(responseData.error_infos).to.be.an('array');
    responseData.error_infos.forEach(function(error) {
        pm.expect(error).to.be.a('string');
        pm.expect(error).to.not.equal(null);
        pm.expect(error).to.not.equal(undefined);
    });
});

ぴええええ 何から何までお世話になっています!

しかし、まぁいうなればエラーメッセージのテストをしておきたいのですが、それは自分で追加する必要がありそうです。

なので、上記のテストに自分で追加します。

pm.expect(responseData.error_infos).to.be.an('array');
responseData.error_infos.forEach(function(error) {
    pm.expect(error).to.be.a('string');
    pm.expect(error).to.not.equal(null);
    pm.expect(error).to.not.equal(undefined);
    pm.expect(error).to.equal('driver_idがありません。'); // 追加
});

でも、自分で一から書くよりずっと楽ですよね!

テストを更新したら、テストが合格するか試したいですよね!
そしたら、次のテスト結果にあるぐるっと回るようなアイコンをクリックします。

これで、再度テストが試せて想定通りの動作をしていることがわかりました。

で、このテストは今後「送信」をクリックすると、自動で実行してくれるのでテストが失敗したか成功したか、常に把握することができます。

ちなみに、私はPostmanさんのMeetupというオフラインのイベントに行って上記の説明を教えて頂き、なんとタダで東京駅のおしゃれな夜景を見ながらおいしいご飯とお酒も頂いちゃいました!!しかもPostmanロゴのかわいいTシャツも!!!!(泣)
なんでそんなに気前がいいんですか(号泣)
もはや自分が乞食で恥ずかしいレベル(*ノωノ)
他のエンジニアさんとも交流が持てますし、オススメでしかありません!

↓にワークショップのリンクを貼っておきますので、興味がある方はぜひどうぞ。

https://postman.connpass.com/

Postmanさんのオフィス
Postmanさんのでらおしゃれなオフィス。東京駅の目の前です

Chromeデベロッパツール js、tsデバッグのちょっと便利な使い方

Chromeの開発者ツール、Web系の開発をしている方なら一度は触ったことがあると思います。

今回は、普通の使い方はもう知ってるよという人向けに、私が個人的に気に入っててちょっと便利な使い方を書いておきます。

①ブレークポイントを一時的に無効にする

サンプルスクリプトは下記です。

    const dog = {name: 'dog', age: 0}
    const monkey = {name: 'monkey', age: 1}
    const cat = {name: 'cat', age: 5}
    const animals = [dog, monkey, cat]
    animals.forEach(animal => {
      if(animal.name === 'monkey') {
        console.log('ウキー')
      }
    })

例えば、下記のような場所にブレークポイントを置いたとして

リロードのたびに実行されるのがなんかイヤだな…ってことがあると思います。

そういう時は、右クリックすると、次のようなメニューが出ますので

Disable breakpoint を選択すると、Enableにするまでこのブレークポイントで止まりません。

私は結構これを使います。

なんで使うかっていうと、ページのリロードって弊社では例えばVue使ってるんですが、コードの更新ごとにリロード走って結構頻繁に起こるんですよね。そのたびにブレークポイントで止まってるとかが面倒だからです。

あとは普通にループの中で、ループの1回目はブレークしたいけど、後はもういいよ…という場合などに使います。

「ブレークポイント自体もう一度クリックしたら消えるじゃん」

という声も聞こえますが、そうすると、またコード直して同じところにブレークポイントを付けたい、という場合にちょっと手間が発生します。

ちょっとの手間、惜しんでいきましょう!!

②ブレークポイントに条件を設定する

ループの中の例えばループの10回目以降だけ発言する不具合がある、などの場合にポチポチステップ実行を押していませんか?

ブレークポイントに条件を設定することができます。

ブレークしたい場所を右クリックしてください。

「Add conditional breacpoint」を選択します。

animal の age が 2より大きい時だけブレークするように設定します。

黄色いクエスチョンマークのブレークポイントができたらOKです。

これで、age > 2のcat の場合のみブレークするようになります。

③すべてのブレークポイントを一時的に無効にする

①で紹介したブレークポイントを一時的に無効にする、ですが、多くのブレークポイントがある場合に、いちいちDisableにしていくのは面倒だと思います。

そんな時に、一度に無効にできる方法があります。

下記の開発者ツールの右のほうにある電池のようなマークをクリックしてください。そうすると、ブレークポイントが一気に一時的に無効にできます。

また一気に有効にしたい場合は、同じマークをクリックすると、一気に有効になります。

後は、便利な使い方がもっとあると思いますが、公式サイトにも情報がいっぱいあるので、ぜひ見てみてください。

ブレークポイントでコードを一時停止する

TypeScript IDEの型推論がまだ適当なのかもしれない

最近TypeScriptを触り始めた私です。

ちょっとわかりづらかったことがあったのでメモ的に書いておきます。

私はIDEはPHPStormを使っていて、バージョンはPhpStorm 2023.2.4です。
PHPStormで設定しているTypeScriptのバージョンは5.1.3です。
Nodeのtscのバージョンは5.0.4です。

さてさて、次のようなTypeScriptがあるときに

    class Person {
      constructor(public name: string, public ramen?:string) { }
      talk() {
        console.log('最近どうですか?')
      }
      setRamen(ramen: string) : Person{
        this.ramen = ramen
        return this
      }
      build():Person {
        return this
      }
    }

    class Kyushujin extends Person {
      talk() {
        console.log('最近はなんばしよっとね?')
      }
      build():Kyushujin {
        return this
      }
    }

    const kyushujin =  new Kyushujin('太郎')
        .setRamen('とんこつ')
        .build()

    if(kyushujin instanceof Kyushujin) {
      console.log('九州人です')
    }

    kyushujin.talk()

実行結果は下記の通りです。

ちゃんとインスタンスkyusyujinは”Kyushujin”として認識されていますが、new Kyushujin の部分でIDEがしてくれる型推論は 下記のスクショのようにスーパータイプのPersonなんですよね。

PHPStormのスクショ① const kyushujin:Personとなっている

エッ ってなりましたね。TypeはKyushujinとして表示されるべきだと思ったからです。

これ、setRamen() で 戻り値の型が Personになってるからみたいで setRamen()をコメントアウトすると、Kyushujinとして推論してくれます。

PHPStormのスクショ② const kyushujin:Kyushujin となっている

実行結果は上記と一緒です。

一瞬、あれ?サブタイプにちゃんとなってないじゃん!って思って焦りました。

あんまりまだわかってないことが多いので、まだまだ勉強中です。(´ω`)

ちなみに余談で、弊社ではGithub Copilot 使ってますが、下記までタイプしたところで

    const kyushujin =  new Kyushujin('太郎')
        .setRamen(
 .setRamen('とんこつ')

って勝手にコパイロットくんが入れてくれたんですよ!!!

すごくないですか???Σ(゚Д゚)スゲェ!!

九州人のラーメンは ‘とんこつ’ を set する

を生成できるところまで来てるんですね。(`・ω・´)