Angular7框架搭建及項目開發(fā)【已升級Angular8】

一、介紹

通過本文學習,能夠掌握搭建Angular開發(fā)環(huán)境及快速項目構建。

本文主要使用Angular7.x + Ng-zorro-ant(v7.5.0)組件庫進行搭建。Angular框架基礎核心原理并不在文中進行太多介紹,主要文檔參考請鏈接至官方中文文檔

代碼開發(fā)編輯器:Visual Studio Code

所需掌握技術點:ES6、Typescript、html、Less、Rxjs

二、Angular開發(fā)環(huán)境搭建

安裝Angular-CLI

注:在安裝Angular-CLI之前,請確保你的設備安裝了Node.js v10.9.0以上版本。

  1. 使用npm命令安裝angular/cli
npm install -g @angular/cli@7.3.6
  1. 安裝成功后,運行以下命令即可查看是否安裝成功
ng version / ng v 查看腳手架版本信息
  1. 出現以下畫面表示已安裝成
     _                      _                 ____ _     ___
    / \   _ __   __ _ _   _| | __ _ _ __     / ___| |   |_ _|
   / △ \ | '_ \ / _` | | | | |/ _` | '__|   | |   | |    | |
  / ___ \| | | | (_| | |_| | | (_| | |      | |___| |___ | |
 /_/   \_\_| |_|\__, |\__,_|_|\__,_|_|       \____|_____|___|
                |___/
    

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)建項目并運行

  1. 運行CLI命令創(chuàng)建,如下所示
ng add ngExample
  1. 接下來,ng new命令會提示你添加初始項目所要包含的特性,默認選擇后需等待CLI安裝Angular包及其他依賴包。

  2. 項目創(chuàng)建成功后,進入項目根目錄運行以下指令,向項目中添加ant組件庫

cd ngExample
ng add ng-zorro-antd

輸入命令后,選擇ng-zorro安裝特性,等待安裝成功。

  1. 使用VSCode編輯工具,打開項目文件夾,在VSCode終端中運行以下CLI命令
ng serve

在終端中看到Compiled successfully,表示已經成功啟動Angular項目,具體參考以下圖片中內容:

vscode.png
  1. 在瀏覽器中訪問localhost:4200,目前默認端口為4200,后續(xù)有需要可根據實際情況在CLI啟動命令中變更端口。

  2. 到此為止,在瀏覽器中看到以下頁面時,整個環(huán)境搭建以及簡單項目構建已經完成,即將進入本文案例開發(fā)。

browser.png

四、項目框架技術點

4.1 目錄結構

由于初始化項目目錄結構并不能很好的滿足開發(fā)規(guī)則,因此對目錄結構進行了優(yōu)化,如下所示

directory.png
目錄 說明
src/app/core 核心組件目錄,例如HTTP攔截器、國際化服務等
src/app/layout 層級組件目錄,例如主框架頁面、頁面頭部組件等
src/app/routes 業(yè)務組件目錄,包含主體路由以及各業(yè)務模塊路由
src/app/shared 共享文件目錄,項目中可多次且通用文件可在此目錄中聲明
src/assets 資源目錄,用于存放icon字體、國際化翻譯文件、圖片等
src/environments 環(huán)境配置文件
src/styles 自定義樣式文件

除了上述優(yōu)化后的目錄介紹,其余文件介紹請參考Angular中文文檔 - 項目文件結構

在tsconfig.json中配置模塊引入時轉為相對路徑

{
  "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目錄是我們要編寫的代碼目錄,我們寫的代碼都是放在這個目錄。
一個Angular程序至少需要一個模塊和一個組件,在我們新建項目的時候命令行已經默認生成出來了。

app
    app.component.ts  // 組件
    app.module.ts    // 模塊

4.2 頁面主體結構

4.2.1 主體結構開發(fā)

layout這個目錄在Angular中屬于一個模塊,所以需要有l(wèi)ayout.module.ts文件做承載,且要把該文件引入到根模塊app.module.ts中。

  • 在src/app/layout中創(chuàng)建default.component.html模板
<div class="container">
    <!-- 頭部 -->
    <div class="header"></div>

    <!-- 中間內容 -->
    <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)化頁面結構

如何優(yōu)化頁面結構?

根據ng-zorro-ant組件庫為我們提供了一些常用的頁面布局組件,我們只需要簡單步驟就能夠替換。

  • 替換default.component.html內容
<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>這幾個是ng-zorro-ant組件庫所提供的已封裝組件。

4.3 自定義組件

4.3.1 組件相關的概念

  • 組件元數據裝飾器(@Component)
    簡稱組件裝飾器,用來告知Angular框架如何處理一個TypeScript類.
    Component裝飾器包含多個屬性,這些屬性的值叫做元數據,Angular會根據這些元數據的值來渲染組件并執(zhí)行組件的邏輯

  • 模板(Template)
    我們可以通過組件自帶的模板來定義組件的外觀,模板以html的形式存在,告訴Angular如何來渲染組件,一般來說,模板看起來很像html,但是我們可以在模板中使用Angular的數據綁定語法,來呈現控制器中的數據。

  • 控制器(controller)
    控制器就是一個普通的typescript類,他會被@Component來裝飾,控制器會包含組件所有的屬性和方法,絕大多數的業(yè)務邏輯都是寫在控制器里的。控制器通過數據綁定與模板來通訊,模板展現控制器的數據,控制器處理模板上發(fā)生的事件。

/*這里是從Angular核心模塊里面引入了component裝飾器*/
import {Component} from '@angular/core';

/*用裝飾器定義了一個組件以及組件的元數據  所有的組件都必須使用這個裝飾器來注解*/
@Component({
  /*組件元數據  Angular會通過這里面的屬性來渲染組件并執(zhí)行邏輯 */

  // 這是css選擇器,表示這個組件可以通過app-user來調用
  selector: 'app-user',
  // 組件的模板,定義了組件的布局和內容
  templateUrl: './user.component.html',
  // 該模板引用less樣式
  styleUrls: ['./user.component.less']
})
// UserComponent本來就是一個普通的typescript類,但是上面的組件元數據裝飾器告訴Angular,UserComponent是一個組件,需要把一些元數據附加到這個類上,Angular就會把UserComponent當組件來處理
// 這個類實際上就是該組件的控制器,我們的業(yè)務邏輯就是在這個類中編寫
export class UserComponent implements OnInit {
  
  // 組件初始化時加載
  ngOnInit() {
    console.log('這是用戶管理頁面');
  }
}

4.3.2 組件綁定數據

  • 首先在user.component.ts中定義一個值title,隨后在組件初始化時賦值
export class UserComponent implements OnInit {
  
  title: string = '';
  
  ngOnInit() {
    console.log('這是用戶管理頁面');
    this.title = ‘用戶管理頁’;
  }
}
  • 如何使用這個值?在user.component.html頁面中使用雙括號綁定
<h1>{{title}}</h1>

4.3.3 雙向數據綁定

<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 服務及創(chuàng)建觀察者對象

什么是服務?定義公共的方法,使得方法在組件之間共享調用

Angular 把組件和服務區(qū)分開,以提高模塊性和復用性。 通過把組件中和視圖有關的功能與其他類型的處理分離開,你可以讓組件類更加精簡、高效。

4.5.1 創(chuàng)建服務并注冊引用

  • management目錄中創(chuàng)建一個名為management.service.ts文件,初始內容如下
import { Injectable } from '@angular/core';

@Injectable({
    providedIn: 'root'
})
export class ManagementService {
    constructor() {

    }
}
  • 服務注冊

management.module.ts中注冊

import { ManagementService } from './management.service';

@NgModule({
    providers: [ManagementService]
})

  • 服務引用

user.component.ts中引用

import { ManagementService } from '../management.service';

constructor(
    private managementService: ManagementService
) { }

4.5.2 創(chuàng)建可觀察對象

可觀察對象支持在應用中的發(fā)布者和訂閱者之間傳遞消息。 在需要進行事件處理、異步編程和處理多個值的時候,可觀察對象相對其它技術有著顯著的優(yōu)點。

可觀察對象是聲明式的 —— 也就是說,雖然你定義了一個用于發(fā)布值的函數,但是在有消費者訂閱它之前,這個函數并不會實際執(zhí)行。 訂閱之后,當這個函數執(zhí)行完或取消訂閱時,訂閱者就會收到通知。

  • 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 訂閱

當創(chuàng)建了觀察者對象后,需要通過subscribe訂閱的方式來實現功能。

list;

ngOnInit() {
    this.managementService.getUsers().subscribe(data => {
        this.list = data;
    });
}
  • 訂閱成功后使用ng-zorro-ant表格組件現實,其中涉及到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>
table1.png

4.7 表單

本文中使用了ng-zorro-ant封裝的表單組件,是具有數據收集、校驗和提交功能的表單。

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 ** : 把每個子 FormControl 的值聚合進一個對象,它的 key 是每個控件的名字。 它通過歸集其子控件的狀態(tài)值來計算出自己的狀態(tài)。

4.8 HTTP攔截器

其作用是可攔截HTTP請求返回響應狀態(tài)碼,針對狀態(tài)碼進行處理。

  • 在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: '服務器成功返回請求的數據。',
  201: '新建或修改數據成功。',
  202: '一個請求已經進入后臺排隊(異步任務)。',
  204: '刪除數據成功。',
  400: '發(fā)出的請求有錯誤,服務器沒有進行新建或修改數據的操作。',
  401: '用戶沒有權限(令牌、用戶名、密碼錯誤)。',
  403: '用戶得到授權,但是訪問是被禁止的。',
  404: '發(fā)出的請求針對的是不存在的記錄,服務器沒有進行操作。',
  406: '請求的格式不可得。',
  410: '請求的資源被永久刪除,且不會再得到的。',
  422: '當創(chuàng)建一個對象時,發(fā)生一個驗證錯誤。',
  500: '服務器發(fā)生錯誤,請檢查服務器。',
  502: '網關錯誤。',
  503: '服務不可用,服務器暫時過載或維護。',
  504: '網關超時。',
};

