Angular
官方網站針對 從 AngularJS 升級到 Angular 提供了比較詳細的文檔,並給出了一個 PhoneCat 升級教程 的案例演示,指導一步步如何改造。但總的來講,這個案例仍是太過簡單,並不能很好地還原一個最原始的、相對複雜的、版本更低的遺留項目該如何一步步升級,以及升級過程當中可能須要考慮的一些額外因素。html
本篇文章會以一個相對複雜的遺留項目爲原型來探討該如何一步步進行漸進式地升級改造,以及針對不一樣狀況能夠採起哪些策略,算是一篇結合了實際項目改造後的經驗之談。webpack
遺留項目按照不一樣業務拆分紅了多個業務模塊和一個公共模塊,即有多個代碼倉庫,以下圖:git
從上圖可知,遺留項目中主要使用的是 AngularJS 1.4
,只有一個模塊D
使用了高版本的 Angular
。其實若是正確的業務劃分,模塊D
是屬於模塊C
的一個子模塊(或一部分)。github
本來是考慮將模塊C
拆分爲更多更小的模塊,再加上想嘗試用較新的 Angular
技術棧來寫新功能,在種種緣由的促使下最終有了 Angular
高版本的模塊D
。可是,在後續的開發實踐中發現這樣的拆分是存在必定問題的(好比維護兩套相似邏輯的代碼、修改容易疏漏等等),不過因爲已經用了高版本的 Angular
,沒法再簡簡單單地合併回去。web
最終,除了須要考慮如何將 AngularJS 1.4
一步步升級到高版本的Angular
,也須要考慮在升級到必定程度以後將相同業務模塊的代碼倉庫進行合併。bootstrap
這部分主要包含實際改造中遇到的一些硬性限制以及相對應的升級改造原則。api
(1)代碼量與時間上的限制app
首先,做爲遺留項目,各個倉庫的代碼量不一(有多有少),但整體的量是很是龐大的。所以不可能在短時間內或一次性所有改造完成。對此較好的策略是從較小的倉庫開始着手,這樣既能用較小的成原本作技術預研,判斷改造方案的可行性,也能較好地控制改造後的風險。改形成功一個以後,則能夠依葫蘆畫瓢慢慢鋪開,改造剩餘的倉庫。ide
其次,現有遺留項目還在不斷地改舊增新,這也將佔據大部分的編碼時間,而且還存在按期的發版上線,在作技術升級改造的同時須要優先保證正常的功能開發與發版。簡而言之,升級改造和改舊增新是並行的,升級改造須要兼顧改舊增新。工具
不管是從代碼量上考慮,仍是從改造時間的不連續性上考慮,新舊代碼必然是長期共存的,而且爲了保證正常的發版,升級改造也必須是漸進式的增量升級。
(2)公共模塊便利帶來的升級限制
所謂成也蕭何敗蕭何,在升級改造中,公共模塊的存在是最爲尷尬的。正是由於其複用得多,對其改造後的影響範圍也是最大的,好比改一個公共的組件就須要檢查並修改全部引用了公共模塊的倉庫。
特別是若是要對公共模塊中使用的某個庫進行升級,那麼全部引用公共模塊的模塊也都必須同時升級,而且還須要檢查 break changes
的影響,這頗有可能須要較大的工做量才能完成。所以有時不能只從升級的可行性上考慮,還須要考慮升級的必要性和其後所能帶來的收益大小等等。(這個問題在 angular-ui-boostrap
的升級改造中遇到,後面詳談。)
總的來講,雖然不一樣的遺留項目可能面對的狀況和限制或多或少會有所差別,但大致上升級改造沒法作到一步到位,將會是一個長期的過程是比較常見的狀況,對此所須要的則是一個漸進式、增量升級的過程與解決方案。
這部分所談的演進方向主要是一些概要描述,不包含具體的實踐細節。
(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
。以下圖:
(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
組件庫進行替代,只要保證樣式和基本交互可以基本一致便可。
綜上,主要介紹了遺留項目的基本狀況、項目中的限制與應當遵循的升級改造原則、大體的升級改造方向以及遇到的主要難點。後續系列文章準備將進一步討論升級方案中一些步驟具體如何實踐以及踩過的坑。