今すぐstrict: trueにしたい!「Classi Angular Night #3」参加レポート

6/13(木)に渋谷で開催されたClassi Angular Nightに参加してきました!

Angular系のイベントに参加するのは今回が初だったので若干緊張していましたが、実際に参加すると学べることばかりで、次回もぜひ参加したいと思えるような素晴らしいイベントでした。

本記事ではそのイベントの内容を簡単にまとめていきたいと思います。

セッション1:HRMOSにおけるAngularのエラーハンドリングについて

株式会社ビズリーチの @deepblue_will さんのセッションです。

発表資料は以下のURLに公開してくださりました。

https://docs.google.com/presentation/d/1hq3OONQxUgGCHWDYVDWJyylZ6HsKmppWYIPU6CrnJ0Y/edit#slide=id.p

紹介されたエラーハンドリングのポイントは以下の3つです。

  • Custom Error Class
  • Interceptor
  • ErrorHandler

一言で言うと、「アプリケーションで発生したエラーを柔軟にハンドリングするための方法」かと思います。

まずErrorクラスを継承した独自のエラークラスを定義して、それをInterceptorでHTTP通信ロジックに割り込ませ、発生した例外をエラーハンドラで好きに処理するということを実現されていました。

エラーハンドラでの処理では、Sentryに状況を送信するコードが紹介されていました。

Sentryというサービスは知らなかったので調べてみたところ、アプリケーションのログを収集してエラーの原因究明に役立てることができるツールのようです。

想定外の箇所でJavaScriptのエラーが発生しているとなかなか気づけないので、このような仕組みを整えて分析できると非常にバグ修正が効率化できそうです。

セッション2:Deep dive into Component

Classi株式会社の @kasaharu さんのセッションです。

AngularのComponentの基本のおさらいから始まり、ライフサイクルメソッドやChangeDetector等のディープな話までいく非常にためになるセッションでした。

Angularのコンポーネントには8つのライフサイクルメソッドがありますが、それぞれの発火するタイミングを押さえて利用の仕方を考えないと、意図したタイミングで呼ばれなかったり、呼び出しが多すぎてパフォーマンスが下がったりしてしまいます。

Angularのライフサイクルメソッド

  • ngOnChanges
  • ngOnInit
  • ngDoCheck
  • ngAfterContentInit
  • ngAfterContentChecked
  • ngAfterViewInit
  • ngAfterViewChecked
  • ngOnDestroy

コンポーネントはルートコンポーネントからツリー構造をなす形で構成されますが、そのどこかでイベントが発生した場合、Angularはコンポーネントツリーを上からチェックし直します。

このとき、コンポーネントが多くて、かつコンポーネント内のライフサイクルメソッドに重い処理が書いてあるとパフォーマンスに影響が出てきます。

そこで、「@Inputデコレーターがつけられたフィールドに変更がない場合は変更検知をスキップする」という指定を@Componentの中で行うことができます。

ChangeDetectionStrategy.OnPushというやつですね。

実際にこのオプションは日常的に使うようにしていますが、具体的にどれくらいパフォーマンスを上げられているのかは計測できていませんでした。

今回のセッションで紹介されたenableDevToolsオプションを設定することで、Chromeのデベロッパーツールで確認できるとのことだったので、パフォーマンスにどれだけ差が出るかを検証してみたいと思います。

セッション3:Upgrade Angular to v8.0

続いて言わずと知れたAngularの達人、 @laco2net さんのセッションです。

資料は以下のURLに公開してくださりました。

https://docs.google.com/presentation/d/e/2PACX-1vRfAA6JhQ-oPl_nKBSFHhDhkBrPcNHVu3OKZOEsfpHQ6FXZV1X_b3A50Hwx7_O1o6gA4WJSa_Y6E7aU/pub?start=false&loop=false&delayms=3000&slide=id.p

Angularのバージョンの上がり方のルールや、既存のバージョンから次以降のバージョンにアップデートする方法が紹介されました。

https://update.angular.io/ というサイトを使うことで、任意のバージョン間のアップデート方法をチェックリスト形式で見ることができるので、アップデートの際には重宝しそうです…!

v2 → v4、v4 → v5、v6 → v7とアップデートにかかる工数はどんどん下がっている傾向にあるようですが、v7 → v8はv6 → v7に比べて少し工数が増えそうとのことです。

Angularでは、破壊的な変更はDeprecatedになってから最低2つのバージョンを置いてから削除されるルールになっており、それに基づいて今回は以下の破壊的変更が行われました。

  • @angular/http パッケージの削除
  • platform-browserからexportされていた{ DOCUMENT }が@angular/commonからのexportに変更
  • Node.js v8のサポートが終了

@angular/httpはDeprecatedになった頃に@angular/common/httpに切り替えておいたので、v8へのアップデートでは困らなさそうです。

Node.jsのバージョンなどは開発者全員に周知した上で変更を依頼する必要がありそうです。僕はndenvを使ってNode.jsのバージョンを管理しているので、ひとまずアップデートは問題なさそうです。

