Angular 1 vs. Angular 2 深度比較
Angular 1 vs. Angular 2 深度比較
AngularJS 2 盡管還在Alpha階段,但主要功能和文檔已經發布。讓我我們了解下Angular 1 和 2 的區別,以及新的設計目標將如何實現。
Angular 2 當前仍處于 Alpha/開發預覽階段,但是主要功能和核心文檔都已經可用了。讓我們一起了解下 Angular 2 的設計目標,以及實現它們的計劃:
-
Angular 2 主要目標
-
更易于推論
-
Angular 1 vs Angular 2 變化偵測
-
基于 Zones 的更透明的內部構件
-
改進的堆棧跟蹤
-
大幅提升的性能 (以及原理)
-
改進的模塊化
-
改進的依賴注入
-
Web 組件友好 (如何達成以及原理)
-
支持影子 DOM
-
支持 Android 和 iOS 的原生移動渲染
-
支持服務端渲染
-
改進的可測試性
-
向 Angular 2 遷移的路徑
-
總結
Angular 2 主要目標
Angular 2 的主要目標是創建一個簡單易用并且快速工作的 web 框架。讓我們看看這是如何達到的:
目標:更易于推論
在當前版本的 Angular 中,我們有時不得已對應特定的使用場景推論框架內部構建,比如必須推論應用事件初始化和摘要循環:
-
在 Angular 1 中沒有摘要循環結束事件 (查看原因),因為這種事件可能會促發更多的變化,以至于使摘要循環持續下去
-
我們必須推論何時調用 $scope.apply 或 $cope.digest,而這并不總是容易的
-
有時我們必須調用 $timeoutto讓Angular 結束摘要循環,當 DOM 穩定時再做一些操作
為了使 Angular 2 更易于推論,一個目標是創建更多開箱即用的透明內部構建。開始之前,讓我們看看 Angular 1 的綁定機制是如何實現的,然后如何使它更透明。
Angular 1 如何實現綁定
Angular 1 這么流行的主要原因之一是,ng-model 功能可以使界面上的改動立即反應在一個簡單 Javascript 對象上。
根據這個 podcast (查看 3:50 處),Angular 1 是這樣完成此功能的:
-
Javascript 運行時中,每一樣東西都是可以依設計打補丁的 – 如果需要我們可以改變 Number 類
-
Angular 在啟動時會給所有的異步交互點打補丁:
-
超時
-
Ajax 請求
-
瀏覽器事件
-
Websockets,等等
-
在那些交互點,Angular 會對 scope 對象進行變動檢查,如果發現有變動就激發相應的監視器
-
重新運行變動檢查,檢查是否有更多的變化發生,重新運行監視器,等等
Angular 1 綁定運行的后果
結果是 DOM 一直同簡單 Javascript 對象進行同步,盡管這樣可以工作,但是這使得有時難以進行推論:
-
不清楚哪些監視器會運行,什么順序,多少次
-
模型更新順序難以推論和預期
-
摘要循環多次運行導致時間消耗
Angular 團隊制定 Angular 2 開發方向時,其中一點是提取 Angular 代碼中的異步交互點補丁機制,以便可以重用它。
Zones 介紹
這些重構的結果就是 Zone.js,它類似于 Java 中的 thread-local 上下文。
他可以用于很多場景,比如可以允許框架生成更長的跨越多個 JavaScript VM 的堆棧跟蹤信息。
Angular 2 如何因 Zones 而更透明
Angular 2 使用 zones 機制使摘要循環不再被需要。簡單的非 Angular 指定代碼可以透明地激發一個Angular 2 摘要,如下是由一個 zone 中的組件激發的示例:
element.addEventListener('keyup', function () { console.log('Key pressed.'); }); });
不再需要 $scope.apply 或 $scope.digest,每樣東西都透明地工作。或許我們不必推論出 zones 適用于大多數一般場景,但是可以通過使用 VmTurnZone 在 Angular zone 外運行代碼。
目標: 提升性能
上面描述的消化周期明確表示,這一切都將會耗費時間,盡管很多性能在 Angular 1.3 和 Angular 1.4 版本中得到改進。
但不清楚哪些性能可以改進更多,原因之一是存在變化檢測循環的可能性。
為了更好地理解如何實現性能提升(比 Angular 1 快5到10倍),參考了很多播客和博客 。我會盡量在這里總結 Angular 2 更快的兩個主要原因:
更為快速的檢測一個單向綁定
它提供了一項檢測單向綁定的機制,這項機制可以允許 Javascript 虛擬機對于代碼到源代碼的實時編譯進行優化和完善。相對于遞歸性掃描對像的變化,這份機制會創建一個方法,這個方法將在 Angular 啟動時去檢查這個綁定是否已經改變。有了這樣的一個檢測函數,我們很容易的自己親手編寫類似函數來測試綁定對象的變化,同時它也很容易被虛擬機優化。
避免掃描部分組件樹
Angular2 也可以讓開發者為變化檢測機制做出相應的一些保障,而不用不斷地掃描一部分的組件樹。就基本上來說,開發者將有兩個選擇:
-
創建一個可見的對象:Angular 將會發現這個對象并且注冊去觀察這個對象。在這種狀況下,如果這個對象發生改變或者保留原來的裝態,Angular 將會通過觀察機制獲得消息,所以就不需要為這個對象運行變化檢測機制。
-
創建一個不可見的對象, 可以使用 非死book 提供的 immutable.js。 同樣的,Angular 也會檢測到這個對象,而且也不需要變化檢測機制去檢測這個不可見的對象。
目標: 提升模塊化
在 Angular 1 中,Angular 的模塊幾乎都依賴于注入容器以及其他相關功能。
這些模塊的例子都不是異步加載的,以 AMD 模塊為例,根據他們的依賴性列出第一次的加載所需的依賴。
Angular 1 和模塊懶加載
Angular 1 的懶加載是類似于 ocLazyLoad 方式的解決方案,但是理想情況下應該是本地框架能更易懂,這在這個播客的 (13:06)的地方,Angular 2 也是這樣的情況。
將 npm 優化為前端包的管理工具
同時 Angular 團隊一直嘗試改進 NPM,讓它變得更加前端友好化;不僅僅是對于 Angular 而言,同時也是對于任何前端 library 而言。
而 Angular 2 則沒有這樣的問題,假如我們選擇npm, 我們完全可以利用新型的ES6 模塊加載器,ES6通過利用es6-module-loader pollyfill 使其變成一個標準的同步模塊加載器。
目標: 改進依賴注入
在Angular 1 的世界里,依賴注入在構建多模塊應用時是一項技術的飛躍, 但是在一些極端的案例中,如果不做出一些重要的變化是不能解決這些問題的。
Angular 1 包含對象全局池
Angular 1 其中一個 DI 案例中每個應用僅有一個對象全局池。這就意味著,如果說主路由 loadsbackendService 我們導向了路由 B,可以延遲加載其他服務指定到這個路由。
問題就是,我們說可以延遲加載一個 secondbackendService,使用完全不同的實現:這就會重寫第一個!當前還沒有辦法同一名字有兩個不同實現的兩個服務,這就會阻止用一個安全的方式從 Angular 1 實現延遲加載。
Angular 1 會靜默重寫模塊,當他們有相同的名字
這是一個特性,允許在測試的時候模擬替換服務層的服務,但是如果恰巧在同一模塊加載了兩次就會發生問題。
Angular 1 的多重依賴注入機制
在 Angular 1 中, 我們可以使用在多重地方使用不同的方法進行注入:
-
在鏈接方法中通過位置注入
-
在直接定義中通過名字注入
-
在controller方法中通過名字,等等。
Angular 2 將會作出怎樣的該進
而在 Angular 2 中有且僅有一種依賴注入機制: 在構造函數中通過類型注入。
constructor(keyUtils: KeyboardUtils) { this.keyUtils = keyUtils; } });
事實上,如果只有一種機制,那么它將變得更加容易學習。同時這種依賴注入器是類似層級結構,在不同層次的組件樹,有可能實現對相同類型的不同實現。
如果一個組件沒有定義依賴,它會代理給上層注入器查找依賴,依次往上。這讓 Angular 2 提供原生的懶加載成為可能。
目標:對 Web Component 友好
Web Components 是 web 的未來,Angular 2 一開始就打算跟未來的的 web component 庫良好協作。 為此,Angular 2 模板語法的一個目標就是保持特性定義簡潔,不將任何 Angular 表達式置于其中 —— 一切都通過屬性綁定。
為了理解為什么這個很重要,來看下面的例子:
<ng1-component> <web-component-widget setting="{{angularExpresssion}}"></web-component-widget> </ng1-component>
這里有個跟未來 web component 交互的 Angular 1 組件。
這里有什么問題呢?
web component 的行為跟瀏覽器組件的行為類似,比如有 img 標簽。
因此,在頁面初始化并且在 Angular 介入之前,Angular 表達式將被傳給組件,并直接作用于它。比如 image 元素用提供的 url 立即加載圖片。
這也是為什么需要像 ng-src 這樣的屬性來克服這個問題。
Angular 2 如何做到更好地跟 Web Components 交互?
Angular 2 的模板語法會避免綁定到普通屬性,除非要讀取常量:
<ng2-component> <web-component-widget [setting]="angularExpresssion"></web-component-widget> </ng2-component>
[setting] 是一個往組件屬性寫入表達式值的屬性綁定。為了避免跟 web component 互操作問題,在普通屬性里絕不會出現 Angular 表達式。
支持 Shadow DOM
Web 組件的主要特征之一就是 Shadow DOM。 這是瀏覽器自身的一種機制,它允許構建本地進行查找組件,看起來是select新的一種實現方式。
一個web組件還是可以通過正常的HTML/CSS 腳本實現,但是同時從主頁面隔離了。在某種程度上來說,就像是在同一個iframe里擁有各自的document根節點。
由于現階段只有 Chrome 才實現了 Shadow DOM, Angular 2 通過以下3種機制來支持它:
-
默認方式:默認情況下,Shadow DOM 不會和內部組件同時出現在同一個組件樹來做為主頁面。
-
模擬Shadow DOM:Shadow DOM CSS 隔離機制可以通過 Polymer 實現,這個類庫可以使的組件中的CSS動態地加上前綴,使得CSS更加清晰明白。
-
真正的Shadow DOM: 正如上文說的那樣,只有在 Chrome 瀏覽器中工作
目標:原生移動支持 – iOS 和 Android
Angular 2 會有兩層,應用層和渲染層。例如一個組件可以用不同的 @View 修飾器修飾,根據運行環境可以在運行時生效。
與 React Native 一樣,Angular 2 支持:
一次學習,到處書寫。
這意味著創建原生應用時可以重用你在創建 web 應用時學習的知識。盡管總是有些區別。
目標:為服務器端渲染提供支持
支持服務器端的渲染對于搜索引擎的優化和用戶感知體驗來說是非常重要的;在一個比較大型的Angular 1 的應用中,即使使用了預先定義的緩存模塊,我們可以清楚地看到當應用開始啟動時,頁面的加載過程。
這時候看來 Angualr2 的這部分特征不是很清晰明朗,但是這種思路或許可以從以下幾個方面得到體現:
-
啟動開始, 同時所有的組件都被綁定
-
而渲染沒有實現
-
一個頁面在服務器被渲染后 , 然后發送到客戶端
-
Angular 將會把它解析 ,接著會吧解析后的頁面注入到 DOM 中,這樣就避免了出現閃爍的效果
目標: 增加測試可行性
相對而言 Angular 2 很難寫真正的單元測試, 因為像 ng-model 真的需要一個 DOM 做測試,這導致這個方案就像使用 PhantomJs.
這個方式產生的問題是這種測試不再是單元測試,這種集成測試有下列問題:
-
執行緩慢
-
脆弱難以維護
這些問題導致一個倒置的 test pyramid, 進而我們大部分測試,包括UI測試,集成測試很難做到真正的單元測試。這意味著構造不斷被真正 bug 之外的東西打破,測試努力收效甚微。
引入獨立的渲染層會使單元測試更快,依賴更少,更方便代碼的書寫和維護,可以更頻繁地使用。
目標: 遷移到 Angular 2
Angular 2 的目標之一是為 Angualr 1 提供一個清晰的遷移路徑。Angular 2 最初版本發布臨近時這會變得更加清晰,但是現在路由可能是一個主要的可行遷移辦法。
新的 Angular 2 路由向下兼容 Angular 1,將允許一個工程同時有 Angualr 1 和 Angular 2 路由 。
結論
我真的為 Angular 2 感到興奮,在嘗試幾個組件之后,我可以看到它是如何的簡單易學,對開發者更加透明。很多事情就像這個文章前面說過的,像 Zones 很容易使用。
與第三方庫的集成大大改進了,如果 npm 也做一些改進對前端代碼的改進就是巨大的。
想嘗試嗎?
最好不要馬上嘗試,如果你想試試,這兒有一個seed project, Visual Studio Code editor 或者 Webstorm 已經提供 Typescript 1.5 支持.
本文地址:http://www.oschina.net/translate/angular-1-vs-angular-2-a-high-level-comparison
原文地址:https://dzone.com/articles/angular-1-vs-angular-2-a-high-level-comparison-1