Angularをv15→v18にアップデートしました
はじめに
この記事では、業務でAngularアプリケーションをv15→v18にアップデートする上でやったこと、考えたことなどを書いていきたいと思います。
アップデートの背景
プロダクト開発においてアプリケーションのフレームワークやライブラリをアップデートすることは、セキュリティリスクの解消やバグの修正、新機能の活用、パフォーマンス向上などにおいて重要だと思います。
voicyでは現在、基本的にAngularのLTSに追従する形でアップデートを行う方針となっています。Angularは半年ごとに新しいメジャーバージョンがリリースされ、原則として18ヶ月間がサポート対象となるため、LTSに追従するためには1年半に1度アップデートをすることになります。
今回はAngular v15のLTSが2024年5月に終了することで優先度が高くなったため、アップデートプロジェクトを始めました。
アップデート内容の決定
はじめに、アップデートにおいてやるべきことやスコープを書き出して決めました。例としては次のような項目です。
- アップデートするAngularバージョンの決定
- v18(依存ライブラリ対応状況によってはv17)
- アップデートするものを決定
- Angularバージョンに依存するもの(Angular, Angular Material, Angular Eslint、AngularFireなど)
- Angularバージョンへの対応が必要なもの(Node.js, TypeScript, RxJSなど)
- Actively supported versionsを参考にバージョンを決定
- 他にアップデートしたいライブラリ(hls.jsなど)
- QAチームとリグレッションテストの方針相談、テストの実施
- アプリケーション全体に影響がある変更のため
また、このプロジェクトの実装者は自分のみであることと、同時期にフロントエンドを開発している人がいなかったのでコンフリクトなどはあまり考慮せずに済みました。
作業手順
Angular CLIを使ったアップデートコマンドng update
では、Angularアプリケーションを一度に複数のメジャーバージョンに更新することはできないため、1バージョンずつ次のような項目を実行しました。
-
Update Guideでバージョンを選択し、表示されたアップデート方法を実行
- 主に
ng update
コマンドを実行npm run ng -- update @angular/core@18 @angular/cli@18 @angular/material@18 @angular-eslint/schematics@18
- (v15ぐらいから
package.json
のnpm scripts
にng
が自動で追加され、グローバルにAngular CLIをインストールしなくてもnode_modules
内のパッケージを利用してng
コマンドを実行することができ、この場合は指定したバージョンのAngular CLIを一時的にインストールしてアップデートしてくれます)
- 主に
-
該当バージョンで
ng new
したファイルと差分を比較し反映した方が良い箇所の検討 -
依存ライブラリのアップデートと修正
-
Angular機能のマイグレーションコマンドの実行
アップデートに付随して主に行った3つのこと
Angular MaterialのMDCコンポーネントへの移行
Angular Materialはv15でMaterial Design Components for Webがベースのcomponentとなり、以前のcomponentはlegacyという名称付きに変更され、非推奨になっていました。
v18に上げるコマンドを実行した際に、legacyのコンポーネントだと上げられないというエラーが出てアップデートを行うことができなかったタイミングで、ようやくMDCのコンポーネントへ移行しました。
MDC移行は公式ドキュメントMigrating to MDC-based Angular Material Componentsに沿って主に次の対応を行いました。
- マイグレーションコマンドの実行
npm run ng -- generate @angular/material:mdc-migration
-
// TODO(mdc-migration):
で自動で置き変えることのできなかったスタイルのクラス名の修正 - 実際に動かしデザイン差分をチェックしスタイルを修正
AngularFire(Firebase)のモジュラーAPIへの移行
AngularFireはv16互換バージョンより、Angularのメジャーバージョンの番号と合うようになりました。
ずっと移行はしようと考えていたのでアップデートを行うこのタイミングで、ようやくFirebaseをモジュラーAPIに移行しました。
AngularFireはAngular用のFirebase JavaScript SDKのラッパーライブラリのため、モジュラーへの移行はFirebase公式ドキュメントの名前空間方式APIからモジュラーAPIへのアップグレードを参考に行いました。
import {
provideFirebaseApp,
initializeApp,
setLogLevel,
} from '@angular/fire/app';
...
providers: [
...
provideFirebaseApp(() => {
setLogLevel('warn');
return initializeApp(environment.firebase);
}),
...
]
一部、以前のバージョンのAngularFireにないAPIがあったので、https://github.com/angular/angularfire/tree/main/src/compat のコードを読んでアプリケーション側で互換性を保つようにコードを追加しました。
Prettierをbuilt-in control flowなどに対応するようにバージョンアップ&設定を修正
v18へのアップデート作業が落ち着いた後にControl Flowを実際に利用してみようとした所、ネストした箇所が次のようにフォーマットされるというのを他のメンバーよりフィードバックがありました。
@if (a) {
<span>aaa</span>
@if (b) {
<span>b</span>
} @else if(c) {
<span>c</span>
} } @else {
<span>d</span>
}
コードフォーマットにはPrettierを採用しており、このフォーマットはControl Flowに対応していなさそうだと感じ、次の対応を行い修正しました。
- Prettierバージョンのアップデート
- Prettier 3.1: New experimental ternaries formatting and Angular control flow syntax!の記事にある通り、Control Flow Syntaxへの対応は3.1からで、案の定バージョンが古かったのでアップデートしました。
- Prettier設定の修正
-
.prettierrc
を見ると、Angular用のHTMLパーサーを利用する設定になっていなかったので修正しました。.prettierrc... "overrides": [ { "files": "*.html", "options": { "parser": "angular" } } ]
-
次のように想定通りのフォーマットになりました。
@if (a) {
<span>aaa</span>
@if (b) {
<span>b</span>
} @else if (c) {
<span>c</span>
}
} @else {
<span>d</span>
}
おわりに
基本的にAngular自体のアップデートは互換性が保たれておりやることは少ないですが、依存ライブラリやサードパーティのライブラリなどの対応では時間がかかるポイントはあるなと改めて思いました。
今回のアップデートは、他の事業優先度の高いタスクがあり対応していなかった部分と新機能へのマイグレーション対応もあり、想定より工数はかかってしまったかなと感じています。
ただアップデートにより業務でもSignalsやControl flowを使えるようになったので、新機能を上手く使って開発の生産性を高めていきたいです!
ここまで記事をお読みいただきありがとうございました!
Discussion