新たにDeprecatedになるものとして、以下の二つがあります。

  • @angular/platform-webworker
  • loadChildrenで文字列指定

注目したのはloadChildrenの方です。今開発しているAngularアプリケーションでは、Angular Routerを使ってルーティングを管理しています。

その中で、ルーティングを分割して遅延読み込みができるloadChildrenを常用していますが、その書き方が以下のように変わるようです。

// Before
loadChildren: './mycomp/mycomp.module#MyCompModule'

// After
loadChildren: import('./mycomp/mycomp.module').then(m => m.MyCompModule)

今までの書き方だと、実際にそこにモジュールがあるかどうかはコンパイル時にしかわからず、誤字によるミスが防げない状態になっていました。

一方、Dynamic Importの機能を活用した新しい書き方では、Linterの恩恵を受けて安全に開発することができます。

今のうちから徐々に書き換えていきたいですね。

その他、IvyというAngularをフルスクラッチで書き直した新しいエンジンの紹介がありました。

バンドルサイズがより小さくなり、原因不明で残っていたバグも修正されるという魅力的なエンジンの今後も楽しみです。

セッション4:tsconfig.jsonのcompilerOptionsにstrict: trueを追加した話

この記事のタイトルにも入れさせていただいたstrict: trueの話題で発表してくださったのは、株式会社ビジネスバンクグループのフロントエンドエンジニアの @shimikyonkyon さん。

資料は以下のURLに公開してくださりました。

https://docs.google.com/presentation/d/1yUYUV46gtci6pW-bApVvaSw9OY7fOdcsCxtyt3qcfB0/edit#slide=id.g5bdf3b4722_2_50

こちらはAngularというよりTypeScriptの話題です。

TypeScriptはトランスパイルすることでJavaScriptを生成する静的型付け言語です。

AngularJSの頃はCoffeeScriptで開発を頑張っていましたが、どうにも動かないというところで頑張ってデバッグしてみたら存在しないプロパティにアクセスしていただけとか、数字を文字列型で持っていただけだったとか、静的型チェックがあれば防げていたような困りごとが結構ありました。

Angular4系を触り始めてTypeScriptを使うことになり、以来その安心感から非常に重宝しています。

さて、TypeScriptでは、tsconfig.jsonというファイルでトランスパイル時の設定を行うことができます。

compilerOptionsセクションには様々なルールが用意されていて、それを有効にすることでより厳密な型チェックを行うように設定できます。

今回紹介されたstrict: trueを指定することにより、以下のオプションが有効になるということです。

  • –noImplicitAny
  • –noImplicitThis
  • –alwaysStrict
  • –strictBindCallApply
  • –strictNullChecks
  • –strictFunctionTypes
  • –strictPropertyInitialization

これらをそれぞれ個別にオンオフできるのを、一括でオンにするオプションということのようです。

それぞれのフラグの役割はしみきょんさんのスライドに任せるとして、僕が大変学ばせていただいたのはstrict: trueにしたことによって新たにエラーと判断されたコードの修正方法です。

既存のプロジェクトにstrict: trueを有効化したところ、2,638個ものエラーが上がったとのことでした。

エラーが出ると、つい地道に上から一つひとつ対処して減らそうとする僕ですが、しみきょんさんはエラーをタイプ別に分類することから始められていました。

分類してから実際の修正までの流れは以下のようになるかと思います。

  • どんなエラーがそれぞれどれだけ上がっているかを洗い出す
  • 各エラーに対して、その修正方法を検討してまとめる
  • 対応状況を管理するシートなりを作成する
  • ひたすら修正する

言われてみれば非常にシンプルなこの方法ですが、自分がその局面に置かれたときにパッとこの対応ができるかと言われると全く自信がありません^^;

strict: trueにすることで解消する課題の中に、以下のようなものがあります。

  • クラスの初期値を代入を必須にする
    • 代入しないなら、ビューでngIfディレクティブ等を使って存在しないフィールドにアクセスしないようにする必要があるが、対応方法が開発者ごとに異なる。
  • 明示的にフィールドがnullを許容していない場合にnullを使えないようにする
    • フィールドがnullの場合を考慮してngIfディレクティブが使われているのを、コードを読み解いて見抜かなければならない。

このあたりは日常的に課題を感じていたので、strict: trueによってルールが敷かれるのは非常にありがたいと思っています。

今ちょうどAngularを使って開発しているアプリのアップデートを行なっているので、ついでにstrict: trueオプションを有効化してより安全な開発環境を手に入れたいと強く思いました。

まとめ

Angularの初心者からちょっとだけ進むと、以下のような要素が登場すると思っています。

  • ChangeDetector
  • NgModule
  • LazyLoading
  • CompilerOptions
  • ngrx(状態管理)
  • Container Component/Presentational Component
  • zone.js

実際に業務として開発するアプリケーションは、これらの特徴をしっかりと押さえて活用していくことが求められると思います。

そのようなAngularの中〜上級者のスキルを効率的に学ぶ場として、このようなイベントはとてもありがたいと感じました。

学んだきりで終わらせないよう、しっかりとプロダクトに反映させていきたいと思います!