Angular 1 vs. Angular 2 深度比較

類別: IT

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 將會通過觀察機制獲得訊息,所以就不需要為這個物件執行變化檢測機制。

  • 建立一個不可見的物件, 可以使用 Facebook 提供的 immutable.js。 同樣的,Angular 也會檢測到這個物件,而且也不需要變化檢測機制去檢測這個不可見的物件。

Goal: Improved Modularity

In Angular 1, the Angular modules are mostly dependency injection containers that group related functionality.

These modules are for example not asynchronously loaded based on their dependency listings the first time that a dependency is needed, like AMD modules for instance.

Angular 1 and Module Lazy-loading

Angular 1 lazy loading is still possible with a solution like ocLazyLoad, but ideally it should be something native to the framework and more transparent, and according to this podcast it seems that in Angular 2 it will be the case (see at 13:06).

還沒有人翻譯此段落

    我來翻譯

    Improvements to npm as a Frontend Package Manager

    Also in general the Angular team is looking into improving npm to make it more frontend friendly, not only for Angular but for any frontend library in general.

    Angular 2 does not force this, but if we choose to do so we can take advantage of the new ES6 module loader, which is a standard async module loader that can be already used via the es6-module-loaderpollyfill.

    Goal: Improved Dependency Injection

    Angular 1 dependency injection is a leap forward in building more modular applications, but it has a couple of corner cases that cannot be fixed anymore without major breaking changes.

    還沒有人翻譯此段落

      我來翻譯

      Angular 1 Has a Global Pool of Objects

      One of the DI corner cases in Angular 1 is that there is only one global pool of objects per application. This means that if for example the main route loads backendService and we navigate to route B, we could there lazy-load other services specific of that route.

      The problem is, let’s say we lazy load a second backendService with a completely different implementation: it would overwrite the first one! There is currently no way to have two services with the same name but different implementations, which prevents lazy-loading from being implemented in Angular 1 in a safe way.

      Angular 1 Silently Overwrites Modules If They Have the Same Name

      This is a feature to allow for example to replace the service layer services with mocks at test time, but might cause issues if we accidentally load two times the same module.

      還沒有人翻譯此段落

        我來翻譯

        In Angular 1 There Are Multiple DI Mechanisms

        In Angular 1, we can inject dependencies in multiple places in different ways:

        • in the link function by position

        • in the directive definition by name

        • in the controller function by name, etc.

        In Angular 2 there is the goal to unify these mechanism into a single mechanism, for reducing the learning curve and improve readability.

        How Will Angular 2 DI Improve the Situation

        In Angular 2 there will be only one DI mechanism: constructor injection by type.

        constructor(keyUtils: KeyboardUtils) {        this.keyUtils = keyUtils;    }});

        The fact that there is only one mechanism makes it easier to learn. Also the dependency injector is hierarchical, meaning that at different points of the component tree it’s possible to have different implementations of the same type.

        還沒有人翻譯此段落

          我來翻譯

          If a component does not have a dependency defined, it will delegate the lookup to it’s parent injector and so forth. This sets the ground for providing native lazy-loading support in Angular 2.

          Goal: Web Component Friendly

          The future of the web is Web Components, and Angular 2 wants from the start to play well with future web component libraries. For this, one of the goals of the Angular 2 template syntax is to keep attributes clean and don’t put any Angular expressions on them – everything is binded via properties only.

          To understand why this is important, take a look at this example:

          <ng1-component>     <web-component-widget setting="{{angularExpresssion}}"></web-component-widget> </ng1-component>

          Here we have an Angular 1 component that interacts with a future web component library.

          還沒有人翻譯此段落

            我來翻譯

            What is the Problem Here?

            Well, the web component behaves just like a browser component, such has for example the img tag.

            Therefore at page startup time and before Angular gets a chance to kick in, the Angular expression will be passed to the component which would act upon it directly, like the image element immediately loads the image using the url provided.

            This is actually the reason why all the attributes like ng-src are needed, to work around this issue.

            How Angular 2 Will Interact Better With Web Components?

            In Angular 2, the template syntax will avoid to bind to plain attributes, unless to read constants:

            <ng2-component>       <web-component-widget [setting]="angularExpresssion"></web-component-widget></ng2-component>

            The [setting] is a property binding that will write a value of an expression into a component property. In no place will there be Angular expressions in plain attributes, to prevent interoperability problems with web components.

            還沒有人翻譯此段落

              我來翻譯

              Support for Shadow DOM

              One of the key features of web components is the Shadow DOM. This is a native browser mechanism that will allow to build native looking components, say a new implementation of select.

              A web component can still be implemented in plain HTML/CSS but isolated from the main page, in some ways in a similar way to as if it where inside an iframe with a separate document root.

              Because the Shadow DOM is currently only implemented in Chrome, Angular 2 will support it via 3 different mechanisms:

              • default mode: by default the Shadow DOM is not on and the components internals show up in the same document tree as the main page

              • emulated Shadow DOM: the Shadow DOM CSS isolation mechanism can be pollyfiled using Polymer by having the CSS inside the component being prefixed on the fly to make the CSS more specific

              • true Shadow DOM: as mentioned this only works in Chrome

              還沒有人翻譯此段落

                我來翻譯

                目標:原生移動支援 – iOS 和 Android

                Angular 2 會有兩層,應用層和渲染層。例如一個元件可以用不同的 @View 修飾器修飾,根據執行環境可以在執行時生效。

                與 React Native 一樣,Angular 2 支援:

                一次學習,到處書寫。

                這意味著建立原生應用時可以重用你在建立 web 應用時學習的知識。儘管總是有些區別。

                Goal: Support for Server Side Rendering

                Support for server-side rendering is important for SEO and user perception: in a large Angular 1 app we can clearly see the bootstrap process of the page while the startup is ongoing, even if using template cache pre-population.

                This is one of the parts in Angular 2 that is less clear at this moment, but the idea might be implemented like this:

                • the startup occurs and all components get binded

                • but the rendering does not occur

                • a page gets rendered in the server and sent over the wire

                • Angular will parse it and inject parts of the page into the DOM, avoiding the flickering effect

                已有1人翻譯此段(待審批)

                • skull

                  skull

                我來翻譯

                目標: 增加測試可行性

                相對而言 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 支援.

                Angular 1 vs. Angular 2 深度比較原文請看這裡