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

学习 Angular 教程 - 第五部分

starIconstarIconstarIconstarIconstarIcon

5.00/5 (6投票s)

2017 年 9 月 22 日

CPOL

10分钟阅读

viewsIcon

19431

downloadIcon

540

实现懒加载路由以及在 Angular 中使用 Jquery

目录

其余文章的链接

  • 第一部分,我们研究了 Node、TypeScript、模块加载器/打包器和 VS Code。
  • 第二篇文章中,我们创建了一个带有屏幕的简单基础 Angular 应用程序,并介绍了组件和模块等重要概念。
  • 第三篇文章中,我们研究了如何实现 SPA 和验证。
  • 第四篇文章中,我们了解了如何进行 HTTP 调用以及如何使用 Input 和 Output 创建自定义 Angular 组件。
  • 第五部分中,我们涵盖了两个实验 - 一个是如何在 Angular 中使用 Jquery 和延迟路由。
  • 第六部分中,我们再次涵盖了两个实验 - 使用提供者的管道和 DI。

在本文中,我们将看到如何实现延迟路由以及如何在 Angular 中使用 JQuery。

实验 11:使用动态路由进行延迟加载

理论

大型项目会有很多组件和模块,换句话说,我们最终会在浏览器客户端有很多 JS 文件。在客户端浏览器一次性加载这些 JS 文件会严重影响性能。

如果您在此阶段加载当前应用程序并查看开发人员工具,您将在网络选项卡中看到所有 JS 文件都在启动时加载。

当用户第一次访问网站时,我们希望只加载欢迎组件和主组件的 JS。

当用户单击供应商和客户时,相应的 JS 文件应该在那时加载。

让我们调查一下罪魁祸首是谁?

如果您查看我们项目的当前架构,我们有一个模块 (MainModule.ts),所有组件目前都属于这一个模块。因此,当这个模块加载时,它会加载它内部的所有组件。

简单来说,我们需要将模块分解成单独的物理模块文件。

步骤 1:创建三个不同的物理模块

正如理论的前一部分所讨论的,我们首先需要将项目划分为三个不同的物理模块文件:MainModuleSupplierModuleCustomerModule

因此,在 module 文件夹中,让我们创建三个不同的物理模块文件。我们已经有了 MainModule.ts,我们需要再创建两个。

MainModule.ts:此模块将加载“MasterPageComponent.ts”和“WelcomeComponent.ts”。

SupplierModule.ts:此模块将加载“SupplierComponent.ts”。

CustomerModule.ts:这将加载 CustomerComponentGridComponent。请记住,网格仅用于 Customer UI,因此它应该仅在加载 Customer 功能时加载。

步骤 2:从 MainModule 中移除 Supplier 和 CustomerComponent

首先,我们需要从 MainModule 中移除 CustomerComponentSupplierComponentGridComponent 的所有引用。下面是需要从 MainModule 中移除的划掉的源代码。在主模块中,我们只引用了 WelcomeComponentMastePageComponent

当模块之间不存在导入时,这两个模块是解耦的。即使您不使用该组件并且存在导入,解耦也不完整,JS 仍将被加载。

Lot of Code has been removed for clarity. Please download source code
for full code.
import { CustomerComponent }   from '../Component/CustomerComponent';
import { SupplierComponent }   from '../Component/SupplierComponent';
import { WelcomeComponent }   from '../Component/WelcomeComponent';
import { GridComponent }   from '../Component/GridComponent';
import { MasterPageComponent }   from '../Component/MasterPageComponent';

@NgModule({
    imports: [RouterModule.forRoot(ApplicationRoutes), 
        InMemoryWebApiModule.forRoot(CustomerApiService),
             BrowserModule,ReactiveFormsModule,
             FormsModule,HttpModule],
    declarations: [CustomerComponent,
                MasterPageComponent,
                SupplierComponent,
                WelcomeComponent, 
                GridComponent],
    bootstrap: [MasterPageComponent]
})
export class MainModuleLibrary { }

步骤 3:创建不同的路由文件

如前所述,“一个简单的导入引用”会使两个模块耦合。如果模块是耦合的,这些 JS 文件将被加载。

如果您还记得,“MainModule.ts”从“Routing.ts”加载路由,而 Routing.ts 具有对 SupplierComponentCustomerComponent 的导入引用。

因此加载路由也会加载其他组件,我们将无法实现延迟加载。

