65.9K
CodeProject 正在变化。 阅读更多。
Home

关于 Angular 2 Bootstrap 多个应用的说明

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.10/5 (5投票s)

2017年3月7日

CPOL

5分钟阅读

viewsIcon

29832

downloadIcon

393

这是一篇关于如何在单个 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>' +
    '&nbsp;-&nbsp;{{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>' +
    '&nbsp;-&nbsp;{{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>' +
    '&nbsp;-&nbsp;{{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:首次修订
© . All rights reserved.