填寫這份《一分鐘調查》,幫我們(開發組)做得更好!去填寫Home

更關注效能的升級方式

Upgrading for performance

Angular 是當前以及未來的 Angular 名稱。
AngularJS 特指 Angular 的所有 1.x 版本。

Angular is the name for the Angular of today and tomorrow.
AngularJS is the name for all 1.x versions of Angular.

本指南介紹了一些用來將 AngularJS 專案高效地逐塊遷移到 Angular 平臺上的工具。 本章和從 AngularJS 升級很像,但是這裡會用輔助函式 downgradeModule() 取代 UpgradeModule。這會影響到應用如何啟動,以及變更檢測事件如何在兩個框架之間傳播。 它能讓你逐步升級,並提高混合式應用的執行速度,並讓你能在升級過程中儘早用上 Angular 中的最新特性。

This guide describes some of the built-in tools for efficiently migrating AngularJS projects over to the Angular platform, one piece at a time. It is very similar to Upgrading from AngularJS with the exception that this one uses the downgradeModule() helper function instead of the UpgradeModule class. This affects how the app is bootstrapped and how change detection is propagated between the two frameworks. It allows you to upgrade incrementally while improving the speed of your hybrid apps and leveraging the latest of Angular in AngularJS apps early in the process of upgrading.

準備工作

Preparation

在討論你應該如何用 downgradeModule() 來建立混合式應用之前,你可以先採取一些措施來簡化升級過程,甚至在開始升級之前就可以做。 無論你用哪種方式升級,這些步驟都是一樣的,請參考從 AngularJS 升級準備工作部分。

Before discussing how you can use downgradeModule() to create hybrid apps, there are things that you can do to ease the upgrade process even before you begin upgrading. Because the steps are the same regardless of how you upgrade, refer to the Preparation section of Upgrading from AngularJS.

使用 ngUpgrade 升級

Upgrading with ngUpgrade

使用 Angular 中的 ngUpgrade 函式庫,你可以透過建構混合式應用來逐步升級現有的 AngularJS 應用。在這些混合式應用中,你可以混用 AngularJS 和 Angular 的元件與服務,並讓它們天衣無縫地進行互操作。 這意味著你不用一次性完成遷移工作,因為在過渡階段兩個框架可以自然共存。

With the ngUpgrade library in Angular you can upgrade an existing AngularJS app incrementally by building a hybrid app where you can run both frameworks side-by-side. In these hybrid apps you can mix and match AngularJS and Angular components and services and have them interoperate seamlessly. That means you don't have to do the upgrade work all at once as there is a natural coexistence between the two frameworks during the transition period.

ngUpgrade 的工作原理

How ngUpgrade Works

無論選擇 downgradeModule() 還是 UpgradeModule,升級的基本原則都是一樣的:無論是混合式應用背後的心智模型,還是 upgrade/static 的用法。 要了解更多,參閱從 AngularJS 升級ngUpgrade 工作原理部分。

Regardless of whether you choose downgradeModule() or UpgradeModule, the basic principles of upgrading, the mental model behind hybrid apps, and how you use the upgrade/static utilities remain the same. For more information, see the How ngUpgrade Works section of Upgrading from AngularJS.

從 AngularJS 升級中的變更檢測部分僅僅適用於使用 UpgradeModule 的應用。 雖然你處理變更檢測的方式和 downgradeModule()(本章的重點)不同,不過讀一下變更檢測部分還是能為後續內容提供一些有用的上下文知識。

The Change Detection section of Upgrading from AngularJS only applies to apps that use UpgradeModule. Though you handle change detection differently with downgradeModule(), which is the focus of this guide, reading the Change Detection section provides helpful context for what follows.

使用 downgradeModule() 進行變更檢測

Change Detection with downgradeModule()

如前所述,downgradeModule()UpgradeModule 之間的一個關鍵區別,就是如何進行變更檢測,以及檢測結果如何在兩個框架之間傳播。

