一万哪、介紹
通過本文學(xué)習(xí)秀撇,能夠掌握搭建Angular開發(fā)環(huán)境及快速項(xiàng)目構(gòu)建嗅绰。
本文主要使用Angular7.x + Ng-zorro-ant(v7.5.0)組件庫進(jìn)行搭建舍肠。Angular框架基礎(chǔ)核心原理并不在文中進(jìn)行太多介紹,主要文檔參考請鏈接至官方中文文檔窘面。
代碼開發(fā)編輯器:Visual Studio Code
所需掌握技術(shù)點(diǎn):ES6翠语、Typescript、html财边、Less肌括、Rxjs
二、Angular開發(fā)環(huán)境搭建
安裝Angular-CLI
注:在安裝Angular-CLI之前酣难,請確保你的設(shè)備安裝了Node.js v10.9.0以上版本谍夭。
- 使用npm命令安裝angular/cli
npm install -g @angular/cli@7.3.6
- 安裝成功后,運(yùn)行以下命令即可查看是否安裝成功
ng version / ng v 查看腳手架版本信息
- 出現(xiàn)以下畫面表示已安裝成
_ _ ____ _ ___
/ \ _ __ __ _ _ _| | __ _ _ __ / ___| | |_ _|
/ △ \ | '_ \ / _` | | | | |/ _` | '__| | | | | | |
/ ___ \| | | | (_| | |_| | | (_| | | | |___| |___ | |
/_/ \_\_| |_|\__, |\__,_|_|\__,_|_| \____|_____|___|
|___/
Angular CLI: 7.3.6
Node: 11.12.0
OS: darwin x64
Angular:
...
Package Version
------------------------------------------------------
@angular-devkit/architect 0.13.6
@angular-devkit/core 7.3.6
@angular-devkit/schematics 7.3.6
@schematics/angular 7.3.6
@schematics/update 0.13.6
rxjs 6.3.3
typescript 3.2.4
三憨募、創(chuàng)建項(xiàng)目并運(yùn)行
- 運(yùn)行CLI命令創(chuàng)建紧索,如下所示
ng add ngExample
接下來,ng new命令會提示你添加初始項(xiàng)目所要包含的特性馋嗜,默認(rèn)選擇后需等待CLI安裝Angular包及其他依賴包齐板。
項(xiàng)目創(chuàng)建成功后,進(jìn)入項(xiàng)目根目錄運(yùn)行以下指令葛菇,向項(xiàng)目中添加ant組件庫
cd ngExample
ng add ng-zorro-antd
輸入命令后甘磨,選擇ng-zorro安裝特性,等待安裝成功眯停。
- 使用VSCode編輯工具济舆,打開項(xiàng)目文件夾,在VSCode終端中運(yùn)行以下CLI命令
ng serve
在終端中看到Compiled successfully莺债,表示已經(jīng)成功啟動Angular項(xiàng)目滋觉,具體參考以下圖片中內(nèi)容:
在瀏覽器中訪問localhost:4200签夭,目前默認(rèn)端口為4200,后續(xù)有需要可根據(jù)實(shí)際情況在CLI啟動命令中變更端口椎侠。
到此為止第租,在瀏覽器中看到以下頁面時(shí),整個(gè)環(huán)境搭建以及簡單項(xiàng)目構(gòu)建已經(jīng)完成我纪,即將進(jìn)入本文案例開發(fā)慎宾。
四、項(xiàng)目框架技術(shù)點(diǎn)
4.1 目錄結(jié)構(gòu)
由于初始化項(xiàng)目目錄結(jié)構(gòu)并不能很好的滿足開發(fā)規(guī)則浅悉,因此對目錄結(jié)構(gòu)進(jìn)行了優(yōu)化趟据,如下所示
目錄 | 說明 |
---|---|
src/app/core | 核心組件目錄,例如HTTP攔截器术健、國際化服務(wù)等 |
src/app/layout | 層級組件目錄汹碱,例如主框架頁面、頁面頭部組件等 |
src/app/routes | 業(yè)務(wù)組件目錄荞估,包含主體路由以及各業(yè)務(wù)模塊路由 |
src/app/shared | 共享文件目錄咳促,項(xiàng)目中可多次且通用文件可在此目錄中聲明 |
src/assets | 資源目錄,用于存放icon字體泼舱、國際化翻譯文件等缀、圖片等 |
src/environments | 環(huán)境配置文件 |
src/styles | 自定義樣式文件 |
除了上述優(yōu)化后的目錄介紹,其余文件介紹請參考Angular中文文檔 - 項(xiàng)目文件結(jié)構(gòu)
在tsconfig.json中配置模塊引入時(shí)轉(zhuǎn)為相對路徑
{
"compileOnSave": false,
"compilerOptions": {
"baseUrl": "src",
"outDir": "./dist/out-tsc",
"sourceMap": true,
"declaration": false,
"module": "es2015",
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"importHelpers": true,
"target": "es5",
"typeRoots": [
"node_modules/@types"
],
"lib": [
"es2018",
"dom"
],
"paths": {
"@shared": [
"app/shared"
],
"@shared/*": [
"app/shared/*"
],
"@core": [
"app/core"
],
"@core/*": [
"app/core/*"
]
}
}
}
例子:
import { SharedModule } from '@shared/shared.module';
app目錄
app目錄是我們要編寫的代碼目錄娇昙,我們寫的代碼都是放在這個(gè)目錄尺迂。
一個(gè)Angular程序至少需要一個(gè)模塊和一個(gè)組件,在我們新建項(xiàng)目的時(shí)候命令行已經(jīng)默認(rèn)生成出來了冒掌。
app
app.component.ts // 組件
app.module.ts // 模塊
4.2 頁面主體結(jié)構(gòu)
4.2.1 主體結(jié)構(gòu)開發(fā)
layout這個(gè)目錄在Angular中屬于一個(gè)模塊噪裕,所以需要有l(wèi)ayout.module.ts文件做承載,且要把該文件引入到根模塊app.module.ts中股毫。
- 在src/app/layout中創(chuàng)建default.component.html模板
<div class="container">
<!-- 頭部 -->
<div class="header"></div>
<!-- 中間內(nèi)容 -->
<div class="content">
<router-outlet></router-outlet>
</div>
<!--尾部-->
<div class="footer"></div>
</div>
- 在src/app/layout中創(chuàng)建default.component.ts文件
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'layout-default',
templateUrl: './default.component.html',
})
export class LayoutDefaultComponent {
}
該頁面為上中下布局膳音,中間層的<router-outlet></router-outlet>
為路由加載位置。
4.2.2 優(yōu)化頁面結(jié)構(gòu)
如何優(yōu)化頁面結(jié)構(gòu)铃诬?
根據(jù)ng-zorro-ant組件庫為我們提供了一些常用的頁面布局組件祭陷,我們只需要簡單步驟就能夠替換。
- 替換default.component.html內(nèi)容
<nz-layout class="layout">
<nz-header>
<ul nz-menu [nzTheme]="'dark'" [nzMode]="'horizontal'" style="line-height: 64px;">
<li nz-menu-item>用戶列表</li>
</ul>
</nz-header>
<nz-content style="padding:0 50px;">
<nz-breadcrumb style="margin:16px 0;">
<nz-breadcrumb-item>Home</nz-breadcrumb-item>
</nz-breadcrumb>
<div style="background:#fff; padding: 24px; min-height: 280px;">
<router-outlet></router-outlet>
</div>
</nz-content>
<nz-footer style="text-align: center;"></nz-footer>
</nz-layout>
<nz-layout>
<nz-header>
<nz-content>
<nz-footer>
這幾個(gè)是ng-zorro-ant組件庫所提供的已封裝組件趣席。
4.3 自定義組件
4.3.1 組件相關(guān)的概念
組件元數(shù)據(jù)裝飾器(@Component)
簡稱組件裝飾器兵志,用來告知Angular框架如何處理一個(gè)TypeScript類.
Component裝飾器包含多個(gè)屬性,這些屬性的值叫做元數(shù)據(jù)宣肚,Angular會根據(jù)這些元數(shù)據(jù)的值來渲染組件并執(zhí)行組件的邏輯模板(Template)
我們可以通過組件自帶的模板來定義組件的外觀想罕,模板以html的形式存在,告訴Angular如何來渲染組件霉涨,一般來說按价,模板看起來很像html惭适,但是我們可以在模板中使用Angular的數(shù)據(jù)綁定語法,來呈現(xiàn)控制器中的數(shù)據(jù)楼镐。控制器(controller)
控制器就是一個(gè)普通的typescript類癞志,他會被@Component來裝飾,控制器會包含組件所有的屬性和方法框产,絕大多數(shù)的業(yè)務(wù)邏輯都是寫在控制器里的今阳。控制器通過數(shù)據(jù)綁定與模板來通訊茅信,模板展現(xiàn)控制器的數(shù)據(jù),控制器處理模板上發(fā)生的事件墓臭。
/*這里是從Angular核心模塊里面引入了component裝飾器*/
import {Component} from '@angular/core';
/*用裝飾器定義了一個(gè)組件以及組件的元數(shù)據(jù) 所有的組件都必須使用這個(gè)裝飾器來注解*/
@Component({
/*組件元數(shù)據(jù) Angular會通過這里面的屬性來渲染組件并執(zhí)行邏輯 */
// 這是css選擇器蘸鲸,表示這個(gè)組件可以通過app-user來調(diào)用
selector: 'app-user',
// 組件的模板,定義了組件的布局和內(nèi)容
templateUrl: './user.component.html',
// 該模板引用less樣式
styleUrls: ['./user.component.less']
})
// UserComponent本來就是一個(gè)普通的typescript類窿锉,但是上面的組件元數(shù)據(jù)裝飾器告訴Angular酌摇,UserComponent是一個(gè)組件,需要把一些元數(shù)據(jù)附加到這個(gè)類上嗡载,Angular就會把UserComponent當(dāng)組件來處理
// 這個(gè)類實(shí)際上就是該組件的控制器窑多,我們的業(yè)務(wù)邏輯就是在這個(gè)類中編寫
export class UserComponent implements OnInit {
// 組件初始化時(shí)加載
ngOnInit() {
console.log('這是用戶管理頁面');
}
}
4.3.2 組件綁定數(shù)據(jù)
- 首先在
user.component.ts
中定義一個(gè)值title,隨后在組件初始化時(shí)賦值
export class UserComponent implements OnInit {
title: string = '';
ngOnInit() {
console.log('這是用戶管理頁面');
this.title = ‘用戶管理頁’;
}
}
- 如何使用這個(gè)值洼滚?在
user.component.html
頁面中使用雙括號綁定
<h1>{{title}}</h1>
4.3.3 雙向數(shù)據(jù)綁定
<input nz-input placeholder="用戶查詢" [(ngModel)]="value" />
export class UserComponent {
value: string;
}
4.4 路由
4.4.1 路由定義
- 在
management.module.ts
中添加
const routes: Routes = [
{
path: '', component: ManagementComponent, data: { title: '管理' },
children: [
{ path: 'user', component: UserComponent, data: { title: '用戶列表' } },
]
}
];
imports: [
RouterModule.forChild(routes)
],
4.4.2 路由模塊引入
- 把剛定義好的路由
ManagementModule
放到routes-routing.module.ts
中
const routes: Routes = [
{ path: '', component: LayoutDefaultComponent,
children: [
{ path: 'management', loadChildren: 'app/routes/management/management.module#ManagementModule' },
],
}
];
4.4.3 路由使用
- routeLink
<li nz-menu-item [routerLink]="['/management/user']">用戶菜單</li>
4.5 服務(wù)及創(chuàng)建觀察者對象
什么是服務(wù)埂息?定義公共的方法,使得方法在組件之間共享調(diào)用
Angular 把組件和服務(wù)區(qū)分開遥巴,以提高模塊性和復(fù)用性千康。 通過把組件中和視圖有關(guān)的功能與其他類型的處理分離開,你可以讓組件類更加精簡铲掐、高效拾弃。
4.5.1 創(chuàng)建服務(wù)并注冊引用
- 在
management
目錄中創(chuàng)建一個(gè)名為management.service.ts
文件,初始內(nèi)容如下
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class ManagementService {
constructor() {
}
}
- 服務(wù)注冊
在management.module.ts
中注冊
import { ManagementService } from './management.service';
@NgModule({
providers: [ManagementService]
})
- 服務(wù)引用
在user.component.ts
中引用
import { ManagementService } from '../management.service';
constructor(
private managementService: ManagementService
) { }
4.5.2 創(chuàng)建可觀察對象
可觀察對象支持在應(yīng)用中的發(fā)布者和訂閱者之間傳遞消息摆霉。 在需要進(jìn)行事件處理豪椿、異步編程和處理多個(gè)值的時(shí)候,可觀察對象相對其它技術(shù)有著顯著的優(yōu)點(diǎn)携栋。
可觀察對象是聲明式的 —— 也就是說搭盾,雖然你定義了一個(gè)用于發(fā)布值的函數(shù),但是在有消費(fèi)者訂閱它之前刻两,這個(gè)函數(shù)并不會實(shí)際執(zhí)行增蹭。 訂閱之后,當(dāng)這個(gè)函數(shù)執(zhí)行完或取消訂閱時(shí)磅摹,訂閱者就會收到通知滋迈。
- Observable聲明
export class ManagementService {
constructor(private http: HttpClient) {
}
getUsers(): Observable<any> {
let u: User = new User();
u.name = 'zhangsan';
u.email = "zhangsan@163.com";
let users = [{ name: 'zhangsan', email: 'xxx' }];
users.push(u);
return of(users);
}
}
class User {
public name: string;
public email: string;
}
4.6 訂閱
當(dāng)創(chuàng)建了觀察者對象后霎奢,需要通過subscribe
訂閱的方式來實(shí)現(xiàn)功能。
list;
ngOnInit() {
this.managementService.getUsers().subscribe(data => {
this.list = data;
});
}
- 訂閱成功后使用
ng-zorro-ant
表格組件現(xiàn)實(shí)饼灿,其中涉及到ng指令*ngIf
幕侠,指令具體使用方式請參考Angular中文文檔。
<nz-table #basicTable [nzData]="list">
<thead>
<tr>
<th>Name</th>
<th>Email</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let item of list">
<td>{{ item.name }}</td>
<td>{{ item.email }}</td>
</tr>
</tbody>
</nz-table>
4.7 表單
本文中使用了ng-zorro-ant
封裝的表單組件碍彭,是具有數(shù)據(jù)收集晤硕、校驗(yàn)和提交功能的表單。
validateForm: FormGroup;
constructor(
private fb: FormBuilder
) { }
ngOnInit() {
//
this.validateForm = this.fb.group({
userName: [null, [Validators.required]],
email: [null, [Validators.required]]
});
}
在html中庇忌,
<input formControlName="userName" nz-input />
<input formControlName="email" nz-input />
利用this.validateForm.controls[’userName‘].value
可獲取到輸入的值舞箍。
** FormGroup ** : 把每個(gè)子 FormControl 的值聚合進(jìn)一個(gè)對象,它的 key 是每個(gè)控件的名字皆疹。 它通過歸集其子控件的狀態(tài)值來計(jì)算出自己的狀態(tài)疏橄。
4.8 HTTP攔截器
其作用是可攔截HTTP請求返回響應(yīng)狀態(tài)碼,針對狀態(tài)碼進(jìn)行處理略就。
- 在core/net文件夾中創(chuàng)建以下攔截器
import { Injectable, Injector } from '@angular/core';
import { Router } from '@angular/router';
import { HttpInterceptor, HttpRequest, HttpHandler, HttpErrorResponse, HttpEvent, HttpResponseBase } from '@angular/common/http';
import { Observable, of, throwError } from 'rxjs';
import { mergeMap, catchError } from 'rxjs/operators';
import { NzMessageService, NzNotificationService } from 'ng-zorro-antd';
import { _HttpClient } from '@delon/theme';
import { environment } from '@env/environment';
import { DA_SERVICE_TOKEN, ITokenService } from '@delon/auth';
const CODEMESSAGE = {
200: '服務(wù)器成功返回請求的數(shù)據(jù)捎迫。',
201: '新建或修改數(shù)據(jù)成功。',
202: '一個(gè)請求已經(jīng)進(jìn)入后臺排隊(duì)(異步任務(wù))表牢。',
204: '刪除數(shù)據(jù)成功窄绒。',
400: '發(fā)出的請求有錯誤,服務(wù)器沒有進(jìn)行新建或修改數(shù)據(jù)的操作崔兴。',
401: '用戶沒有權(quán)限(令牌彰导、用戶名、密碼錯誤)恼布。',
403: '用戶得到授權(quán)螺戳,但是訪問是被禁止的。',
404: '發(fā)出的請求針對的是不存在的記錄折汞,服務(wù)器沒有進(jìn)行操作倔幼。',
406: '請求的格式不可得。',
410: '請求的資源被永久刪除爽待,且不會再得到的损同。',
422: '當(dāng)創(chuàng)建一個(gè)對象時(shí),發(fā)生一個(gè)驗(yàn)證錯誤鸟款。',
500: '服務(wù)器發(fā)生錯誤膏燃,請檢查服務(wù)器。',
502: '網(wǎng)關(guān)錯誤何什。',
503: '服務(wù)不可用组哩,服務(wù)器暫時(shí)過載或維護(hù)。',
504: '網(wǎng)關(guān)超時(shí)。',
};
/**
* 默認(rèn)HTTP攔截器伶贰,其注冊細(xì)節(jié)見 `app.module.ts`
*/
@Injectable()
export class DefaultInterceptor implements HttpInterceptor {
constructor(private injector: Injector) { }
get msg(): NzMessageService {
return this.injector.get(NzMessageService);
}
private goTo(url: string) {
setTimeout(() => this.injector.get(Router).navigateByUrl(url));
}
private checkStatus(ev: HttpResponseBase) {
if (ev.status >= 200 && ev.status < 300) return;
const errortext = CODEMESSAGE[ev.status] || ev.statusText;
this.injector.get(NzNotificationService).error(
`請求錯誤 ${ev.status}: ${ev.url}`,
errortext
);
}
private handleData(ev: HttpResponseBase): Observable<any> {
// 可能會因?yàn)?`throw` 導(dǎo)出無法執(zhí)行 `_HttpClient` 的 `end()` 操作
if (ev.status > 0) {
this.injector.get(_HttpClient).end();
}
this.checkStatus(ev);
// 業(yè)務(wù)處理:一些通用操作
switch (ev.status) {
case 200:
break;
case 401: // 未登錄狀態(tài)碼
// 請求錯誤 401: https://preview.pro.ant.design/api/401 用戶沒有權(quán)限(令牌蛛砰、用戶名、密碼錯誤)黍衙。
(this.injector.get(DA_SERVICE_TOKEN) as ITokenService).clear();
this.goTo('/passport/login');
break;
case 403:
case 404:
case 500:
break;
default:
if (ev instanceof HttpErrorResponse) {
console.warn('未可知錯誤泥畅,大部分是由于后端不支持CORS或無效配置引起', ev);
return throwError(ev);
}
break;
}
return of(ev);
}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
// 統(tǒng)一加上服務(wù)端前綴
let url = req.url;
if (!url.startsWith('https://') && !url.startsWith('http://')) {
url = environment.SERVER_URL + url;
}
const newReq = req.clone({ url });
return next.handle(newReq).pipe(
mergeMap((event: any) => {
// 允許統(tǒng)一對請求錯誤處理
if (event instanceof HttpResponseBase)
return this.handleData(event);
// 若一切都正常,則后續(xù)操作
return of(event);
}),
catchError((err: HttpErrorResponse) => this.handleData(err)),
);
}
}
- 攔截器使用
在Angular框架@angular/common/http
中琅翻,提供了HTTP_INTERCEPTORS
位仁,我們可以通過自己定義的攔截器觸發(fā),做出對應(yīng)業(yè)務(wù)處理方椎。
放到app.module.ts
中
providers: [
{ provide: HTTP_INTERCEPTORS, useClass: DefaultInterceptor, multi: true}
]
4.9 管道
什么是管道聂抢?Angular中的管道(pipe)是用來對輸入的數(shù)據(jù)進(jìn)行處理,如大小寫轉(zhuǎn)換棠众、數(shù)值和日期格式化等涛浙。
4.9.1 日期格式化管道
管道以 | 符號右邊表示。
<p>{{today | date:'yyyy-MM-dd HH:mm:ss' }}</p>
4.9.2 自定義管道
- 自定義管道的步驟:
使用 @Pipe 裝飾器定義 Pipe 的 metadata 信息摄欲,如 Pipe 的名稱 - 即 name 屬性
實(shí)現(xiàn) PipeTransform 接口中定義的 transform 方法
import { Pipe, PipeTransform } from '@angular/core';
[@Pipe](/user/Pipe)({ name: 'welcome' })
export class WelcomePipe implements PipeTransform {
transform(value: string): string {
if(!value) return value;
if(typeof value !== 'string') {
throw new Error('Invalid pipe argument for WelcomePipe');
}
return "Welcome to " + value;
}
}
- 自定義管道使用
<div>
<p>{{ 'ngExample' | welcome }}</p> <!-- Output: Welcome to ngExample -->
</div>
五、Angular性能優(yōu)化
5.1 多模塊懶加載
優(yōu)化前我們工程就一個(gè)主模塊文件(app.module.ts)疮薇,路由跳轉(zhuǎn)各頁面其實(shí)都屬于該模塊一部分胸墙,假如路由對應(yīng)各頁面都是子組件的話,編譯時(shí)都會被打包到同一個(gè)文件按咒。
拆分模塊迟隅,如下所示
const routes: Routes = [
{ path: '', component: LayoutDefaultComponent,
children: [
{ path: '', redirectTo: 'index/welcome', pathMatch: 'full' },
{ path: 'index', loadChildren: 'app/routes/index/index.module#IndexModule' },
{ path: 'management', loadChildren: 'app/routes/management/management.module#ManagementModule' },
],
}
];
可以看到寫法明顯不同,每個(gè)路由頁面其實(shí)都是一個(gè)單獨(dú)模塊励七,然后在編譯時(shí)每個(gè)模塊都會單獨(dú)編譯成一個(gè)文件智袭。而且路由到某個(gè)頁面時(shí),才會加載該模塊js文件掠抬。
運(yùn)行時(shí)可以從控制臺發(fā)現(xiàn)以下信息吼野,表示拆分成功
chunk {app-routes-index-index-module} app-routes-index-index-module.js, app-routes-index-index-module.js.map (app-routes-index-index-module) 6.02 kB [rendered]
chunk {app-routes-management-management-module} app-routes-management-management-module.js, app-routes-management-management-module.js.map (app-routes-management-management-module) 6.78 kB [rendered]
5.2 打包優(yōu)化
在打包時(shí)添加命令
--prod –aot
優(yōu)化編譯方式。
六两波、心得體會
從最開始的技術(shù)選型以及到后續(xù)慢慢深入學(xué)習(xí)了解Angular框架之后瞳步,每一個(gè)疑問難點(diǎn)解決過程走過來,Angular都能為我?guī)眢@喜腰奋。
Angular 以M(model數(shù)據(jù))V(view視圖/表現(xiàn)層)C(controller控制器/業(yè)務(wù)邏輯)為基礎(chǔ)单起,降低前端重復(fù)工作的勞動量,擴(kuò)展了HTML功能劣坊,組件復(fù)用性高嘀倒。
其中以雙向綁定數(shù)據(jù)為核心,Angular開發(fā)過程中基本上是在操作數(shù)據(jù)。
七测蘑、學(xué)習(xí)網(wǎng)站分享
八灌危、此文檔項(xiàng)目源碼
目前項(xiàng)目源碼Angular框架已升級至Angular8