因此,让我们从 MainModule.ts 中移除 CustomerSupplier 组件的所有引用,请参见下面划掉的代码。

import {Component} from '@angular/core';
import {CustomerComponent} from '../Component/CustomerComponent';
import {SupplierComponent} from "../Component/SupplierComponent";
import {WelcomeComponent} from "../Component/WelcomeComponent";
export const ApplicationRoutes = [
    { path: 'Customer', component: CustomerComponent },
    { path: 'Supplier', component: SupplierComponent },
     { path: '', component:WelcomeComponent  },
    { path: 'UI/Index.html', component:WelcomeComponent  }
];

但是,我们仍然需要为“Customer”和“Supplier”定义路由,同时不能使用“import”语句,因为它会使模块耦合。如果您查看当前定义路由的语法,我们需要在 import 中包含该组件,否则我们无法定义路由。

{ path: 'CustomerComponent', component:CustomerComponent  },

为此,Angular 提供了一个很好的属性,称为“loadChildren”。在“loadChildren”中,我们需要像 string 一样用单引号给出模块。这意味着这将在运行时而不是编译时进行评估。

{ 
path: 'Customer',
loadChildren:'../Module/CustomerModuleLibrary#CustomerModuleLibrary' 
}

loadChildren”的结构应遵循此模式

路由的完整代码将如下图所示

import {Component} from '@angular/core';
import {WelcomeComponent} from "../Component/WelcomeComponent";
export const ApplicationRoutes = [
    { path: 'Customer', 
loadChildren:'../Module/CustomerModuleLibrary#CustomerModuleLibrary' },
    { path: 'Supplier', 
loadChildren: '../Module/SupplierModuleLibrary#SupplierModuleLibrary' },
     { path: '', component:WelcomeComponent  },
    { path: 'UI/Index.html', component:WelcomeComponent  },
    { path: 'UI', component:WelcomeComponent  }
];

我们还需要再创建两个路由文件,一个用于“Customer”,一个用于“Supplier”,如下图所示

import {Component} from '@angular/core';
import {CustomerComponent} from "../Component/CustomerComponent";
export const CustomerRoutes = [
    { path: 'Add', component:CustomerComponent  }
];
import {Component} from '@angular/core';
import {SupplierComponent} from "../Component/SupplierComponent";
export const SupplierRoutes = [
    { path: 'Add', component:SupplierComponent  }
];

SupplierRoutes”和“CustomerRoutes”是子路由,而“ApplicationRoutes”是父路由。

步骤 4:在 Supplier 和 Customer 模块中调用子路由

supplier 模块和 customer 模块中,我们需要加载它们在“步骤 3”中定义的相应路由。要加载子路由,我们需要使用“RouterModule.forChild”。

import { NgModule }      from '@angular/core';
import { CommonModule } from '@angular/common';
import {FormsModule , ReactiveFormsModule} from "@angular/forms"
import { SupplierComponent }   from '../Component/SupplierComponent';
import { RouterModule }   from '@angular/router';
import { SupplierRoutes }   from '../Routing/SupplierRouting';   
import {CustomerApiService} from "../Api/CustomerApi"
@NgModule({
    imports: [RouterModule.forChild(SupplierRoutes),
             CommonModule,ReactiveFormsModule,
             FormsModule],
    declarations: [SupplierComponent],
    bootstrap: [SupplierComponent]
})
export class SupplierModuleLibrary { }

同样,我们需要为 Customer 模块执行此操作。

import { NgModule }      from '@angular/core';
import { CommonModule } from '@angular/common';
import {FormsModule , ReactiveFormsModule} from "@angular/forms"
import { CustomerComponent }   from '../Component/CustomerComponent';
import { GridComponent }   from '../Component/GridComponent';
import { RouterModule }   from '@angular/router';
import { CustomerRoutes }   from '../Routing/CustomerRouting';   
import { InMemoryWebApiModule } from 'angular2-in-memory-web-api';
import {CustomerApiService} from "../Api/CustomerApi"
import { HttpModule } from '@angular/http';

@NgModule({
    imports: [RouterModule.forChild(CustomerRoutes), 
        InMemoryWebApiModule.forRoot(CustomerApiService),
             CommonModule,ReactiveFormsModule,
             FormsModule,HttpModule],
    declarations: [CustomerComponent, 
                GridComponent],
    bootstrap: [CustomerComponent]
})
export class CustomerModuleLibrary { }

步骤 5:配置路由链接