As mentioned before, one of the key differences between downgradeModule() and UpgradeModule has to do with change detection and how it is propagated between the two frameworks.

使用 UpgradeModule,兩套變更檢測系統綁得更緊密一些。 一旦應用中的 AngularJS 部分發生了某些變化,變更檢測就會自動在 Angular 部分觸發它,反之亦然。 這很方便,因為它保證了任何一個框架都不會丟失重要的變更。不過,其實大多數情況下並不需要執行這些額外的變更檢測。

With UpgradeModule, the two change detection systems are tied together more tightly. Whenever something happens in the AngularJS part of the app, change detection is automatically triggered on the Angular part and vice versa. This is convenient as it ensures that neither framework misses an important change. Most of the time, though, these extra change detection runs are unnecessary.

downgradeModule() 會避免顯式觸發變更檢測,除非它確信應用的其它部分對此感興趣。 比如,如果被降級的元件定義了 @Input(),當那個值發生變化時,應用就可能需要知道。 因此,downgradeComponent() 就會自動在該元件上觸發變更檢測。

downgradeModule(), on the other side, avoids explicitly triggering change detection unless it knows the other part of the app is interested in the changes. For example, if a downgraded component defines an @Input(), chances are that the app needs to be aware when that value changes. Thus, downgradeComponent() automatically triggers change detection on that component.

但是,大多數情況下,應用的其它地方並不會關心某個元件中進行的區域性更改。 比如,如果使用者點選了某個表單的提交按鈕,通常會由元件自行處理這個操作的結果。 話雖如此,但在某些情況下,你可能希望把這些變化傳播到應用中由另一個框架控制的部分。 這時候,你就有責任透過手動觸發變更檢測來通知相關方。

In most cases, though, the changes made locally in a particular component are of no interest to the rest of the app. For example, if the user clicks a button that submits a form, the component usually handles the result of this action. That being said, there are cases where you want to propagate changes to some other part of the app that may be controlled by the other framework. In such cases, you are responsible for notifying the interested parties by manually triggering change detection.

如果你希望某些程式碼片段在應用的 AngularJS 部分觸發變更檢測,就要把它包在 scope.$apply() 中。 同樣,要想在 Angular 中觸發變更檢測,就要呼叫 ngZone.run()

If you want a particular piece of code to trigger change detection in the AngularJS part of the app, you need to wrap it in scope.$apply(). Similarly, for triggering change detection in Angular you would use ngZone.run().

很多情況下,是否執行額外的變更檢測可能並不重要。不過,在較大或變更檢測較多的應用中,它們可能會產生顯著地影響。 透過讓你更精細的控制變更檢測的傳播方式,downgradeModule() 可以讓你的混合式應用達到更好地效能。

In many cases, a few extra change detection runs may not matter much. However, on larger or change-detection-heavy apps they can have a noticeable impact. By giving you more fine-grained control over the change detection propagation, downgradeModule() allows you to achieve better performance for your hybrid apps.

使用 downgradeModule()

Using downgradeModule()

AngularJS 和 Angular 都有自己的模組概念,來幫你把應用按功能組織成內聚的程式碼塊。

Both AngularJS and Angular have their own concept of modules to help organize an app into cohesive blocks of functionality.

它們在架構和實現方面的細節有很大不同。在 AngularJS 中,你可以用 angular.module() 指定名字和依賴,以建立一個模組。 然後,你可以使用它的各種方法新增資產。在 Angular 中,你要建立一個帶有 NgModule 裝飾器的類別,靠這個裝飾器的元資料來描述這些資產。

Their details are quite different in architecture and implementation. In AngularJS, you create a module by specifying its name and dependencies with angular.module(). Then you can add assets using its various methods. In Angular, you create a class adorned with an NgModule decorator that describes assets in metadata.

在混合式應用中,你同時執行著兩個框架。這意味著你至少需要一個來自 AngularJS 的模組和一個來自 Angular 的模組。

In a hybrid app you run both frameworks at the same time. This means that you need at least one module each from both AngularJS and Angular.