/**
 * 默認HTTP攔截器,其注冊細節(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> {
    // 可能會因為 `throw` 導出無法執(zhí)行 `_HttpClient` 的 `end()` 操作
    if (ev.status > 0) {
      this.injector.get(_HttpClient).end();
    }
    this.checkStatus(ev);
    // 業(yè)務處理:一些通用操作
    switch (ev.status) {
      case 200:
        break;
      case 401: // 未登錄狀態(tài)碼
        // 請求錯誤 401: https://preview.pro.ant.design/api/401 用戶沒有權限(令牌、用戶名、密碼錯誤)。
        (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)一加上服務端前綴
    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è)務處理。

放到app.module.ts

providers: [
    { provide: HTTP_INTERCEPTORS, useClass: DefaultInterceptor, multi: true}
]

4.9 管道

什么是管道?Angular中的管道(pipe)是用來對輸入的數據進行處理,如大小寫轉換、數值和日期格式化等。

4.9.1 日期格式化管道

管道以 | 符號右邊表示。

<p>{{today | date:'yyyy-MM-dd HH:mm:ss' }}</p>

4.9.2 自定義管道

  • 自定義管道的步驟:

使用 @Pipe 裝飾器定義 Pipe 的 metadata 信息,如 Pipe 的名稱 - 即 name 屬性
實現 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)化前我們工程就一個主模塊文件(app.module.ts),路由跳轉各頁面其實都屬于該模塊一部分,假如路由對應各頁面都是子組件的話,編譯時都會被打包到同一個文件。

拆分模塊,如下所示

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' },
      ],
    }
];

可以看到寫法明顯不同,每個路由頁面其實都是一個單獨模塊,然后在編譯時每個模塊都會單獨編譯成一個文件。而且路由到某個頁面時,才會加載該模塊js文件。

運行時可以從控制臺發(fā)現以下信息,表示拆分成功

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)化

在打包時添加命令
--prod –aot 優(yōu)化編譯方式。

六、心得體會

從最開始的技術選型以及到后續(xù)慢慢深入學習了解Angular框架之后,每一個疑問難點解決過程走過來,Angular都能為我?guī)眢@喜。

Angular 以M(model數據)V(view視圖/表現層)C(controller控制器/業(yè)務邏輯)為基礎,降低前端重復工作的勞動量,擴展了HTML功能,組件復用性高。

其中以雙向綁定數據為核心,Angular開發(fā)過程中基本上是在操作數據。

七、學習網站分享

Angular-CLI

ant組件庫

Angualr中文文檔

八、此文檔項目源碼

目前項目源碼Angular框架已升級至Angular8

Github鏈接

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容