之前几次升级 Angular 版本的经历总体还算顺利,因此原本对 Angular 8 升级到 9 的过程也比较乐观。虽然 Angular 9 开始将默认启动新的 Angular Ivy,但考虑到已经正式发布数月,甚至 Android 10 都已问世,自己的项目又没有用特别冷门的依赖,依然没有担心会遇到问题。可惜墨菲定律无处不在,最后还是花了比想象中更多的时间完成升级。
为此,本文简单介绍自己在从 Angular 8 升级到 9 最终到 10 时遇到的问题及相应的解决方法,仅供参考。
首先,Angular 官方文档提供了详细的升级说明,建议在升级项目前首先通览相关文档,了解可能存在的问题。可以从下面的链接找到升级到 Angular 10 的最新信息。
https://angular.io/guide/updating-to-version-10
下面是自己在升级时实际操作的几个主要步骤:
- 更新 Angular 8 相关依赖至最新版
- ng update @angular/core@8 @angular/cli@8
- ng build –prod –aot
- 确认变更
- 更新 @azure/msal-angular 至最新版 1.0.0
- 安装 1.0.0 版并为新版的接口变化修改代码(1)
- ng build –prod –aot
- 确认变更
- 升级至 Angular 9,检查并乎略一些依赖版本问题
- ng update @angular/core@9 @angular/cli@9 –force(2)
- ng build –prod –aot
- 确认变更
- 升级 Angular Material
- ng update @angular/material@9
- 检查 Angular Material 引用问题(3)
- 增加 ngcc 作为 postinstall 脚本(4)
- ng build –prod –aot
- 确认变更
- ng add @angular/localize (5)
- ng serve –ssl 启动并检查应用基本功能
- 升级至 Angular 10,检查并乎略一些依赖版本问题
- ng update @angular/core @angular/cli –force
- 临时手段解决 ngx-restangular 编译问题(6)
- ng build –prod –aot
- 确认变更
- ng update @angular/material
- ng build –prod –aot
- 确认变更
- ng serve –ssl 启动并检查应用基本功能
- 大功告成
以下具体说明上述步骤中的一些关键操作。
(1)MSAL Angular 代码更改
在升级 Angular 9 之前,自己的项目使用 MSAL Angular 0.1.4 实现对 Microsoft 账户或 ADD 的验证。当时该库的文档和范例代码质量就差强人意,缺少说明和链接失效比比皆是。更重要的是,该库并不兼容 Angular Ivy,这也是没有更早升级 Angular 9 的原因之一。等着 MSAL Angular 花了几个月的时间终于正式发布 1.0.0 版本才开始升级,依然遇到几个问题:
- 方法接口变化,getUser() 改为 getAccount(),displayableId 成员取消
- 服务初始化方式变化,结构虽然较过去清晰,但文档和示例代码不足,甚至还有语法拼写错误
- 请求验证成功后返回的 payload 格式发生变化,token 变量改为 rawIdToken
- loginRedirect() 似乎不再正常工作,仍需进一步排查原因
无论如何,能够兼容 Angular 9 已经不易,总比 ngx-restangular 那样根本无法成功编译要好些。MSAL Angular 已经两个月没有新版本发布,不知今后的更新是否能解决一些问题。
(2)升级至 Angular 9
这步本身没有什么问题,不过需要检查一些报警的依赖版本冲突。自己的项目中主要是 tslib 已经到了 2.0 而 Angular 9 要求 1.10.0,以及 Angular http 停在了大版本7。确认没有问题后就以 –force 参数强制升级。
(3)检查 Angular Material 引用问题
新版本 Angular Material 要求每个引用都明确指定具体组件,如:
import { MatSpinner } from '@angular/material/progress-spinner';
而不允许在一句引用中同时引入多个组件,如:
import { MatSpinner, MatSnackBar } from '@angular/material';
正常情况下 ng update 会自动更正代码,不过自己在操作时似乎遇到一些问题,没有顺利完成,只好收到批量做了一些处理。
(4)增加 ngcc 作为 postinstall 脚本
在 package.json 中增加 “postinstall”: “ngcc” 有助于避免可能的库兼容问题。参见 ngcc 的和 ModuleWithProviders 迁移的相关说明。
(5)增加 @angular/localize
如果项目中使用了 Angular 自带的 i18n 功能,可能需要执行 ng add @angular/localize 添加必要的依赖。不过这并非必要步骤,可以在遇到提示时才采取行动。
(6)解决 ngx-restangular 5.0.0 编译问题
很可惜,ngcc 不是银弹。至少,它无法解决 ngx-restangular 的兼容问题。
ngx-restangular 是自己项目中使用的主要第三方库之一,帮助使用 REST API 的使用。这个项目本身算不上太活跃,但基本功能还算充分。可惜的是,半年没有更新的它,果然无法在 Ivy 下正确编译,报错如下:
ERROR in node_modules/ngx-restangular/lib/ngx-restangular.module.d.ts:8:97 - error TS2314: Generic type 'ModuleWithProviders<T>' requires 1 type argument(s).
static forRoot(providers?: any[], configFunction?: (provider: any, ...arg: any[]) => void): ModuleWithProviders;
搜索尝试一些方法无果后,不得已手动修改库文件,增加类型 <RestangularModule> 在 node_modules/ngx-restangular/lib/ngx-restangular.module.d.ts 第八行末尾:
static forRoot(providers?: any[], configFunction?: (provider: any, ...arg: any[]) => void): ModuleWithProviders<RestangularModule>;
在 CI 管道中,则暂时每次都在 ng build 之前以 sed 命令完成代码更改:
sed -i 's/ModuleWithProviders;/ModuleWithProviders<RestangularModule>;/g' node_modules/ngx-restangular/lib/ngx-restangular.module.d.ts
于是,暂时绕过了编译错误。ngx-restangular 的功能本身倒似乎没有问题,可以正常工作。
经过功能确认和测试后,一个 Angular 8 应用终于顺利升级为了 Angular 10 应用。尽管并非一帆风顺,但 Ivy 确实明显提升了编译性能并减小了体积,总管没有白费功夫。如有需要,还可以参考 AngularGo (GitHub 地址),了解升级过程中的配置与代码更改。