大多數情況下,你可以使用與常規應用程式相同的方式來指定模組。然後,使用 upgrade/static 輔助函式來讓兩個框架了解對方使用的資產。這叫做"升級(upgrading)"和"降級(downgrading)"。

For the most part, you specify the modules in the same way you would for a regular app. Then, you use the upgrade/static helpers to let the two frameworks know about assets they can use from each other. This is known as "upgrading" and "downgrading".

定義:

Definitions:

  • 升級:讓 AngularJS 中的資產,比如元件或服務,可用於應用中的 Angular 部分。

    Upgrading: The act of making an AngularJS asset, such as a component or service, available to the Angular part of the app.

  • 降級:讓 Angular 中的資產,比如元件或服務,可用於應用中的 AngularJS 部分

    Downgrading: The act of making an Angular asset, such as a component or service, available to the AngularJS part of the app.

依賴互聯中最重要的部分之一是把兩個主模組聯結在一起。這就是 downgradeModule() 的用武之地。使用它來建立 AngularJS 模組(你可以在 AngularJS 主模組中把這個模組用作依賴項),該模組將引導你的 Angular 主模組,並啟動混合式應用中的 Angular 部分。從某種意義上說,它把 NgModule "降級"成了 AngularJS 模組。

An important part of inter-linking dependencies is linking the two main modules together. This is where downgradeModule() comes in. Use it to create an AngularJS module—one that you can use as a dependency in your main AngularJS module—that will bootstrap your main Angular module and kick off the Angular part of the hybrid app. In a sense, it "downgrades" an Angular module to an AngularJS module.

有幾點需要注意:

There are a few things to note, though:

  1. 你不必把 Angular 模組直接傳給 downgradeModule()downgradeModule() 所需要的只是一個用來建立模組實例 "配方"(比如工廠函式)。

    You don't pass the Angular module directly to downgradeModule(). All downgradeModule() needs is a "recipe", for example, a factory function, to create an instance for your module.

  2. 除非應用實際用到了,否則不會初始化這個 Angular 模組。

    The Angular module is not instantiated until the app actually needs it.

下面是如何使用 downgradeModule() 來聯結兩個模組的例子。

The following is an example of how you can use downgradeModule() to link the two modules.

      
      // Import `downgradeModule()`.
import { downgradeModule } from '@angular/upgrade/static';

// Use it to downgrade the Angular module to an AngularJS module.
const downgradedModule = downgradeModule(MainAngularModuleFactory);

// Use the downgraded module as a dependency to the main AngularJS module.
angular.module('mainAngularJsModule', [
  downgradedModule
]);
    

為 Angular 模組指定一個工廠

Specifying a factory for the Angular module

如前所述,downgradeModule() 需要知道如何實例化 Angular 模組。你可以透過提供可以建立 Angular 模組實例的工廠函式來定義該配方。 downgradeModule() 接受兩種型別的工廠函式:

As mentioned earlier, downgradeModule() needs to know how to instantiate the Angular module. It needs a recipe. You define that recipe by providing a factory function that can create an instance of the Angular module. downgradeModule() accepts two types of factory functions:

  1. NgModuleFactory
  2. (extraProviders: StaticProvider[]) => Promise<NgModuleRef>

當傳入 NgModuleFactory 時,downgradeModule() 會把它傳給 platformBrowserbootstrapModuleFactory() 來實例化模組。它與預先(AOT)編譯模式相容。 預先編譯能讓你的應用載入更快。要了解預先編譯的更多知識,以及如何建立 NgModuleFactory,參閱 預先編譯 章。

When you pass an NgModuleFactory, downgradeModule() uses it to instantiate the module using platformBrowser's bootstrapModuleFactory(), which is compatible with ahead-of-time (AOT) compilation. AOT compilation helps make your apps load faster. For more about AOT and how to create an NgModuleFactory, see the Ahead-of-Time Compilation guide.

