feat: 翻译完了 NgModule 部分

This commit is contained in:
Zhicheng Wang 2018-03-21 14:02:05 +08:00
parent 37e19cd50b
commit 2b6e7c8ca1
5 changed files with 384 additions and 6 deletions

View File

@ -1,5 +1,7 @@
# Lazy Loading Feature Modules
# 惰性加载的特性模块
#### Prerequisites
#### 前提条件
@ -10,38 +12,63 @@ A basic understanding of the following:
* [Feature Modules](guide/feature-modules).
[特性模块](guide/feature-modules).
* [JavaScript Modules vs. NgModules](guide/ngmodule-vs-jsmodule).
[JavaScript 模块与 NgModules](guide/ngmodule-vs-jsmodule).
* [Frequently Used Modules](guide/frequent-ngmodules).
[常用模块](guide/frequent-ngmodules).
* [Types of Feature Modules](guide/module-types).
[特性模块的分类](guide/module-types).
* [Routing and Navigation](guide/router).
[路由与导航](guide/router).
For the final sample app with two lazy loaded modules that this page describes, see the
<live-example></live-example>.
如果需要本页描述的具有两个惰性加载模块的范例应用,参见<live-example></live-example>
<hr>
## High level view
## 高层视角
There are three main steps to setting up a lazy loaded feature module:
要想建立一个惰性加载的特性模块,有三个主要步骤:
1. Create the feature module.
创建该特性模块。
1. Create the feature modules routing module.
创建该特性模块的路由模块。
1. Configure the routes.
配置相关路由。
## Set up an app
## 建立应用
If you dont already have an app, you can follow the steps below to
create one with the CLI. If you do already have an app, skip to
[Configure the routes](#config-routes). Enter the following command
where `customer-app` is the name of your app:
如果你还没有应用,可以遵循下面的步骤使用 CLI 创建一个。如果已经有了,可以直接跳到 [配置路由](#config-routes)部分。
输入下列命令,其中的 `customer-app` 表示你的应用名称:
```sh
ng new customer-app --routing
@ -53,11 +80,18 @@ generates a file called `app-routing.module.ts`, which is one of
the files you need for setting up lazy loading for your feature module.
Navigate into the project by issuing the command `cd customer-app`.
这会创建一个名叫 `customer-app` 的应用,而 `--routing` 标识生成了一个名叫 `app-routing.module.ts` 的文件,它是你建立惰性加载的特性模块时所必须的。
输入命令 `cd customer-app` 进入该项目。
## Create a feature module with routing
## 创建一个带路由的特性模块
Next, youll need a feature module to route to. To make one, enter
the following command at the terminal window prompt where `customers` is the name of the module:
接下来,你需要一个要路由到的特性模块。要生成一个,请输入下列命令,其中的 `customers` 是该模块的名字:
```sh
ng generate module customers --routing
@ -70,14 +104,24 @@ for anything that concerns customers. `CustomersRoutingModule` will handle
any customer-related routing. This keeps the apps structure organized as
the app grows and allows you to reuse this module while easily keeping its routing intact.
这会创建一个 `customers` 目录,其中有两个文件:`CustomersModule` 和 `CustomersRoutingModule`
`CustomersModule` 扮演的是与客户紧密相关的所有事物的管理员。`CustomersRoutingModule` 则会处理任何与客户有关的路由。
这样就可以在应用不断成长时保持应用的良好结构,并且当复用本模块时,你可以轻松的让其路由保持完好。
The CLI imports the `CustomersRoutingModule` into the `CustomersModule` by
adding a JavaScript import statement at the top of the file and adding
`CustomersRoutingModule` to the `@NgModule` `imports` array.
CLI 会把 `CustomersRoutingModule` 自动导入到 `CustomersModule`。它会在文件的顶部添加一条 JavaScript 的 `import` 语句,并把 `CustomersRoutingModule` 添加到 `@NgModule``imports` 数组中。
## Add a component to the feature module
## 向特性模块中添加组件
In order to see the module being lazy loaded in the browser, create a component to render some HTML when the app loads `CustomersModule`. At the command line, enter the following:
要想在浏览器中看出该模块惰性加载成功了,就创建一个组件用来在应用加载 `CustomersModule` 之后渲染出一些 HTML。在命令行中输入如下命令
```sh
ng generate component customers/customer-list
@ -87,16 +131,24 @@ ng generate component customers/customer-list
This creates a folder inside of `customers` called `customer-list`
with the four files that make up the component.
这会在 `customers` 目录中创建一个名叫 `customer-list` 的文件夹,其中包含该组件的四个文件。
<!-- For more information
about components, see [Components](). -->
Just like with the routing module, the CLI imports the
`CustomerListComponent` into the `CustomersModule`.
就像路由模块一样CLI 也自动把 `CustomerListComponent` 导入了 `CustomersModule`
## Add another feature module
## 再添加一个特性模块
For another place to route to, create a second feature module with routing:
为了提供另一个可路由到的地点,再创建第二个带路由的特性模块:
```sh
ng generate module orders --routing
@ -105,8 +157,12 @@ ng generate module orders --routing
This makes a new folder called `orders` containing an `OrdersModule` and an `OrdersRoutingModule`.
这会创建一个名叫 `orders` 的新文件夹,其中包含 `OrdersModule``OrdersRoutingModule`
Now, just like with the `CustomersModule`, give it some content:
现在,像 `CustomersModule` 一样,给它添加一些内容:
```sh
ng generate component orders/order-list
@ -115,17 +171,24 @@ ng generate component orders/order-list
## Set up the UI
## 建立 UI
Though you can type the URL into the address bar, a nav
is easier for the user and more common. Replace the default
placeholder markup in `app.component.html` with a custom nav
so you can easily navigate to your modules in the browser:
虽然你也可以在地址栏中输入 URL不过导航菜单会更好用而且更常见。
`app.component.html` 中的占位脚本替换成一个自定义的导航,以便你在浏览器中能轻松地在模块之间导航。
<code-example path="lazy-loading-ngmodules/src/app/app.component.html" region="app-component-template" title="src/app/app.component.html" linenums="false">
</code-example>
To see your app in the browser so far, enter the following command in the terminal window:
要想在浏览器中看到你的应用,就在终端窗口中输入下列命令:
```sh
ng serve
@ -134,106 +197,174 @@ ng serve
Then go to `localhost:4200` where you should see “app works!” and three buttons.
然后,跳转到 `localhost:4200`,这时你应该看到 “app works!” 和三个按钮。
<figure>
<img src="generated/images/guide/lazy-loading-ngmodules/three-buttons.png" width="300" alt="three buttons in the browser">
</figure>
To make the buttons work, you need to configure the routing modules.
要想让这些按钮生效,你需要配置一下这些路由模块。
{@a config-routes}
## Configure the routes
## 配置路由
The two feature modules, `OrdersModule` and `CustomersModule`, have to be
wired up to the `AppRoutingModule` so the router knows about them. The structure is as follows:
这两个特性模块(`OrdersModule` 和 `CustomersModule`)应该挂接到 `AppRoutingModule` 中,来让路由器知道它们。其结构如下:
<figure>
<img src="generated/images/guide/lazy-loading-ngmodules/lazy-load-relationship.jpg" width="400" alt="lazy loaded modules diagram">
</figure>
Each feature module acts as a doorway via the router. In the `AppRoutingModule`, you configure the routes to the feature modules, in this case `OrdersModule` and `CustomersModule`. This way, the router knows to go to the feature module. The feature module then connects the `AppRoutingModule` to the `CustomersRoutingModule` or the `OrdersRoutingModule`. Those routing modules tell the router where to go to load relevant components.
每个特性模块都是路由器中的一个“门口”。在 `AppRoutingModule` 中,你配置了一些路由指向这些特性模块(即 `OrderModule``CustomersModule`)。
通过这种方式,路由器就知道了如何跳转到特性模块。然后,特性模块就把 `AppRoutingModule``CustomersRoutingModule``OrdersRoutingModule` 连接到一起。这些路由模块会告诉路由器该到哪里去加载相应的组件。
### Routes at the app level
### 顶层的路由
In `AppRoutingModule`, update the `routes` array with the following:
`AppRoutingModule` 中,把 `routes` 数组修改成这样:
<code-example path="lazy-loading-ngmodules/src/app/app-routing.module.ts" region="const-routes" title="src/app/app-routing.module.ts" linenums="false">
</code-example>
The import statements stay the same. The first two paths are the routes to the `CustomersModule` and the `OrdersModule` respectively. Notice that the lazy loading syntax uses `loadChildren` followed by a string that is the path to the module, a hash mark or `#`, and the modules class name.
这些 `import` 语句没有变化。前两个路径分别路由到了 `CustomersModule``OrdersModule`。注意看惰性加载的语法:`loadChildren` 后面紧跟着一个字符串,它指向模块路径,然后是一个 `#`,然后是该模块的类名。
### Inside the feature module
### 特性模块内部
Next, take a look at `customers.module.ts`. If youre using the CLI and following the steps outlined in this page, you dont have to do anything here. The feature module is like a connector between the `AppRoutingModule` and the feature routing module. The `AppRoutingModule` imports the feature module, `CustomersModule`, and `CustomersModule` in turn imports the `CustomersRoutingModule`.
接下来看看 `customers.module.ts`。如果你使用的是 CLI并遵循本页面中给出的步骤那么在这里你不必做任何事。
特性模块就像是 `AppRoutingModule` 和该特性自己的路由模块之间的连接器。
`AppRoutingModule` 导入了特性模块 `CustomersModule`,而 `CustomersModule` 又导入了 `CustomersRoutingModule`
<code-example path="lazy-loading-ngmodules/src/app/customers/customers.module.ts" region="customers-module" title="src/app/customers/customers.module.ts" linenums="false">
</code-example>
The `customers.module.ts` file imports the `CustomersRoutingModule` and `CustomerListComponent` so the `CustomersModule` class can have access to them. `CustomersRoutingModule` is then listed in the `@NgModule` `imports` array giving `CustomersModule` access to its own routing module, and `CustomerListComponent` is in the `declarations` array, which means `CustomerListComponent` belongs to the `CustomersModule`.
`customers.module.ts` 文件导入了 `CustomersRoutingModule``CustomerListComponent`,所以 `CustomersModule` 类可以访问它们。
接着 `CustomersRoutingModule` 出现在了 `@NgModule``imports` 数组中,这让 `CustomersModule` 可以访问它的路由模块。而 `CustomerListComponent` 出现在了 `declarations` 数组中,这表示 `CustomerListComponent` 属于 `CustomersModule`
### Configure the feature modules routes
### 配置该特性模块的路由
The next step is in `customers-routing.module.ts`. First, import the component at the top of the file with the other JavaScript import statements. Then, add the route to `CustomerListComponent`.
接下来的步骤位于 `customers-routing.module.ts` 中。首先,在文件的顶部使用 JS 的 `import` 语句导入该组件。然后添加指向 `CustomerListComponent` 的路由。
<code-example path="lazy-loading-ngmodules/src/app/customers/customers-routing.module.ts" region="customers-routing-module" title="src/app/customers/customers-routing.module.ts" linenums="false">
</code-example>
Notice that the `path` is set to an empty string. This is because the path in `AppRoutingModule` is already set to `customers`, so this route in the `CustomersRoutingModule`, is already within the `customers` context. Every route in this routing module is a child route.
注意,`path` 被设置成了空字符串。这是因为 `AppRoutingModule` 中的路径已经设置为了 `customers`,所以 `CustomersRoutingModule` 中的这个路由定义已经位于 `customers` 这个上下文中了。也就是说这个路由模块中的每个路由其实都是子路由。
Repeat this last step of importing the `OrdersListComponent` and configuring the Routes array for the `orders-routing.module.ts`:
重复这个步骤以导入 `OrdersListComponent`,并为 `orders-routing.module.ts` 配置路由树组:
<code-example path="lazy-loading-ngmodules/src/app/orders/orders-routing.module.ts" region="orders-routing-module-detail" title="src/app/orders/orders-routing.module.ts (excerpt)" linenums="false">
</code-example>
Now, if you view the app in the browser, the three buttons take you to each module.
现在,如果你在浏览器中查看该应用,这三个按钮会把你带到每个模块去。
## Confirm its working
## 确认它工作正常
You can check to see that a module is indeed being lazy loaded with the Chrome developer tools. In Chrome, open the dev tools by pressing `Cmd+Option+i` on a Mac or `Ctrl+Alt+i` on a PC and go to the Network Tab.
你可以使用 Chrome 开发者工具来确认一下这些模块真的是惰性加载的。
在 Chrome 中,按 `Cmd+Option+i`Mac`Ctrl+Alt+i`PC并选中 `Network` 页标签。
<figure>
<img src="generated/images/guide/lazy-loading-ngmodules/network-tab.png" width="600" alt="lazy loaded modules diagram">
</figure>
Click on the Orders or Customers button. If you see a chunk appear, youve wired everything up properly and the feature module is being lazy loaded. A chunk should appear for Orders and for Customers but will only appear once for each.
点击 Orders 或 Customers 按钮。如果你看到某个 chunk 文件出现了就表示你已经惰性加载并接入了这个特性模块。Orders 和 Customers 都应该出现一次 chunk并且它们各自只应该出现一次。
<figure>
<img src="generated/images/guide/lazy-loading-ngmodules/chunk-arrow.png" width="600" alt="lazy loaded modules diagram">
</figure>
To see it again, or to test after working in the project, clear everything out by clicking the circle with a line through it in the upper left of the Network Tab:
要想再次查看它或测试本项目后面的行为,只要点击 Network 页左上放的“清除”图标即可。
<figure>
<img src="generated/images/guide/lazy-loading-ngmodules/clear.gif" width="200" alt="lazy loaded modules diagram">
</figure>
Then reload with `Cmd+r` or `Ctrl+r`, depending on your platform.
然后,使用 `Cmd+r`Mac`Ctrl+r`PC 重新加载页面。
## `forRoot()` and `forChild()`
## `forRoot()``forChild()`
You might have noticed that the CLI adds `RouterModule.forRoot(routes)` to the `app-routing.module.ts` `imports` array. This lets Angular know that this module,
`AppRoutingModule`, is a routing module and `forRoot()` specifies that this is the root
routing module. It configures all the
routes you pass to it, gives you access to the router directives, and registers the `RouterService`.
Use `forRoot()` in the `AppRoutingModule`&mdash;that is, one time in the app at the root level.
你可能已经注意到了CLI 会把 `RouterModule.forRoot(routes)` 添加到 `app-routing.module.ts``imports` 数组中。
这会让 Angular 知道 `AppRoutingModule` 是一个路由模块,而 `forRoot()` 表示这是一个根路由模块。
它会配置你传入的所有路由、让你能访问路由器指令并注册 `RouterService`
`AppRoutingModule` 中使用 `forRoot()`,在本应用中这只会在顶层模块中写一次。
The CLI also adds `RouterModule.forChild(routes)` to feature routing modules. This way, Angular
knows that the route list is only responsible for providing additional routes and is intended for feature modules. You can use `forChild()` in multiple modules.
CLI 还会把 `RouterModule.forChild(routes)` 添加到各个特性模块中。这种方式下 Angular 就会知道这个路由列表只负责提供额外的路由并且其设计意图是作为特性模块使用。你可以在多个模块中使用 `forChild()`
`forRoot()` contains injector configuration which is global; such as configuring the Router. `forChild()` has no injector configuration, only directives such as `RouterOutlet` and `RouterLink`.
`forRoot()` 包含的注入器配置是全局性的,比如对路由器的配置。`forChild()` 中没有注入器配置,只有像 `RouterOutlet``RouterLink` 这样的指令。
<hr>
## More on NgModules and routing
## 更多关于 NgModule 和路由的知识
You may also be interested in the following:
你可能对下列内容感兴趣:
* [Routing and Navigation](guide/router).
[路由与导航](guide/router)。
* [Providers](guide/providers).
[服务提供商](guide/providers)。
* [Types of Feature Modules](guide/module-types).
[特性模块的分类](guide/module-types)。

View File

@ -10,25 +10,38 @@ A basic understanding of the following concepts:
* [Bootstrapping](guide/bootstrapping).
[引导](guide/bootstrapping)。
* [JavaScript Modules vs. NgModules](guide/ngmodule-vs-jsmodule).
[JavaScript 模块与 NgModules](guide/ngmodule-vs-jsmodule).
[JavaScript 模块与 NgModules](guide/ngmodule-vs-jsmodule)
<hr />
## Purpose of `@NgModule`
## `@NgModule` 的设计意图
At a high level, NgModules are a way to organize Angular apps
and they accomplish this through the metadata in the `@NgModule`
decorator. The metadata falls
into three categories:
宏观来讲NgModule 是组织 Angular 应用的一种方式,它们通过 `@NgModule` 装饰器中的元数据来实现这一点。
这些元数据可以分成三类:
* **Static:** Compiler configuration which tells the compiler about directive selectors and where in templates the directives should be applied through selector matching. This is configured via the `declarations` array.
**静态的:**编译器配置,用于告诉编译器指令的选择器并通过选择器匹配的方式决定要把该指令应用到模板中的什么位置。它是通过 `declarations` 数组来配置的。
* **Runtime:** Injector configuration via the `providers` array.
**运行时:**通过 `providers` 数组提供给注入器的配置。
* **Composability/Grouping:** Bringing NgModules together and making them available via the `imports` and `exports` arrays.
**组合/分组:**通过 `imports``exports` 数组来把多个 NgModule 放在一起,并彼此可用。
```typescript
@NgModule({
@ -48,6 +61,8 @@ into three categories:
## `@NgModule` metadata
## `@NgModule` 元数据
The following table summarizes the `@NgModule` metadata properties.
下面是 `@NgModule` 元数据中属性的汇总表:
@ -87,28 +102,39 @@ The following table summarizes the `@NgModule` metadata properties.
A list of [declarable](guide/ngmodule-faq#q-declarable) classes,
(*components*, *directives*, and *pipes*) that _belong to this module_.
*属于该模块*的[可声明对象](guide/ngmodule-faq#q-declarable)*组件*、*指令*和*管道*)的列表。
<ol>
<li>
When compiling a template, you need to determine a set of selectors which should be used for triggering their corresponding directives.
当编译模板时,你需要确定一组选择器,它们将用于触发相应的指令。
</li>
<li>
The template is compiled within the context of an NgModule&mdash;the NgModule within which the template's component is declared&mdash;which determines the set of selectors using the following rules:
该模板在 NgModule 环境中编译 —— 模板的组件是在该 NgModule 内部声明的,它会使用如下规则来确定这组选择器:
<ul>
<li>
All selectors of directives listed in `declarations`.
列在 `declarations` 中的所有指令选择器。
</li>
<li>
All selectors of directives exported from imported NgModules.
从所导入的 NgModule 中导出的那些指令的选择器。
</li>
</ul>
@ -123,6 +149,8 @@ The following table summarizes the `@NgModule` metadata properties.
Don't re-declare a class imported from another module.
不要重复声明从其它模块中导入的类。
</td>
</tr>
@ -144,20 +172,34 @@ The following table summarizes the `@NgModule` metadata properties.
Angular registers these providers with the NgModule's injector.
If it is the NgModule used for bootstrapping then it is the root injector.
Angular 会使用该模块的注入器注册这些提供商。
如果该模块是启动模块,那就会使用根注入器。
These services become available for injection into any component, directive, pipe or service which is a child of this injector.
当需要注入到任何组件、指令、管道或服务时,这些服务对于本注入器的子注入器都是可用的。
A lazy-loaded module has its own injector which
is typically a child of the application root injector.
惰性加载模块有自己的注入器,它通常是应用的根注入器的子注入器。
Lazy-loaded services are scoped to the lazy module's injector.
If a lazy-loaded module also provides the `UserService`,
any component created within that module's context (such as by router navigation)
gets the local instance of the service, not the instance in the root application injector.
惰性加载的服务是局限于这个惰性加载模块的注入器中的。
如果惰性加载模块也提供了 `UserService`,那么在这个模块的上下文中创建的任何组件(比如在路由器导航时),都会获得这个服务的本模块内实例,而不是来自应用的根注入器的实例。
Components in external modules continue to receive the instance provided by their injectors.
其它外部模块中的组件也会使用它们自己的注入器提供的服务实例。
For more information on injector hierarchy and scoping, see [Providers](guide/providers).
要深入了解关于多级注入器及其作用域,参见[服务提供商](guide/providers)。
</td>
</tr>
@ -175,19 +217,29 @@ The following table summarizes the `@NgModule` metadata properties.
A list of modules which should be folded into this module. Folded means it is
as if all the imported NgModule's exported properties were declared here.
要折叠Folded进本模块中的其它模块。折叠的意思是从被导入的模块中导出的那些软件资产同样会被声明在这里。
Specifically, it is as if the list of modules whose exported components, directives, or pipes
are referenced by the component templates were declared in this module.
特别是,这里列出的模块,其导出的组件、指令或管道,当在组件模板中被引用时,和本模块自己声明的那些是等价的。
A component template can [reference](guide/ngmodule-faq#q-template-reference) another component, directive, or pipe
when the reference is declared in this module or if the imported module has exported it.
For example, a component can use the `NgIf` and `NgFor` directives only if the
module has imported the Angular `CommonModule` (perhaps indirectly by importing `BrowserModule`).
组件模板可以[引用](guide/ngmodule-faq#q-template-reference)其它组件、指令或管道,不管它们是在本模块中声明的,还是从导入的模块中导出的。
比如,只有当该模块导入了 Angular 的 `CommonModule也可能从 `BrowserModule` 中间接导入)时,组件才能使用 `NgIf``NgFor` 指令。
You can import many standard directives from the `CommonModule`
but some familiar directives belong to other modules.
For example, you can use `[(ngModel)]` only
after importing the Angular `FormsModule`.
你可以从 `CommonModule` 中导入很多标准指令,不过也有些常用的指令属于其它模块。
比如,你只有导入了 Angular 的 `FormsModule` 时才能使用 `[(ngModel)]`
</td>
</tr>
@ -211,14 +263,24 @@ The following table summarizes the `@NgModule` metadata properties.
A component in another module can [use](guide/ngmodule-faq#q-template-reference) _this_
module's `UserComponent` if it imports this module and this module exports `UserComponent`.
导出的可声明对象就是本模块的*公共 API*。
只有当其它模块导入了本模块,并且本模块导出了 `UserComponent` 时,其它模块中的组件才能[使用](guide/ngmodule-faq#q-template-reference)*本*模块中的 `UserComponent`
Declarations are private by default.
If this module does _not_ export `UserComponent`, then only the components within _this_
module can use `UserComponent`.
默认情况下这些可声明对象都是私有的。
如果本模块*没有*导出 `UserComponent`,那么就只有*本*模块中的组件才能使用 `UserComponent`
Importing a module does _not_ automatically re-export the imported module's imports.
Module 'B' can't use `ngIf` just because it imported module 'A' which imported `CommonModule`.
Module 'B' must import `CommonModule` itself.
导入某个模块*并不会*自动重新导出被导入模块的那些导入。
模块 B 不会因为它导入了模块 A而模块 A 导入了 `CommonModule` 而能够使用 `ngIf`
模块 B 必须自己导入 `CommonModule`
A module can list another module among its `exports`, in which case
all of that module's public components, directives, and pipes are exported.
@ -228,6 +290,9 @@ The following table summarizes the `@NgModule` metadata properties.
If Module 'A' re-exports `CommonModule` and Module 'B' imports Module 'A',
Module 'B' components can use `ngIf` even though 'B' itself didn't import `CommonModule`.
[重新导出](guide/ngmodule-faq#q-reexport)可以让模块被显式传递。
如果模块 A 重新导出了 `CommonModule`,而模块 B 导入了模块 A那么模块 B 就可以使用 `ngIf` 了 —— 即使它自己没有导入 `CommonModule`
</td>
</tr>
@ -243,6 +308,8 @@ The following table summarizes the `@NgModule` metadata properties.
<td>
A list of components that are automatically bootstrapped.
要自动启动的组件列表。
Usually there's only one component in this list, the _root component_ of the application.
@ -255,6 +322,8 @@ The following table summarizes the `@NgModule` metadata properties.
A bootstrap component is automatically added to `entryComponents`.
启动组件会自动添加到 `entryComponents` 中。
</td>
</tr>
@ -270,22 +339,38 @@ The following table summarizes the `@NgModule` metadata properties.
<td>
A list of components that can be dynamically loaded into the view.
那些可以动态加载进视图的组件列表。
By default, an Angular app always has at least one entry component, the root component, `AppComponent`. Its purpose is to serve as a point of entry into the app, that is, you bootstrap it to launch the app.
默认情况下Angular 应用至少有一个入口组件,也就是根组件 `AppComponent`
它用作进入该应用的入口点,也就是说你通过引导它来启动本应用。
Routed components are also _entry components_ because they need to be loaded dynamically.
The router creates them and drops them into the DOM near a `<router-outlet>`.
路由组件也是*入口组件*,因为你需要动态加载它们。
路由器创建它们,并把它们扔到 DOM 中的 `<router-outlet>` 附近。
While the bootstrapped and routed components are _entry components_,
you don't have to add them to a module's `entryComponents` list,
as they are added implicitly.
虽然引导组件和路由组件都是*入口组件*,不过你不用自己把它们加到模块的 `entryComponents` 列表中,因为它们会被隐式添加进去。
Angular automatically adds components in the module's `bootstrap` and route definitions into the `entryComponents` list.
Angular 会自动把模块的 `bootstrap` 中的组件和路由定义中的组件添加到 `entryComponents` 列表。
That leaves only components bootstrapped using one of the imperative techniques, such as [`ViewComponentRef.createComponent()`](https://angular.io/api/core/ViewContainerRef#createComponent) as undiscoverable.
而那些使用不易察觉的[`ViewComponentRef.createComponent()`](https://angular.io/api/core/ViewContainerRef#createComponent)的方式进行命令式引导的组件仍然需要添加。
Dynamic component loading is not common in most apps beyond the router. If you need to dynamically load components, you must add these components to the `entryComponents` list yourself.
动态组件加载在除路由器之外的大多数应用中都不太常见。如果你需要动态加载组件,就必须自己把那些组件添加到 `entryComponents` 列表中。
For more information, see [Entry Components](guide/entry-components).
要了解更多,参见[入口组件](guide/entry-components)一章。
@ -300,12 +385,24 @@ The following table summarizes the `@NgModule` metadata properties.
## More on NgModules
## 关于 NgModule 的更多知识
You may also be interested in the following:
你可能还对下列内容感兴趣:
* [Feature Modules](guide/feature-modules).
[特性模块](guide/feature-modules)。
* [Entry Components](guide/entry-components).
[入口组件](guide/entry-components)。
* [Providers](guide/providers).
[提供商](guide/providers)。
* [Types of Feature Modules](guide/module-types).
[特性模块的分类](guide/module-types)。

View File

@ -684,9 +684,13 @@ When an eagerly loaded module provides a service, for example a `UserService`, t
imports another module that provides the same `UserService`, Angular registers one of
them in the root app injector (see [What if I import the same module twice?](guide/ngmodule-faq#q-reimport)).
当立即加载的模块提供了服务时,比如 `UserService`,该服务是在全应用级可用的。如果根模块提供了 `UserService`,并导入了另一个也提供了同一个 `UserService` 的模块Angular 就会把它们中的一个注册进应用的根注入器中(参见[如果两次导入了同一个模块会怎样?](guide/ngmodule-faq#q-reimport))。
Then, when some component injects `UserService`, Angular finds it in the app root injector,
and delivers the app-wide singleton service. No problem.
然后,当某些组件注入 `UserService`Angular 就会发现它已经在应用的根注入器中了,并交付这个全应用级的单例服务。这样不会出现问题。
### The lazy loaded scenario
### 惰性加载场景
@ -1021,13 +1025,23 @@ in one folder, that the contents of that folder would be a feature module that y
your `SearchModule`. It would contain all of the components, routing, and templates that
would make up the search functionality.
特性模块是你围绕特定的应用业务领域创建的模块,比如用户工作流、小工具集等。它们包含指定的特性,并为你的应用提供支持,比如路由、服务、窗口部件等。
要对你的应用中可能会有哪些特性模块有个概念,考虑如果你要把与特定功能(比如搜索)有关的文件放进一个目录下,该目录的内容就可能是一个名叫 `SearchModule` 的特性模块。
它将会包含构成搜索功能的全部组件、路由和模板。
For more information, see [Feature Modules](guide/feature-modules) and
[Module Types](guide/module-types)
要了解更多,参见[特性模块](guide/feature-modules)和[模块的分类](guide/module-types)。
## What's the difference between NgModules and JavaScript Modules?
## 在 NgModule 和 JavaScript 模块之间有什么不同?
In an Angular app, NgModules and JavaScript modules work together.
在 Angular 应用中NgModule 会和 JavaScript 的模块一起工作。
In modern JavaScript, every file is a module
(see the [Modules](http://exploringjs.com/es6/ch_modules.html) page of the Exploring ES6 website).
Within each file you write an `export` statement to make parts of the module public.

View File

@ -1,5 +1,7 @@
# Sharing Modules
# 共享特性模块
#### Prerequisites
#### 前提条件
@ -10,16 +12,24 @@ A basic understanding of the following:
* [Feature Modules](guide/feature-modules).
[特性模块](guide/feature-modules).
* [JavaScript Modules vs. NgModules](guide/ngmodule-vs-jsmodule).
[JavaScript 模块与 NgModules](guide/ngmodule-vs-jsmodule).
[JavaScript 模块与 NgModules](guide/ngmodule-vs-jsmodule)
* [Frequently Used Modules](guide/frequent-ngmodules).
[常用模块](guide/frequent-ngmodules).
* [Routing and Navigation](guide/router).
[路由与导航](guide/router).
* [Lazy loading modules](guide/lazy-loading-ngmodules).
[惰性加载模块](guide/lazy-loading-ngmodules).
<!--* Components (#TBD) We dont have a page just on the concept of components, but I think one would be helpful for beginners.-->
<hr>
@ -28,8 +38,12 @@ Creating shared modules allows you to organize and streamline your code. You can
used directives, pipes, and components into one module and then import just that module wherever
you need it in other parts of your app.
创建共享模块能让你更好地组织和梳理代码。你可以把常用的指令、管道和组件放进一个模块中,然后在应用中其它需要这些的地方导入该模块。
Consider the following module from an imaginary app:
想象某个应用有下列模块:
```typescript
import { CommonModule } from '@angular/common';
@ -55,16 +69,22 @@ Note the following:
* It imports the `CommonModule` because the module's component needs common directives.
它导入了 `CommonModule`,因为该模块需要一些常用指令。
* It declares and exports the utility pipe, directive, and component classes.
它声明并导出了一些工具性的管道、指令和组件类。
* It re-exports the `CommonModule` and `FormsModule`.
它重新导出了 `CommonModule``FormsModule`
它重新导出了 `CommonModule``FormsModule`
By re-exporting `CommonModule` and `FormsModule`, any other module that imports this
`SharedModule`, gets access to directives like `NgIf` and `NgFor` from `CommonModule`
and can bind to component properties with `[(ngModel)]`, a directive in the `FormsModule`.
通过重新导出 `CommonModule``FormsModule`,任何导入了这个 `SharedModule` 的其它模块,就都可以访问来自 `CommonModule``NgIf``NgFor` 等指令了,也可以绑定到来自 `FormsModule` 中的 `[(ngModel)]` 的属性了。
Even though the components declared by `SharedModule` might not bind
with `[(ngModel)]` and there may be no need for `SharedModule`
to import `FormsModule`, `SharedModule` can still export
@ -72,23 +92,43 @@ to import `FormsModule`, `SharedModule` can still export
way, you can give other modules access to `FormsModule` without
having to import it directly into the `@NgModule` decorator.
即使 `SharedModule` 中声明的组件没有绑定过 `[(ngModel)]`,而且`SharedModule` 也不需要导入 `FormsModule``SharedModule` 仍然可以导出 `FormsModule`,而不必把它列在 `imports` 中。
这种方式下,你可以让其它模块也能访问 `FormsModule`,而不用直接在自己的 `@NgModule` 装饰器中导入它。
### Using components vs services from other modules.
### 使用来自其它模块的组件和服务
There is an important distinction between using another module's component and
using a service from another module. Import modules when you want to use
directives, pipes, and components. Importing a module with services means that you will have a new instance of that service, which typically is not what you need (typically one wants to reuse an existing service). Use module imports to control service instantiation.
在使用来自其它模块的组件和来自其它模块的服务时,有一个很重要的区别。
当你要使用指令、管道和组件时,导入那些模块就可以了。而导入带有服务的模块意味着你会拥有那个服务的一个新实例,这通常不会是你想要的结果(你通常会想取到现存的服务)。使用模块导入来控制服务的实例化。
The most common way to get a hold of shared services is through Angular
[dependency injection](guide/dependency-injection), rather than through the module system (importing a module will result in a new service instance, which is not a typical usage).
获取共享服务的最常见方式是通过 Angular 的[依赖注入系统](guide/dependency-injection),而不是模块系统(导入模块将导致创建新的服务实例,那不是典型的用法)。
To read about sharing services, see [Providers](guide/providers).
要进一步了解共享服务,参见[服务提供商](guide/providers)。
<hr />
## More on NgModules
## 关于 NgModule 的更多知识
You may also be interested in the following:
你可能还对下列内容感兴趣:
* [Providers](guide/providers).
[服务提供商](guide/providers).
* [Types of Feature Modules](guide/module-types).
[特性模块的分类](guide/module-types).

View File

@ -6,15 +6,23 @@
* A basic understanding of [Bootstrapping](guide/bootstrapping).
对[引导](guide/bootstrapping)有基本的了解。
* Familiarity with [Providers](guide/providers).
熟悉[服务提供商](guide/providers)。
For a sample app using the app-wide singleton service that this page describes, see the
<live-example name="ngmodules"></live-example> showcasing all the documented features of NgModules.
本页中描述的这种全应用级单例服务的例子位于<live-example name="ngmodules"></live-example>,它示范了 NgModule 的所有已文档化的特性。
<hr />
## Providing a singleton service
## 提供单例服务
An injector created from a module definition will have services which are singletons with respect to
that injector. To control the lifetime of services, one controls the creation and destruction of
injectors. For example, a route will have an associated module. When the route is activated, an
@ -23,9 +31,16 @@ the route, the injector is destroyed. This means that services declared in a rou
a lifetime equal to that of the route. Similarly, services provided in an application module will
have the same lifetime of the application, hence singleton.
那些在定义模块时创建的注入器将会拥有一些服务,这些服务对于该注入器来说都是单例的。要控制这些服务的生命周期,其实就是控制注入器的创建和销毁。
比如,路由定义中就可以有一个关联模块。当激活该路由时,就会给那个模块创建一个新注入器,并将其作为当前注入器的子注入器。当离开该路由时,这个新注入器也就被销毁了。
这也意味着在这个模块中声明的那些服务也随之销毁了,它们的生命周期与该路由完全相同。
类似的,在应用模块中提供的那些服务的生命周期也等同于该应用,因此是单例的。
The following example module is called, as a convention, `CoreModule`. This use of `@NgModule` creates organizational infrastructure and gives you
a way of providing services from a designated NgModule.
下面的范例模块习惯上叫做 `CoreModule`。`@NgModule` 用来创建结构良好的基础设施,让你能够在一个指定的模块中提供服务。
<code-example path="ngmodules/src/app/core/core.module.ts" region="user-service" title="src/app/core/core.module.ts" linenums="false">
</code-example>
@ -34,11 +49,16 @@ Here, `CoreModule` provides the `UserService`, and because `AppModule`
imports `CoreModule`, any services that `CoreModule` provides are available
throughout the app, because it is a root of the injector tree. It will also be a singleton because the injector lifetime of the `AppModule` is for the duration of the application.
这里的 `CoreModule` 提供了 `UserService`,并且由于 `AppModule` 导入了 `CoreModule`,所以 `CoreModule` 中提供的任何服务也能在整个应用中使用,因为它是注入器树的根节点。
它还是单例的,因为在该应用运行期间,该注入器的生命周期等同于 `AppModule` 的。
Angular registers the `UserService` provider with the app root
injector, making a singleton instance of the `UserService`
available to any component that needs it,
whether that component is eagerly or lazily loaded.
Angular 使用应用的根注入器注册了 `UserService` 提供商,可以让任何需要它的组件(无论它是立即加载的还是惰性加载的)都能使用 `UserService` 的单例。
The root `AppModule` could register the `UserService` directly,
but as the app grows, it could have other services and
components like spinners, modals, and so on. To
@ -46,11 +66,19 @@ keep your app organized, consider using a module such as `CoreModule`.
This technique simplifies the root `AppModule` in its
capacity as orchestrator of the application as a whole.
根模块 `AppModule` 当然也可以直接注册 `UserService`,不过随着应用的成长,可能还会出现其它的服务和组件,比如 列表框、模态框等等。
要想保持你应用的良好结构,就要考虑使用诸如 `CoreModule` 这样的模块。
这种方式简化了根模块 `AppModule`,让它只需要扮演整个应用的总指挥,而不必事必躬亲。
Now you can inject such services into components as needed. In terms of
Angular NgModules, you only need to define the services in one `@NgModule`.
See [JS Modules vs. NgModules](guide/ngmodule-vs-jsmodule) for
more information on how to differentiate between the two.
现在,你可以把这些服务注入到需要它们的各个组件中了。
从 Angular 模块的角度来说,你只需要把这些服务定义在一个 `@NgModule` 中。
要想深入了解两者的区别,参见 [JS 模块 vs. NgModule](guide/ngmodule-vs-jsmodule)。
As a general rule, import modules with providers _exactly once_,
preferably in the application's _root module_.
That's also usually the best place to configure, wrap, and override them.
@ -61,35 +89,61 @@ That's also usually the best place to configure, wrap, and override them.
For more detailed information on services, see the [Services](tutorial/toh-pt4) chapter of the
[Tour of Heroes tutorial](tutorial).
要想深入了解关于服务的信息,参见[《英雄指南》教程](tutorial)中的[服务](tutorial/toh-pt4)一章。
## `forRoot()`
If a module provides both providers and declarations (components, directives, pipes) then loading it in a child injector such as a route, would duplicate the provider instances. The duplication of providers would cause issues as they would shadow the root instances, which are probably meant to be singletons. For this reason Angular provides a way to separate providers out of the module so that same module can be imported into the root module with `providers` and child modules without `providers`.
如果某个模块同时提供了服务提供商和可声明对象(组件、指令、管道),那么当在某个子注入器中加载它的时候(比如路由),就会生成多个该服务提供商的实例。
而存在多个实例会导致一些问题,因为这些实例会屏蔽掉根注入器中该服务提供商的实例,而它的本意可能是作为单例对象使用的。
因此Angular 提供了一种方式来把服务提供商从该模块中分离出来,以便该模块既可以带着 `providers` 被根模块导入,也可以不带 `providers` 被子模块导入。
1. Create a static method `forRoot()` (by convention) on the module.
在该模块上创建一个静态方法 `forRoot()`(习惯名称)。
2. Place the providers into the `forRoot` method as follows.
把那些服务提供商放进 `forRoot` 方法中,参见下面的例子。
<!-- MH: show a simple example how to do that without going to deep into it. -->
To make this more concrete, consider the `RouterModule` as an example. `RouterModule` needs to provide the `Router` service, as well as the `RouterOutlet` directive. `RouterModule` has to be imported by the root application module so that the application has a `Router` and the application has at least one `RouterOutlet`. It also must be imported by the individual route components so that they can place `RouterOutlet` directives into their template for sub-routes.
我们以 `RouterModule` 为例来具体说说。`RouterModule` 要提供 `Router` 服务,还要提供 `RouterOutlet` 指令。
`RouterModule` 要由根应用模块导入,以便该应用拥有一个路由器,而且它还需要至少一个 `RouterOutlet`
`RouterModule` 还必须由各个独立的路由组件导入,让它们能在自己的模板中使用 `RouterOutlet` 指令来支持其子路由。
If the `RouterModule` didnt have `forRoot()` then each route component would instantiate a new `Router` instance, which would break the application as there can only be one `Router`. For this reason, the `RouterModule` has the `RouterOutlet` declaration so that it is available everywhere, but the `Router` provider is only in the `forRoot()`. The result is that the root application module imports `RouterModule.forRoot(...)` and gets a `Router`, whereas all route components import `RouterModule` which does not include the `Router`.
如果 `RouterModule` 没有 `forRoot()`,那么每个路由组件都会创建一个新的 `Router` 实例。这将会破坏整个应用,因为应用中只能有一个 `Router`
`RouterModule` 拥有 `RouterOutlet` 指令,它应该随处可用,但是 `Router` 只能有一个,它应该在 `forRoot()` 中提供。
最终的结果就是,应用的根模块导入了 `RouterModule.forRoot(...)` 以获取 `Router`,而所有路由组件都导入了 `RouterModule`,它不包括这个 `Router` 服务。
If you have a module which provides both providers and declarations, use this pattern to separate them out.
如果你有一个同时提供服务提供商和可声明对象的模块,请使用下面的模式把它们分离开。
A module that adds providers to the application can offer a
facility for configuring those providers as well through the
`forRoot()` method.
那些需要把服务提供商加到应用中的模块可以通过某种类似 `forRoot()` 方法的方式配置那些服务提供商。
`forRoot()` takes a service configuration object and returns a
[ModuleWithProviders](api/core/ModuleWithProviders), which is
a simple object with the following properties:
`forRoot()` 接收一个服务配置对象,然后返回一个 [ModuleWithProviders](api/core/ModuleWithProviders) ,它是一个带有下列属性的简单对象:
* `ngModule`: in this example, the `CoreModule` class.
`ngModule` 在这个例子中就是 `CoreModule`
* `providers`: the configured providers.
`providers` - 配置好的服务提供商
`providers` - 配置好的服务提供商
In the <live-example name="ngmodules">live example</live-example>
the root `AppModule` imports the `CoreModule` and adds the
@ -100,20 +154,33 @@ This sequence ensures that whatever you add explicitly to
the `AppModule` providers takes precedence over the providers
of imported modules.
在这个<live-example name="ngmodules"></live-example>中,根 `AppModule` 导入了 `CoreModule`,并把它的 `providers` 添加到了 `AppModule` 的服务提供商中。
特别是Angular 会在 `@NgModule.providers` 前面添加这些导入的服务提供商。
这种顺序保证了 `AppModule` 中的服务提供商总是会优先于那些从其它模块中导入的服务提供商。
Import `CoreModule` and use its `forRoot()` method one time, in `AppModule`, because it registers services and you only want to register those services one time in your app. If you were to register them more than once, you could end up with multiple instances of the service and a runtime error.
应该只在 `AppModule` 中导入 `CoreModule` 并只使用一次 `forRoot()` 方法,因为该方法中会注册服务,而你希望那些服务在该应用中只注册一次。
如果你多次注册它们,就可能会得到该服务的多个实例,并导致运行时错误。
You can also add a `forRoot()` method in the `CoreModule` that configures
the core `UserService`.
你还可以在 `CoreModule` 中添加一个用于配置 `UserService``forRoot()` 方法。
In the following example, the optional, injected `UserServiceConfig`
extends the core `UserService`. If a `UserServiceConfig` exists, the `UserService` sets the user name from that config.
在下面的例子中,可选的注入 `UserServiceConfig` 扩展了 `Core` 模块中的 `UserService`。如果 `UserServiceConfig` 存在,就从这个配置中设置用户名。
<code-example path="ngmodules/src/app/core/user.service.ts" region="ctor" title="src/app/core/user.service.ts (constructor)" linenums="false">
</code-example>
Here's `forRoot()` that takes a `UserServiceConfig` object:
下面是一个接受 `UserServiceConfig` 参数的 `forRoot()` 方法:
<code-example path="ngmodules/src/app/core/core.module.ts" region="for-root" title="src/app/core/core.module.ts (forRoot)" linenums="false">
</code-example>
@ -132,16 +199,25 @@ The app displays "Miss Marple" as the user instead of the default "Sherlock Holm
Remember to _import_ `CoreModule` as a Javascript import at the top of the file; don't add it to more than one `@NgModule` `imports` list.
记住,在文件顶部使用 JavaScript 的 `import` 语句*导入* `CoreModule`,但不要在多于一个 `@NgModule``imports` 列表中添加它。
<!-- KW--Does this mean that if we need it elsewhere we only import it at the top? I thought the services would all be available since we were importing it into `AppModule` in `providers`. -->
## Prevent reimport of the `CoreModule`
## 防止重复导入 `CoreModule`
Only the root `AppModule` should import the `CoreModule`. If a
lazy-loaded module imports it too, the app can generate
[multiple instances](guide/ngmodule-faq#q-why-bad) of a service.
只有根模块 `AppModule` 才能导入 `CoreModule`。如果一个惰性加载模块也导入了它,
该应用就会为服务生成[多个实例](guide/ngmodule-faq#q-why-bad)。
To guard against a lazy-loaded module re-importing `CoreModule`, add the following `CoreModule` constructor.
要想防止惰性加载模块重复导入 `CoreModule`,可以添加如下的 `CoreModule` 构造函数。
<code-example path="ngmodules/src/app/core/core.module.ts" region="ctor" title="src/app/core/core.module.ts" linenums="false">
</code-example>
@ -152,9 +228,15 @@ The injection would be circular if Angular looked for
decorator means "look for `CoreModule` in an ancestor
injector, above me in the injector hierarchy."
这个构造函数要求 Angular 把 `CoreModule` 注入到它自己。
如果 Angular 在*当前*注入器中查找 `CoreModule`,这个注入过程就会陷入死循环。
`@SkipSelf` 装饰器表示 “在注入器树中那些高于我的祖先注入器中查找 `CoreModule`”。
If the constructor executes as intended in the `AppModule`,
there would be no ancestor injector that could provide an instance of `CoreModule` and the injector should give up.
如果构造函数在 `AppModule` 中执行,那就没有祖先注入器能提供 `CoreModule` 的实例,于是注入器就会放弃查找。
By default, the injector throws an error when it can't
find a requested provider.
The `@Optional` decorator means not finding the service is OK.
@ -167,19 +249,23 @@ and the constructor concludes uneventfully.
It's a different story if you improperly import `CoreModule` into a lazy-loaded module such as `CustomersModule`.
但如果你把 `CoreModule` 导入到像 `CustomerModule` 这样的惰性加载模块中,事情就不一样了。
Angular creates a lazy-loaded module with its own injector,
a _child_ of the root injector.
`@SkipSelf` causes Angular to look for a `CoreModule` in the parent injector, which this time is the root injector.
Of course it finds the instance imported by the root `AppModule`.
Now `parentModule` exists and the constructor throws the error.
Angular 创建一个惰性加载模块,它具有自己的注入器,它是根注入器的*子注入器*。
`@SkipSelf` 让 Angular 在其父注入器中查找 `CoreModule`,这次,它的父注入器却是根注入器了(而上次父注入器是空)。
Angular 创建一个惰性加载模块,它具有自己的注入器,它是根注入器的*子注入器*。
`@SkipSelf` 让 Angular 在其父注入器中查找 `CoreModule`,这次,它的父注入器却是根注入器了(而上次父注入器是空)。
当然,这次它找到了由根模块 `AppModule` 导入的实例。
该构造函数检测到存在 `parentModule`,于是抛出一个错误。
Here are the two files in their entirety for reference:
以下这两个文件仅供参考:
<code-tabs linenums="false">
<code-pane
@ -198,10 +284,20 @@ Here are the two files in their entirety for reference:
## More on NgModules
## 关于 NgModule 的更多知识
You may also be interested in:
你还可能对下列内容感兴趣:
* [Sharing Modules](guide/sharing-ngmodules), which elaborates on the concepts covered on this page.
[共享模块](guide/sharing-ngmodules)解释了本页中涉及的这些概念。
* [Lazy Loading Modules](guide/lazy-loading-ngmodules).
[惰性加载模块](guide/lazy-loading-ngmodules)。
* [NgModule FAQ](guide/ngmodule-faq).
[NgModule 常见问题](guide/ngmodule-faq)。