在步骤 3 和 4 中,我们定义了父路由和子路由。父路由在“Routing.ts”中定义,而子路由在“CustomerRouting.ts”和“SupplierRouting.ts”中定义。因此,现在路由链接必须更改为“Supplier/Add”和“Customer/Add”,如以下代码所示。

<a [routerLink]="['Supplier/Add']">Supplier</a> <br />
<a [routerLink]="['Customer/Add']">Customer</a><br>

现在,主页的完整代码如下所示

<table border="0" width="100%">
<tr>
<td width="20%"><img src="http://www.questpond.com/img/logo.jpg" alt="Alternate Text" />
</td>
<td width="80%">Questpond.com Private limited</td>
</tr>
<tr>
<td valign=top>Left Menu<br />
<a [routerLink]="['Supplier/Add']">Supplier</a> <br />
<a [routerLink]="['Customer/Add']">Customer</a><br>
<a [routerLink]="['']">Home</a>
</td>
<td>
<div id=”dynamicscreen”>
<router-outlet></router-outlet>
</div>
</td>
</tr>
<tr>
<td></td>
<td>Copy right @Questpond</td>
</tr>
</table>

步骤 6:将浏览器模块替换为通用模块

BrowserModule”和“CommonModule”是 Angular 的模块。“BrowserModule”包含启动服务和启动应用程序的代码,而“CommonModule”包含“NgIf”和“NgFor”等指令。
BrowserModule”重新导出了“CommonModule”。或者简单来说,“BrowserModule”使用了“CommonModule”。所以如果你加载“BrowserModule”,你也会加载“CommonModule”。

所以现在如果你在所有三个模块中都加载“BrowserModule”,那么你最终会加载“CommonModule”三次。当你进行延迟加载时,你真的不希望加载三次,它应该只加载一次。

因此,如果所有三个模块中都有“BrowserModule”,那么您最终会得到如下图所示的错误。

此错误表示“BrowserModule”已在“MainModule”中加载。请在“CustomerModule”和“SupplierModule”中使用“CommonModule”。

因此,在主模块中,加载浏览器模块,在其余模块中,加载“CommonModule”。

import { BrowserModule } from '@angular/platform-browser';
// Other imports have been removed for clarity
@NgModule({
    imports: [RouterModule.forRoot(ApplicationRoutes),
        InMemoryWebApiModule.forRoot(CustomerApiService),
             BrowserModule,ReactiveFormsModule,
             FormsModule,HttpModule],
    declarations: [
                MasterPageComponent,
                WelcomeComponent],
    bootstrap: [MasterPageComponent]
})
export class MainModuleLibrary { }

但在客户和供应商模块中,只加载通用模块。

import { CommonModule } from '@angular/common';
// other imports has been removed for clarity

@NgModule({
    imports: [RouterModule.forChild(SupplierRoutes),
             CommonModule,ReactiveFormsModule,
             FormsModule],
    declarations: [SupplierComponent],
    bootstrap: [SupplierComponent]
})
export class SupplierModuleLibrary { }
import { CommonModule } from '@angular/common';
// Other import has  been removed for claroty
@NgModule({
    imports: [RouterModule.forChild(CustomerRoutes),
        InMemoryWebApiModule.forRoot(CustomerApiService),
             CommonModule,ReactiveFormsModule,
             FormsModule,HttpModule],
    declarations: [CustomerComponent,
                GridComponent],
    bootstrap: [CustomerComponent]
})
export class CustomerModuleLibrary { }

步骤 7:检查延迟加载是否正常工作

现在运行您的应用程序,转到网络选项卡并检查延迟加载是否正常工作。您可以看到当应用程序启动时,只加载了“WelcomeComponent”和“MasterPageComponent”。一旦您单击 suppliercustomer,相应的组件将在此刻加载。

请设置适当的过滤器,以便您在网络中不会看到所有 JS 文件。

实验 12:在 Angular 中使用 jQuery

引言

jQuery 是一个非常古老且值得信赖的 JavaScript 框架。它拥有许多稳定且值得信赖的 UI 组件。作为惯例,您不应将 jQuery 与 Angular 一起使用,因为它们都可能与 DOM 操作重叠,导致混淆。

jQuery 使用“$”语法直接操作 HTML DOM,而 Angular 在 HTML DOM 上创建一个糖衣 DOM,并通过这个糖衣 DOM 进行操作。

因此,jQuery 可以操作 HTML DOM,而 Angular 不了解这种操作,从而造成更多混淆。