另外,你還可以傳入一個普通函式,它要返回一個解析為 NgModuleRef(比如你的 Angular 模組) 的 Promise。該函式接收一個額外 Providers 的陣列,這個陣列可以在所返回 NgModuleRefInjector 中可用。 例如,如果你在使用 platformBrowserplatformBrowserDynamic,就可以把 extraProviders 陣列傳給它們:

Alternatively, you can pass a plain function, which is expected to return a promise resolving to an NgModuleRef (i.e. an instance of your Angular module). The function is called with an array of extra Providers that are expected to be available on the returned NgModuleRef's Injector. For example, if you are using platformBrowser or platformBrowserDynamic, you can pass the extraProviders array to them:

      
      const bootstrapFn = (extraProviders: StaticProvider[]) => {
  const platformRef = platformBrowserDynamic(extraProviders);
  return platformRef.bootstrapModule(MainAngularModule);
};
// or
const bootstrapFn = (extraProviders: StaticProvider[]) => {
  const platformRef = platformBrowser(extraProviders);
  return platformRef.bootstrapModuleFactory(MainAngularModuleFactory);
};
    

使用 NgModuleFactory 需要更少的樣板程式碼,並且是一個很好的預設選項,因為它支援 AOT 開箱即用。 使用自訂函式需要稍多的程式碼,但是給你提供了更大的靈活性。

Using an NgModuleFactory requires less boilerplate and is a good default option as it supports AOT out-of-the-box. Using a custom function requires slightly more code, but gives you greater flexibility.

按需實例化 Angular 模組

Instantiating the Angular module on-demand

downgradeModule()UpgradeModule 之間的另一個關鍵區別,就是後者要求你預先實例化 AngularJS 和 Angular 的模組。 這意味著你必須為實例化應用中的 Angular 而付出代價 —— 即使你以後不會用到任何 Angular 資產。 downgradeModule() 則不那麼激進。它只會在第一次用到時才實例化 Angular 部分,也就是說,當它需要實例化一個降級後的元件時。

Another key difference between downgradeModule() and UpgradeModule is that the latter requires you to instantiate both the AngularJS and Angular modules up-front. This means that you have to pay the cost of instantiating the Angular part of the app, even if you don't use any Angular assets until later. downgradeModule() is again less aggressive. It will only instantiate the Angular part when it is required for the first time; that is, as soon as it needs to create a downgraded component.

你還可以更進一步,甚至不必將應用程式中 Angular 部分的程式碼下載到使用者的瀏覽器中 —— 直到需要它的那一刻。 當不需要初始渲染或使用者尚未訪問到混合式應用中的 Angular 部分時,這特別有用。

You could go a step further and not even download the code for the Angular part of the app to the user's browser until it is needed. This is especially useful when you use Angular on parts of the hybrid app that are not necessary for the initial rendering or that the user doesn't reach.

舉一些例子:

A few examples are:

  • 你只想在特定的路由上使用 Angular,除非使用者訪問此路由,否則你不需要它。

    You use Angular on specific routes only and you don't need it until/if a user visits such a route.

  • 你可以將 Angular 用於僅對特定型別的使用者可見的特性,比如:登入使用者、管理員或 VIP 成員。這樣在使用者通過了身份驗證之前,你都無需載入 Angular。

    You use Angular for features that are only visible to specific types of users; for example, logged-in users, administrators, or VIP members. You don't need to load Angular until a user is authenticated.

  • 你可以把 Angular 用於應用中那些在初始渲染時不太重要的特性,並且願意為了更好地初始載入效能,而忍受載入該特性時的一點延遲。

    You use Angular for a feature that is not critical for the initial rendering of the app and you can afford a small delay in favor of better initial load performance.

透過 downgradeModule() 啟動

Bootstrapping with downgradeModule()

你可能已經猜到了,你不需要修改引導現有 AngularJS 應用的方式。UpgradeModule 需要一些額外的步驟,但 downgradeModule() 能自行引導 Angular 模組,你只要為它提供配方即可。

