关于 Angular 2 Bootstrap 多个应用的说明
这是一篇关于如何在单个 HTML 页面中引导(bootstrap)多个 Angular 应用的注意事项。
引言
这是一篇关于如何在单个 HTML 页面中引导(bootstrap)多个 Angular 应用的注意事项。
背景
Angular 的 网站 提供了一些关于如何引导 Angular 应用的示例,但这些示例只引导了单个 HTML 页面中的一个应用。虽然不是绝对必要,但你可能会发现有必要在单个 HTML 页面中引导多个应用。
- 结构上的原因 - 在 ASP.NET MVC 应用中,通常会将所有页面共用的组件放在布局页面中。你可能希望将这些组件与页面特定的组件分开引导;
- 性能上的原因 - 默认情况下,Angular 的 变更检测 的范围是应用中的整个组件树。如果你知道某些组件中的操作(DOM 事件和回调)对其他组件的状态没有影响,你可能希望将它们单独引导,这样变更检测就会在一个更小的范围内运行。
附加的示例是一个 Visual Studio 2015 Update 3 中的 ASP.NET MVC 应用。如果你想运行它,我强烈建议你看看我 之前的帖子,因为编译和运行一个 Typescript 的 Angular 2 应用并非易事。
该示例在一个 HTML 页面中引导了两个 Angular 应用。
- 每个 Angular 应用的组件位于“app1”和“app2”文件夹中。
- “shared-sub-component”和“math-service”在这两个 Angular 应用之间共享,以演示不同的引导可以共享公共代码。
- 这两个 Angular 应用都将在“Index.cshtml”页面中引导。
以下部分为了完整性而稍显冗长。如果你只对引导多个 Angular 应用感兴趣,可以跳过大部分内容。
共享服务和共享组件
为了演示不同的引导可以共享公共代码,我创建了一个简单的服务和一个简单的组件。
import { Injectable } from '@angular/core';
@Injectable()
export class MathService {
public AddOne(n) {
return n + 1;
}
}
“math-service.ts”执行一个简单的数学运算。它将被用于所有组件,以演示 Angular 服务的代码可以被单独引导的组件共享。
import { Component, DoCheck } from '@angular/core';
import { MathService } from '../../services/math-service';
@Component({
selector: 'shared-sub-component',
template: '<div class="component">' +
'<h3>Shared Component</h3>' +
'<div><button (click)="Add()">Click to add Count</button>' +
' - {{Count}}</div>' +
'</div>'
})
export class SharedSubComponent implements DoCheck {
public Count = 0;
constructor(private math: MathService) { }
public ngDoCheck() {
console.log('Shared Change detection!')
}
public Add() {
this.Count = this.math.AddOne(this.Count);
}
}
“shared-sub-component.ts”实现了一个 Angular 组件。它将被用作其他组件的子组件,以演示 Angular 组件可以被单独引导的组件共享。
Angular 应用 1
在本篇注意事项中,我将在同一网页上引导两个 Angular 应用。第一个应用实现在“app1”文件夹中。
“component-1.ts”实现了该应用唯一的组件。
import { Component, DoCheck } from '@angular/core';
import { MathService } from '../../services/math-service';
@Component({
selector: 'component-1',
template: '<div class="component">' +
'<h3>Component-1</h3>' +
'<div><button (click)="Add()">Click to add Count</button>' +
' - {{Count}}</div>' +
'<shared-sub-component></shared-sub-component>' +
'</div>'
})
export class Component1 implements DoCheck {
public Count = 0;
constructor(private math: MathService) { }
public ngDoCheck() {
console.log('component-1 Change detection!')
}
public Add() {
this.Count = this.math.AddOne(this.Count);
}
}
“component-1
”组件使用“MathService
”进行简单的数学运算。它还将“shared-sub-component
”用作子组件。“component-1
”被打包在“app.module.ts”文件中。
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { Component1 } from './components/component-1';
import { SharedSubComponent } from '../common/components/shared-sub-component';
import { MathService } from '../services/math-service';
@NgModule({
imports: [BrowserModule],
providers: [
MathService
],
declarations: [
Component1,
SharedSubComponent
],
bootstrap: [
Component1
]
})
export class AppModule { }
“AppModule
”将通过“app1.ts”文件引导。
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app.module';
try { enableProdMode(); } catch(e) { }
const platform = platformBrowserDynamic();
platform.bootstrapModule(AppModule);
Angular 应用 2
第二个 Angular 应用实现在“app2”文件夹中。它与第一个应用 virtually 相同。你不需要花时间去查看它。列出它只是为了完整性。
“component-2.ts”组件。
import { Component, DoCheck } from '@angular/core';
import { MathService } from '../../services/math-service';
@Component({
selector: 'component-2',
providers: [
MathService
],
template: '<div class="component">' +
'<h3>Component-2</h3>' +
'<div><button (click)="Add()">Click to add Count</button>' +
' - {{Count}}</div>' +
'<shared-sub-component></shared-sub-component>' +
'</div>'
})
export class Component2 implements DoCheck {
public Count = 0;
constructor(private math: MathService) { }
public ngDoCheck() {
console.log('component-2 Change detection!')
}
public Add() {
this.Count = this.math.AddOne(this.Count);
}
}
“app.module.ts”模块。
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { Component2 } from './components/component-2';
import { SharedSubComponent } from '../common/components/shared-sub-component';
@NgModule({
imports: [BrowserModule],
declarations: [
Component2,
SharedSubComponent
],
bootstrap: [
Component2
]
})
export class AppModule { }
“app2.ts”应用。
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app.module';
try { enableProdMode(); } catch (e) { }
const platform = platformBrowserDynamic();
platform.bootstrapModule(AppModule);
“systemjs.config.js”文件
为了在单个网页中引导多个 Angular 应用,我对 Angular 官方网站的“systemjs.config.js”文件做了一些修改。
// http://plnkr.co/edit/aZqdJe3OZ8K2odHioWkB?p=info
let ng2_getstarter = function (option) {
let configuration = {
paths: { 'npm:': option.npm },
map: {
app: option.appPath,
'@angular/core': 'npm:@angular/core/bundles/core.umd.js',
'@angular/common': 'npm:@angular/common/bundles/common.umd.js',
'@angular/compiler': 'npm:@angular/compiler/bundles/compiler.umd.js',
'@angular/platform-browser':
'npm:@angular/platform-browser/bundles/platform-browser.umd.js',
'@angular/platform-browser-dynamic':
'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js',
'@angular/http': 'npm:@angular/http/bundles/http.umd.js',
'@angular/router': 'npm:@angular/router/bundles/router.umd.js',
'@angular/forms': 'npm:@angular/forms/bundles/forms.umd.js',
// other libraries
'rxjs': 'npm:rxjs'
},
packages: {
rxjs: {
defaultExtension: 'js'
},
app: {
defaultExtension: 'js'
}
}
}
System.config(configuration);
return {
bootstrap: function (appFile) {
System.import(appFile).catch(function (err) { console.error(err);});
}
};
};
- “
ng2_getstarter
”函数接受一个 JSON “option
”对象,并返回一个用于引导 Angular 应用的对象。 - “
option
”对象需要提供“npm”文件夹的路径和 Angular 应用根文件夹的路径。
引导应用
有了“systemjs.config.js”文件的帮助,在同一页面中引导多个 Angular 应用非常简单。“Index.cshtml”文件展示了如何使用“ng2_getstarter
”函数。
<body>
<component-1></component-1>
<component-2></component-2>
</body>
在 HTML 部分,我们要引导“component-1
”和“component-2
”。“component-1
”打包在“app1.ts”文件中,而“component-2
”打包在“app2.ts”文件中。
let ng_starter = ng2_getstarter({
npm: '@Url.Content("~/node_modules/")',
appPath: '@Url.Content("~/ngApp/")'
});
ng_starter.bootstrap('@Url.Content("~/ngApp/app1/app1.js")');
ng_starter.bootstrap('@Url.Content("~/ngApp/app2/app2.js")');
- 通过将“npm”文件夹的路径和 Angular 应用根目录的路径传递给“
ng2_getstarter
”函数,我们可以获得一个“ng_starter
”对象。 - 要引导应用,我们只需在“
ng_starter
”对象上调用“boostrap()
”函数,并将每个 Angular 应用文件的路径传递进去。
“TSC”错误
根据你的 Visual Studio 中安装的 Typescript 编译器,你可能会遇到构建应用的问题。如果你遇到上述错误,很可能是你的 Typescript 编译器版本不匹配。你可以参考我 之前的帖子 来找出解决方案。
构建并加载网页
如果一切顺利,你可以编译并加载网页到浏览器中。
你可以在同一个页面中看到两个 Angular 应用都已成功引导。你可以点击按钮来检查它们是否正常工作。
如果你打开开发者工具查看控制台,你会发现每个应用中的按钮点击只会触发同一应用内的变更检测。如果你不打算让一个应用中的操作更新其他应用的状态,这应该会带来一些性能优势。
关注点
- 这是一篇关于如何在单个 HTML 页面中引导(bootstrap)多个 Angular 应用的注意事项。
- 并非总是需要在同一个页面中引导多个应用,但在某些条件下可能会有一些优势。
- 希望您喜欢我的博文,并希望这篇笔记能以某种方式帮助您。
历史
- 2017/3/5:首次修订