但是,有时我们希望使用 jQuery UI 组件,例如 Angular 中可能没有的 jQuery 网格、jQuery 日历等。

如果 Angular 是您的主要框架,那么首先查看您是否可以在 Angular 中获得解决方案,如果不能,则使用 jQuery 或任何其他框架。

在这个实验中,我们将使用 jQuery 来使我们的网格组件淡入淡出。因此,让我们创建一个名为 Hide Grid 的按钮。当最终用户单击 Hide Grid 时,网格应逐渐变得可见和不可见。

步骤 1:安装 JQuery

因此,第一步是获取 jQuery。让我们启动节点命令,同时获取 jQuery,并将其条目保存到“package.json”文件中。

npm install jquery –save

步骤 2:安装 JQuery 类型定义

JavaScript 分为两代,一代是 TypeScript 之前,即纯 JavaScript,另一代是 TypeScript 之后。JavaScript 是一种动态的无类型语言,而 TypeScript 是强类型语言。我们可以调用不存在的方法,分配未创建的变量等等。

另一方面,TypeScript 是强类型语言。所有事情都在设计时/编译时完成,TypeScript 必须预先了解方法、参数等所有内容。

现在,像 jQuery 这样的框架是用纯 JavaScript 编写的,因此如果要在 TypeScript 中使用它们,我们需要暴露它们的类型、参数等。这就是我们需要创建类型定义文件的地方。类型定义是 TypeScript 文件,它们暴露 JavaScript 对象的形状和结构,以便 TypeScript 可以在设计时理解 JavaScript 类型。

您一定想知道我们是否需要手动创建类型定义文件?不,您不需要。jQuery 已经有类型定义,相反,几乎所有流行的 JavaScript 框架都有它们的类型定义文件。

因此,要获取 jQuery 类型定义,我们需要通过指向“@types/jquery”进行 npm install。实际上,您可以使用“@types”加载任何类型,例如,如果您想加载 lodash,您可以对“@types/lodash”执行“npm install”。

npm install @types/jquery –save

步骤 3:在 UI 中提供 ID

jQuery 使用选择器引用 HTML UI 元素。在选择器中,我们需要提供名称或 ID,通过它们我们可以获取该 HTML 元素的引用。因此,让我们将我们的“grid”组件包装在一个 DIV 标签中,并为其分配一些“id”值,就像我们在下面的代码中给出的“divgrid”一样。

此外,我们创建了一个调用组件中“Fade”方法的按钮。

<input (click)="Fade()" type="button" value="Hide Grid"/>
<div id="divgrid">
<grid-ui 
      [grid-columns]="[{'colName':'CustomerCode'},
      {'colName':'CustomerName'},{'colName':'CustomerAmount'}]" 
    [grid-data]="Customers" 
    (grid-selected)="Select($event)"></grid-ui>
<div>

步骤 4:在组件中导入和使用 Jquery

现在,首先要做的是将 jQuery 导入到您的组件文件中。为此,我们需要使用以下代码。您可以看到 import 语句的使用方式略有不同。我们使用了“*”和“as”关键字。“*”表示我们要引用完整的 JQuery 文件,“$”是我们将在代码中引用 jQuery 语法的别名。

import * as $ from "jquery";

一旦导入了 jQuery,我们现在就可以使用“$”来执行 jQuery 语法。您可以看到我们创建了一个名为“Fade”的方法,其中我们引用了 HTML DIV ID 并调用了“fadeToggle”方法来实现淡入淡出。

import * as $ from "jquery";
export class CustomerComponent {
   // Code removed for clarity
   Fade(){
        $("#divgrid").fadeToggle(3000);
    }
	   }

步骤 5:在 Systemjs.config.js 中进行条目

我们的 JS 文件是使用 SystemJS 模块加载器加载的。因此,我们需要在“Systemjs.config.js”文件中进行条目,说明 jQuery 文件在哪里。

'jquery': '../node_modules/jquery/dist/jquery.js'

您可以看到,我们已将文件夹路径指定为“dist”。“dist”代表分发文件夹。jQuery 的最终编译副本在此文件夹中,因此我们已指定了它。

现在运行程序并查看输出。如果您单击“Hide”按钮,您应该会看到网格淡出和淡入。

如需进一步阅读,请观看下面的面试准备视频和逐步视频系列。

历史

  • 2017 年 9 月 22 日:初始版本
© . All rights reserved.