As you might have guessed, you don't need to change anything in the way you bootstrap your existing AngularJS app. Unlike UpgradeModule—which requires some extra steps— downgradeModule() is able to take care of bootstrapping the Angular module, as long as you provide the recipe.

要開始使用任何 upgrade/static API,你仍然要像在普通 Angular 應用中一樣載入 Angular 框架。要想用 SystemJS 做到這一點,你可以遵循升級的準備工作中的指導,有選擇的從快速上手專案的 Github 儲存庫中複製程式碼。

In order to start using any upgrade/static APIs, you still need to load the Angular framework as you would in a normal Angular app. You can see how this can be done with SystemJS by following the instructions in the Upgrade Setup guide, selectively copying code from the QuickStart github repository.

你還需要用 npm install @angular/upgrade --save 安裝 @angular/upgrade 套件,並新增一個指向 @angular/upgrade/static 套件的對映:

You also need to install the @angular/upgrade package via npm install @angular/upgrade --save and add a mapping for the @angular/upgrade/static package:

system.config.js
      
      '@angular/upgrade/static': 'npm:@angular/upgrade/bundles/upgrade-static.umd.js',
    

接下來,建立一個 app.module.ts 檔案,並新增如下 NgModule 類別:

Next, create an app.module.ts file and add the following NgModule class:

app.module.ts
      
      import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

@NgModule({
  imports: [
    BrowserModule
  ]
})
export class MainAngularModule {
  // Empty placeholder method to satisfy the `Compiler`.
  ngDoBootstrap() {}
}
    

這個最小的 NgModule 匯入了 BrowserModule,Angular 每個基於瀏覽器的應用都會匯入該模組。 它還定義了一個空的 ngDoBootstrap() 方法,來防止 Compiler 返回錯誤。 在這裡它是必要的,因為 NgModule 裝飾器上還沒有宣告 bootstrap

This bare minimum NgModule imports BrowserModule, the module every Angular browser-based app must have. It also defines an empty ngDoBootstrap() method, to prevent the Compiler from returning errors. This is necessary because the module will not have a bootstrap declaration on its NgModule decorator.

你不用把 bootstrap 宣告加到 NgModule 裝飾器上,因為 AngularJS 擁有應用的根元件,並且 ngUpgrade 會負責啟動必要的元件。

You do not add a bootstrap declaration to the NgModule decorator since AngularJS owns the root template of the app and ngUpgrade bootstraps the necessary components.

現在你可以用 downgradeModule() 把 AngularJS 和 Angular 的模組聯結在一起。

You can now link the AngularJS and Angular modules together using downgradeModule().

app.module.ts
      
      import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { downgradeModule } from '@angular/upgrade/static';

const bootstrapFn = (extraProviders: StaticProvider[]) => {
  const platformRef = platformBrowserDynamic(extraProviders);
  return platformRef.bootstrapModule(MainAngularModule);
};
const downgradedModule = downgradeModule(bootstrapFn);

angular.module('mainAngularJsModule', [
  downgradedModule
]);
    

現有的 AngularJS 程式碼仍然在和以前一樣正常工作,但你已經可以開始新增新的 Angular 程式碼了。

The existing AngularJS code works as before and you are ready to start adding Angular code.

使用元件與可注入物件

Using Components and Injectables

downgradeModule()UpgradeModule 之間的區別就是這些。 其餘的 upgrade/static API 和概念的工作方式在不同的混合式應用中都完全一樣了。 欲知詳情,參閱從 AngularJS 升級

The differences between downgradeModule() and UpgradeModule end here. The rest of the upgrade/static APIs and concepts work in the exact same way for both types of hybrid apps. See Upgrading from AngularJS to learn about:

雖然可以降級可注入物件,但在實例化 Angular 模組之前,無法使用降級後的可注入物件。 安全起見,你需要確保降級後的可注入物件不會用於應用中不受 Angular 控制的任何地方。

While it is possible to downgrade injectables, downgraded injectables will not be available until the Angular module that provides them is instantiated. In order to be safe, you need to ensure that the downgraded injectables are not used anywhere outside the part of the app where it is guaranteed that their module has been instantiated.

