AngularJS 遗留项目的升级改造之路(一)
目录
- 序言
- 遗留项目概述
- 条件限制下的升级原则
- 升级改造的演进方向
- 遇到的主要难点
- 小结
- 参考
1. 序言
Angular 官方网站针对 从 AngularJS 升级到 Angular 提供了比较详细的文档,并给出了一个 PhoneCat 升级教程 的案例演示,指导一步步如何改造。但总的来说,这个案例还是太过简单,并不能很好地还原一个最原始的、相对复杂的、版本更低的遗留项目该如何一步步升级,以及升级过程中可能需要考虑的一些额外因素。
本篇文章会以一个相对复杂的遗留项目为原型来探讨该如何一步步进行渐进式地升级改造,以及针对不同情况可以采取哪些策略,算是一篇结合了实际项目改造后的经验之谈。
2. 遗留项目概述
遗留项目按照不同业务拆分成了多个业务模块和一个公共模块,即有多个代码仓库,如下图:
从上图可知,遗留项目中主要使用的是 AngularJS 1.4,只有一个模块D使用了高版本的 Angular 。其实如果正确的业务划分,模块D是属于模块C的一个子模块(或一部分)。
原本是考虑将模块C拆分为更多更小的模块,再加上想尝试用较新的 Angular 技术栈来写新功能,在种种原因的促使下最终有了 Angular 高版本的模块D。但是,在后续的开发实践中发现这样的拆分是存在一定问题的(比如维护两套类似逻辑的代码、修改容易疏漏等等),不过由于已经用了高版本的 Angular,无法再简简单单地合并回去。
最终,除了需要考虑如何将 AngularJS 1.4 一步步升级到高版本的Angular ,也需要考虑在升级到一定程度之后将相同业务模块的代码仓库进行合并。
3. 条件限制下的升级原则
这部分主要包含实际改造中遇到的一些硬性限制以及相对应的升级改造原则。
(1)代码量与时间上的限制
首先,作为遗留项目,各个仓库的代码量不一(有多有少),但总体的量是非常庞大的。因此不可能在短期内或一次性全部改造完成。对此较好的策略是从较小的仓库开始着手,这样既能用较小的成本来做技术预研,判断改造方案的可行性,也能较好地控制改造后的风险。改造成功一个之后,则可以依葫芦画瓢慢慢铺开,改造剩余的仓库。
其次,现有遗留项目还在不断地改旧增新,这也将占据大部分的编码时间,并且还存在定期的发版上线,在做技术升级改造的同时需要优先保证正常的功能开发与发版。简而言之,升级改造和改旧增新是并行的,升级改造需要兼顾改旧增新。
不论是从代码量上考虑,还是从改造时间的不连续性上考虑,新旧代码必然是长期共存的,并且为了保证正常的发版,升级改造也必须是渐进式的增量升级。
(2)公共模块便利带来的升级限制
所谓成也萧何败萧何,在升级改造中,公共模块的存在是最为尴尬的。正是因为其复用得多,对其改造后的影响范围也是最大的,比如改一个公共的组件就需要检查并修改所有引用了公共模块的仓库。
特别是如果要对公共模块中使用的某个库进行升级,那么所有引用公共模块的模块也都必须同时升级,并且还需要检查 break changes 的影响,这很有可能需要较大的工作量才能完成。因此有时不能只从升级的可行性上考虑,还需要考虑升级的必要性和其后所能带来的收益大小等等。(这个问题在 angular-ui-boostrap 的升级改造中遇到,后面详谈。)
总的来说,虽然不同的遗留项目可能面对的情况和限制或多或少会有所差异,但大体上升级改造无法做到一步到位,将会是一个长期的过程是比较常见的情况,对此所需要的则是一个渐进式、增量升级的过程与解决方案。
4. 升级改造的演进方向
这部分所谈的演进方向主要是一些概要描述,不包含具体的实践细节。
(1)代码风格改造
遗留项目中第一优先需要改造的就是代码风格。由于 AngularJS 1.4 版本本身的特性限制,遗留项目中存在着大量的 replace:true 、变量绑定在 $scope 上、文件目录不清晰等与 Angular 规范不太匹配的代码。
而要改造好代码风格,与升级相比还是较为容易实现,只需要约定好相应的规范(可以参考 Angular 官网推荐的 风格指南),之后则是花费工作量的事情。
(2)AngularJS 1.4 升级到 1.5
从以下三方面综合考虑,有必要将遗留项目中使用的 AngularJS 1.4 至少升级到 AngularJS 1.5 :
第一: 升级的代价相对较小。因为毕竟是小版本的升级,虽然存在一定的 breaking changes,但根据官方提供的迁移文档,所需更改相对较少,只需要针对性的检查一番和做少量修改即可。详情可见此
第二: AngularJS 1.5 新增的特性将有助于更方便地实现新功能(比如 component、单向绑定、新的生命周期等)。
第三: AngularJS 1.5 新增了组件API,有助于改造遗留代码的风格。不论是在代码风格上还是在组件的生命周期上,其都比较像 Angular 中的等价物,在此基础上将代码升级到 Angular 时会更容易。
(3)引入 TypeScript
这里所说的引入 TypeScript,并不会像官网案例中那样引入了 TypeScript 后就将所有文件直接改为 .ts 文件,而是依旧采用渐进式的升级改造方式。可以通过借助 webpack 打包工具,让项目同时支持 .js 和 .ts 两种文件格式,有针对性的使用相关插件,最终统一生成 js 的目标文件。这样就可以不用一次性将全部文件改为 .ts 文件,把改造的影响降到最低,只需要在后续改造中一步步将 .js 替换为 .ts 文件即可。
另外,在现有遗留项目中,针对 js 文件有用 eslint 进行代码风格检查与约束。现在添加了 TypeScript,针对 ts 文件同样也可以使用 eslint。从 eslint 6.0 之后可以根据不同的文件后缀使用不同的规则,这样就可以同时支持 js 和 ts 两种文件。
(4)引入 angular-ts-decorator(可选)
在将 AngularJS 升级到 1.5+ 之后,可以通过引入 angular-ts-decorator 以 Angular 2 的代码风格对遗留代码进一步改造或直接编写新业务。angular-ts-decorator 的原理很简单,其实就是借助装饰器,将 AngularJS 模块声明、指令、控制器声明全部包装了一层,其内在实质没有变化。
简而言之,可以通过使用 angular-ts-decorator 将 AngularJS 的代码风格改为如同 Angular 2 代码风格,在享受 Angular 风格的代码带来的便利性的同时,也方便后续的升级改造。
到这一步,你或许会有所疑惑,因为按照官网的升级改造,似乎完全没有必要进行这一步。在必要的代码风格改造 + 引入 TypeScript 后,其实就可以直接进入到开启 AngularJS + Angular 的混合模式了。然后就可以快乐地用高版本的 Angular 的写组件,新功能完全用高版本写,至于涉及到 AngularJS 的部分,利用组件的升/降级方案,可以在 AngularJS 和 Angular 两边混用组件 ,一切看起来似乎很美好,但实际情况会有这么简单和容易吗?
一方面, 需要考虑“改旧增新”开发新功能会占据主要时间,技术升级改造的时间相对较少且不连续。而在项目中引入 angular-ts-decorator 库的工作量是极小的,基本上可以开箱即用,只需要写一两个样例,整个团队就可以按照新风格来写 AngularJS 。这将直接提升团队整体的开发体验,同时新写法与升级后的 Angular 组件很类似(除了 html 依旧是 AngularJS 写法),除了方便后续的升级改造,也更易于维护。
另一方面, 升/降级组件其实都没有想象中那么简单。这里的不简单主要受限于过滤器/管道、属性指令以及第三方 UI 组件库这三个方面(具体在后面遇到的难点中详谈)。如果能够较好的解决这三个问题,那么升级 AngularJS 的组件为 Angular 的组件相对来说就比较容易。
也因此,虽然这一步是可选的,但结合项目的具体情况,其也可能变成是必要的。
(5)启用 AngularJS + Angular 混合模式
开启混合模式本身很简单,只需要引入 Angular 相关的库,然后在 Angular 中引导 AngularJS 模块加载启动即可。详细可见
(6)逐步升级替换 AngularJS
第一: 引入 HttpClient 来处理 Http 请求,并配置好相关的 Http Intercepters 。这会与 AngularJS 中的 $resource 以及配置的 $httpProvider 相关的策略相对应。
第二: 引入 RouterModule,使用相邻出口配置 Angular 的路由策略,让混合应用同时支持 AngularJS 和 Angular 的两种路由。
第三: 如果遗留项目中用了第三方的 AngularJS 的 UI 组件库(比如 angular-ui-bootstrap),首先考虑是否能够升级到对应的 Angular 的版本。如果不能或工作量实在太大,那么则需要考虑是否有可替代的 Angular 版的 UI 组件库,当然这会使得项目中存在 AngularJS 和 Angular 两套第三方 UI 组件库,需要考虑的样式和交互上的一致性。
第四: 全新的功能和页面,可以完全采用 Angular 组件和路由来写,而涉及到 AngularJS 的部分,如果不能一次性升级改造完,则可以采用临时的升/降级组件和服务,来实现混用。(总体原则:优先用高版本 Angular 组件或服务实现相关业务功能)
第五: 合并相同业务模块。因为已经开启混合模式,配置好了 Http 请求和路由策略,所以可以考虑将高版本的 Angular 模块合并到开启混合模式的模块中。
......
(7)最终目标
不论准备工作和具体的升级实施方案如何,技术升级改造的最终目标是简单明确的——合并相同业务模块,并将所有仓库的代码升级到高版本 Angular。如下图:
5. 遇到的主要难点
(1)路由及路由组件的升级改造
Angular 官方文档在路由改造这一块考虑不是很周全或参考性不强,其升级改造方式并不是渐进式的。一般来说,大的遗留项目根本无法一次性将所有的路由组件替换完。因此需要考虑 AngularJS Router 和 Angular Router 两种路由的长期共存的可能性,并在改造中逐步用 Angular Router 去替换 AngularJS Router 。而这方面相关的解决方案,官方的升级文档中并没有提供,需要自己摸索或搜寻。
Tips: 如果考虑在混合应用中只用 AngularJS Router 路由,也是可行的。其中一种解决方案是将所有的 Angular 路由组件进行降级使用,或者如果 AngularJS Router 用的是 ui-router, ui-router 官方也提供了一套对混合应用进行支持的方案 angular-hybrid 。但如果考虑到升级改造本身就是要替换掉 AngularJS Router 路由,那么首选混合路由相对较好。
(2)升级改造中的 breaking changes
对所有第三方库的升级,即使是次版本的升级,有时也会有一些 breaking changes(比如 Angular 1.4 到 1.5),这是升级时所必须注意的。而对相应的 breaking changes 则必须结合实际项目作出评估,判断出影响范围有哪些或者是否很大。如果影响范围很大或修改工作量太大,就需要考虑是否有升级的必要性。
另外,在升级过程中还遇到了依赖升级的情况。在将 Angular 1.4 升级到 1.5 后,在使用 1.5 新增的 component 组件特性时,发现其作为路由组件在项目中使用的 ui-router 0.4.x 中不支持这一特性,而它是从 1.0 及其以后开始支持的。这种大版本的变更必然带来 breaking changes ,在结合了官方的 UI-Router 1.0 Migration 以及项目中使用情况,梳理出 breaking changes 带来的影响点后,判定为影响相对较小可以接受,因此也连带着将 ui-router 升级到了 1.0 。
(3)官方 Angular 升级方案本身的限制
无法对 AngularJS 的过滤器 filter 以及属性指令 attribute directive 进行升级在 Angular 中使用,同时 Angular 的管道 Pipe 以及属性指令 attribute directive 也无法降级在 AngularJS 中使用。
这个主要会带来两个问题:
第一: 无法复用,一定时间内可能会同时存在类似逻辑的两份代码。
第二: 有时要升级一个 AngularJS 组件,会发现里面大量使用了过滤器 filter 以及自定义的属性指令 attribute directive ,升级一个组件的工作量会比预想中的大得多(不能很顺滑的升级组件)。
(4)第三方 UI 组件库的升级改造
遗留项目中主要使用的 UI 组件库是 angular-ui-bootstrap ,一个纯 AngularJS 的组件库。
首先,由于其引入的版本比较低只有 0.14.x ,其组件指令 component directive 实现还是用了 replace: true 等这些无法进行升级的特性,所以无法直接通过升级在 Angular 中使用。
其次,在遗留项目中不仅大量使用 angular-ui-bootstrap 的组件指令 component directive ,也使用了很多它的属性指令 attribute directive ,这也导致了就算将 angular-ui-bootstrap 本身进行升级(至少升到 2.0,存在大量 breaking changes),以使得组件指令 component directive 可以通过暂时升级的方式在 Angular 中使用,但属性指令 attribute directive 无法使用的问题仍旧无法解决。
也因此,最终放弃了升级 angular-ui-bootstrap 本身,而是考虑直接用一个高版本的 Angualr 的 UI 组件库进行替代,只要保证样式和基本交互能够基本一致即可。
6. 小结
综上,主要介绍了遗留项目的基本情况、项目中的限制与应当遵循的升级改造原则、大致的升级改造方向以及遇到的主要难点。后续系列文章准备将进一步讨论升级方案中一些步骤具体如何实践以及踩过的坑。
7. 参考
AngularJS migrating from 1.4 to 1.5
AngularJS 遗留项目的升级改造之路(一)的更多相关文章
- Lumen框架—升级改造之路-仓储层
仓储层与逻辑层搭建 Lumen官方文档:https://lumen.laravel.com/docs/5.5 我的项目地址:https://github.com/BusinessL/big-lume ...
- Lumen框架—升级改造之路-开篇
一.前言 首先,我先阐述下,为什么要做这件事.lumen是一款比较轻型的PHP框架,但是,作为项目开发来说,它还是缺少很多东西,比如Response返回值规范的自定义,异常抛出格式的自定义,以及 ...
- gitlab 迁移、升级打怪之路:8.8.5--> 8.10.8 --> 8.17.8 --> 9.5.9 --> 10.1.4 --> 10.2.5
gitlab 迁移.升级打怪之路:8.8.5--> 8.10.8 --> 8.17.8 --> 9.5.9 --> 10.1.4 --> 10.2.5 gitlab 数据 ...
- 运维工程师打怪升级进阶之路 V2.0
在此之前,发布过两个版本: 运维工程师打怪升级之路 V1.0 版本发布 运维工程师打怪升级必经之路 V1.0.1 很多读者伙伴们反应总结的很系统.很全面,无论是0基础初学者,还是有基础的入门者,或者是 ...
- 数据平台调度升级改造 | 从Azkaban 平滑过度到 Apache DolphinScheduler 的操作实践
Fordeal的数据平台调度系统之前是基于Azkaban进行二次开发的,但是在用户层面.技术层面都存在一些痛点问题难以被解决.比如在用户层面缺少任务可视化编辑界面.补数等必要功能,导致用户上手难体验差 ...
- HTML5音乐播放器(最新升级改造加强版)
最近么,单位里面么老不顺心的,公司一直催要程序员要PHP,然后本宅好不容易推荐了一个,我日嘞,最后待遇变成1.3,吾师最后也同意1.3W,然后还说要考虑... 尼玛,4年多5年不到一点的工作经验,前端 ...
- JAVA8,SPRING,ANGULARJS对项目
java8+spring+angularjs 项目应用 最近有写一个电子订单商务网站,使用JAVA8,SPRING,ANGULARJS对项目使用的技术和大家分享. 第一次写博客,哪有不对需要改正的请联 ...
- AngularJS+requireJS项目的目录结构设想
AngularJS+requireJS项目的目录结构设想 准备用AngularJS + require.js 作为新项目的底层框架,以下目录结果只是一个初步设想: /default 放页面,不过 ...
- Github+yeoman+gulp-angular初始化搭建angularjs前端项目框架
在上篇文章里面我们说到了Github账号的申请与配置 那么当你有了Github账号并创建了一个自己的Github项目之后,首要的当然是搭建自己的项目框架啦! 本人对自己的定位是web前端狗,常用开发框 ...
随机推荐
- moviepy音视频开发:使用credits1给视频加片头片尾字幕
☞ ░ 前往老猿Python博文目录 ░ 一.概述 在<moviepy音视频剪辑:视频基类VideoClip子类DataVideoClip.UpdatedVideoClip.ImageClip. ...
- 第4章 基础知识进阶 第4.1节 Python基础概念之迭代、可迭代对象、迭代器
第四章 基础知识进阶第十七节 迭代.可迭代对象.迭代器 一. 引言 本来计划讲完元组和字典后就讲列表解析和字典解析,但要理解列表解析和字典解析,就需要掌握Python的高级的类型迭代器,因此本节 ...
- 小程序view的显示与隐藏
需要在全局数据块中,设定一个控制键. data: { ......//省略其他代码 showView: true }, 然后是在wxml中,view的class中设置2个class,并用三目表达式来进 ...
- 转:locality sensitive hashing
Motivation The task of finding nearest neighbours is very common. You can think of applications like ...
- 免费部署个人博客到远端GitHub
前言 前面的博客我写到怎么样用hexo建立一个自己的博客网站(没看的可以先看前面那个文章地址,)但是它只能运行在本地端口,如果你分享给你的小伙伴他们是打不开的.如果把它部署到服务器上或空间上每个月都会 ...
- bilibili插件推荐
目前看到的好的插件就两个,现在来介绍一下. 第一个是 哔哩哔哩助手 这是它的功能,这里就以截图来给大家看 以上为这个插件的所有功能. 点击前往官网 第二个是 bilibili网页端添加APP首页推荐 ...
- Oracle数据泵常用命令
导读:expdp和impdp是oracle数据库之间移动数据的工具,本文简单总结了数据泵的常用命令,希望对大家有帮助. 前言 expdp和impdp是oracle数据库之间移动数据的工具.expd ...
- JavaSE17-File&递归&字节流
1.File类 1.1 File类概述和构造方法 File类介绍 它是文件和目录路径名的抽象表示 文件和目录是可以通过File封装成对象的 对于File而言,其封装的并不是一个真正存在的文件,仅仅是一 ...
- Spring Boot 日志各种使用姿势,是时候捋清楚了!
@ 目录 1. Java 日志概览 1.1 总体概览 1.2 日志级别 1.3 综合对比 1.4 最佳实践 2. Spring Boot 日志实现 2.1 Spring Boot 日志配置 2.2 L ...
- Java——排序算法
java排序从大的分类来看,可以分为内排序和外排序:其中,在排序过程中只使用了内存的排序称为内排序:内存和外存结合使用的排序成为外排序. 下面讲的都是内排序. 内排序在细分可以这样分: 1.选择排序: ...