比如,在只使用 Angular 元件的已升級元件中可以使用降級後的服務,但是,不能在那些不依賴 Angular 的 AngularJS 元件中使用它,也不能從其它模組中使用降級過的 Angular 元件。

For example, it is OK to use a downgraded service in an upgraded component that is only used from a downgraded Angular component provided by the same Angular module as the injectable, but it is not OK to use it in an AngularJS component that may be used independently of Angular or use it in a downgraded Angular component from a different module.

使用混合式應用進行預先編譯

Using ahead-of-time compilation with hybrid apps

你可以像在任何其它 Angular 應用中一樣,利用混合式應用的預先(AOT)編譯功能。 混合式應用的設定與預先(AOT)編譯一章所講的大致相同,但 index.htmlmain-aot.ts 略有差異。

You can take advantage of ahead-of-time (AOT) compilation in hybrid apps just like in any other Angular app. The setup for a hybrid app is mostly the same as described in the Ahead-of-Time Compilation guide save for differences in index.html and main-aot.ts.

AOT 需要在 AngularJS 的 index.html 中的 <script> 標籤中載入所有 AngularJS 檔案。

AOT needs to load any AngularJS files that are in the <script> tags in the AngularJS index.html. An easy way to copy them is to add each to the copy-dist-files.js file.

你還要將所產生的 MainAngularModuleFactory 傳給 downgradeModule() 函式,而不是自訂引導函式。

You also need to pass the generated MainAngularModuleFactory to downgradeModule() instead of the custom bootstrap function:

app/main-aot.ts
      
      import { downgradeModule } from '@angular/upgrade/static';
import { MainAngularModuleNgFactory } from '../aot/app/app.module.ngfactory';

const downgradedModule = downgradeModule(MainAngularModuleNgFactory);

angular.module('mainAngularJsModule', [
  downgradedModule
]);
    

這就是當你想讓混合式應用受益於 AOT 時所要做的一切。

And that is all you need to do to get the full benefit of AOT for hybrid Angular apps.

總結

Conclusion

該頁面介紹瞭如何藉助 upgrade/static 套件,來按照你自己的節奏逐步升級現有的 AngularJS 應用。並且升級過程中不會方案此應用的進一步開發。

This page covered how to use the upgrade/static package to incrementally upgrade existing AngularJS apps at your own pace and without impeding further development of the app for the duration of the upgrade process.

具體來說,本章介紹瞭如何使用 downgradeModule() 來代替 UpgradeModule,為混合式應用提供更好的效能和更大的靈活性。

Specifically, this guide showed how you can achieve better performance and greater flexibility in your hybrid apps by using downgradeModule() instead of UpgradeModule.

總結,downgradeModule() 中的關鍵差異性因素是:

To summarize, the key differentiating factors of downgradeModule() are:

  1. 它允許實例化甚至延遲載入 Angular 部分,這能改善初始載入時間。某些情況下,這可能會完全免除啟動第二個框架的成本。

    It allows instantiating or even loading the Angular part lazily, which improves the initial loading time. In some cases this may waive the cost of running a second framework altogether.

  2. 透過避免執行不必要的變更檢測,它提高了效能,給開發人員提供了更大的自訂能力。

    It improves performance by avoiding unnecessary change detection runs while giving the developer greater ability to customize.

  3. 它不需要你更改引導 AngularJS 應用的方式。

    It does not require you to change how you bootstrap your AngularJS app.

當你希望混合式應用的 AngularJS 部分和 Angular 部分保持鬆耦合時,使用 downgradeModule() 是個很好的選擇。 你仍然可以混用並匹配兩個框架中的元件和服務。作為回報,downgradeModule() 為你提供了更大的控制權和更好的效能。

Using downgradeModule() is a good option for hybrid apps when you want to keep the AngularJS and Angular parts less coupled. You can still mix and match components and services from both frameworks, but you might need to manually propagate change detection. In return, downgradeModule() offers more control and better performance.