詳解關(guān)于Angular4 ng-zorro使用過(guò)程中遇到的問(wèn)題
寫在前面
由于現(xiàn)在網(wǎng)絡(luò)上Angular 4的相關(guān)技術(shù)文檔不是很充分,我寫出這個(gè)采坑的記錄文檔,一方面是想給自己在項(xiàng)目中遇到的各種問(wèn)題與個(gè)人的理解記錄下來(lái),另一方面也想著某些坑大家可能也會(huì)遇到,也可以給道友做一個(gè)參考。文檔中的很多地方多有不足,后期我會(huì)慢慢完善,也希望道友們能夠及時(shí)指出文檔中不正確的與可以優(yōu)化的地方。
我計(jì)劃將該幫助文檔分為4個(gè)章節(jié):
章節(jié)一:
關(guān)于angular 4 + ng-zorro在基礎(chǔ)布局與模塊拆分上的一些問(wèn)題與操作步驟
章節(jié)二:
angular 4 引入路由=> 組件模塊化#module模塊化=> 路由模塊化(路由按需加載)
章節(jié)三:
引入攔截器,統(tǒng)一管理請(qǐng)求與相應(yīng)=>引入http服務(wù)進(jìn)行通訊=>引入service服務(wù)與后臺(tái)進(jìn)行通訊=>拆分service服務(wù)=> 應(yīng)用觀察者模式對(duì)數(shù)據(jù)進(jìn)行發(fā)布與訂閱
章節(jié)四:
項(xiàng)目打包=>優(yōu)化
============================= Begin ===============================
章節(jié)一:關(guān)于angular 4 + ng-zorro在基礎(chǔ)布局與模塊拆分上的一些問(wèn)題與操作步驟
在使用阿里爸爸推出的Ng-zorro前,希望你先確保本地的angular-cli版本是最新的版本,目前最新的版本為1.6.3(2018/1/10) *兼容問(wèn)題可能會(huì)導(dǎo)致后期項(xiàng)目打包后部門js丟失
如果你本地已經(jīng)全局安裝了cli或者已經(jīng)使用相對(duì)較舊的版本創(chuàng)建了angular 的項(xiàng)目,那么你可以按照下面的命令去更新你本地與項(xiàng)目中的cli版本去兼容ng-zorro:
首先需要先卸載本地的angular-cli安裝包:
npm uninstall -g angular-cli npm uninstall --save-dev angular-cli
在全局安裝最新版本的cli包:
npm uninstall -g @angular/cli npm cache clean npm install -g @angular/cli@latest
你可以通過(guò)cmd命令行,使用 ng -v 去看到本地目前cli的版本。如果你已經(jīng)安裝了最新的版本,你可以使用新版本的ng命令: [ng new "項(xiàng)目名稱" ]來(lái)創(chuàng)建一個(gè)新的angular 項(xiàng)目。如果你已經(jīng)有angular項(xiàng)目了,那你需要去更新項(xiàng)目中的cli版本。具體的命令如下:
rmdir -rf node_modules dist npm install --save-dev @angular/cli@latest npm install
如果你完成了上面的操作,你可以打開package.json來(lái)看到你項(xiàng)目中的cli版本已經(jīng)更換到了最新版本了。
在使用ng-zorro的過(guò)程中,需要注意兩點(diǎn):
Ng-zorro并不能一次引入在多組件里進(jìn)行使用,如果你的項(xiàng)目中存在子module,相關(guān)的依賴包需要在子module里進(jìn)行引入。需要注意的是,你必須在module里通過(guò)forRoot()方法去使用。
//主module imports: [ BrowserModule, FormsModule, HttpClientModule, NgZorroAntdModule.forRoot(), BrowserAnimationsModule ]
在子module里,就不再需要forRoot()方法了:
//子module imports: [ CommonModule, HttpClientModule, NgZorroAntdModule ]
當(dāng)你引入了所需的這些文件后,你就可以開始使用ng-zorro了。
章節(jié)二:angular 4 引入路由 => 組件模塊化#module模塊化 => 路由模塊化(路由按需加載)
2.1 angular 4 引入路由
import { NgModule } from '@angular/core'
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { FormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http';
import { NgZorroAntdModule } from 'ng-zorro-antd';
import { RouterModule, Routes } from '@angular/router';
import {HashLocationStrategy , LocationStrategy} from '@angular/common';
import { HTTP_INTERCEPTORS } from '@angular/common/http';
//主module imports: [ BrowserModule, FormsModule, HttpClientModule, NgZorroAntdModule.forRoot(), BrowserAnimationsModule ]
//子module imports: [ CommonModule, HttpClientModule, NgZorroAntdModule ],
angular 導(dǎo)入module了之后,一般情況下會(huì)將路由單獨(dú)放在一個(gè)文件中進(jìn)行引入。你需要在主module中進(jìn)行引入,然后在主module里進(jìn)行導(dǎo)出,如果你有子module,那么你需要在子module中進(jìn)行導(dǎo)入,在子module中進(jìn)行導(dǎo)出,因?yàn)镽outermodule作為作為管理路由的工作,會(huì)將多個(gè)模板導(dǎo)入到同一模板中。如果你的項(xiàng)目中需要將路由文件拆分或者如要按需加載與懶加載相關(guān)功能,那么這時(shí)候你可能需要將路由進(jìn)行相互關(guān)聯(lián),在Vue中你可以通過(guò)ES6的一些語(yǔ)法進(jìn)行鏈接,而angular 4提供了loadChildren來(lái)進(jìn)行響應(yīng)的相應(yīng)的鏈接。具體的代碼如下:
imports: [ BrowserModule, FormsModule, HttpClientModule, NgZorroAntdModule.forRoot(), BrowserAnimationsModule, EventAnalysisModule, RouterModule.forRoot( appRoutes ) ], exports: [ RouterModule ],
imports: [ CommonModule, FormsModule, ReactiveFormsModule , NgxEchartsModule, HttpClientModule, NgZorroAntdModule, RouterModule.forChild(EVENTROUTES) ], exports: [ RouterModule ],
routerModule 包含兩個(gè)關(guān)鍵方法,forRoot(),forChild()
這兩個(gè)方法,做為控制多個(gè)模塊在同一模塊進(jìn)行展示,分別在父子module中起到了關(guān)鍵作用,這也是LoadChildren生效的關(guān)鍵步驟。
//路由配置文件
{
path: 'index',
component: NzDemoLayoutTopSide2Component,
children: [
{
path: 'event',
loadChildren: './event/eventAnalysis.module#EventAnalysisModule'
}
]
},
//EventAnalysisModule 路由部分
{
path: 'eventAnalysis',
component: EventAanlysisComponent,
children: [
{
path: 'overview',
component: OverviewComponent
}, {
path: 'CreditEvaluation',
component: CreditEvaluationComponent
}, {
path: 'loanHistroy',
component: LoanHistroyComponent
}, {
path: 'userInfo',
component: UserInfoComponent
}
]
}
如果你的項(xiàng)目比較大,需要將路由進(jìn)行模塊化或者進(jìn)行一些懶加載或者按需加載的相關(guān)功能,你需要通過(guò)loadChildren將路由進(jìn)行聯(lián)系。由于loadChildren是需要依賴到最外層路由導(dǎo)入的文件中的,所以你需要將你導(dǎo)入的模塊的路徑寫在路由參數(shù)中,而不是通過(guò)import的形式導(dǎo)入,并且你需要使用#去分割路徑,和導(dǎo)入的模塊名。
章節(jié)三:引入攔截器,統(tǒng)一管理請(qǐng)求與相應(yīng)
如果你使用axios,你可能用過(guò)他的攔截功能,允許我們把身份認(rèn)證,錯(cuò)誤處理和服務(wù)器狀態(tài)碼等相關(guān)問(wèn)題進(jìn)行統(tǒng)一處理,而不需要在每個(gè)頁(yè)面去單獨(dú)處理,angular在實(shí)現(xiàn)攔截器功能的過(guò)程中也非常簡(jiǎn)單,只需要實(shí)現(xiàn)HttpInterceptor接口就可以了。
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const clonedRequest = req.clone({
headers: req.headers.set('Content-Type', 'text/plain;charset=UTF-8')
});
而intercept方法則有兩個(gè)參數(shù),一個(gè)是 request,一個(gè)是next來(lái)調(diào)用下一個(gè)"中間件"。
按照angular 官網(wǎng)文檔的寫法,request有一個(gè)clone方法,可以去處理我們的請(qǐng)求,并在請(qǐng)求中加入響應(yīng)的參數(shù),如token, header, 瀏覽器cookie等
最后,你需要將你的請(qǐng)求參數(shù)傳遞到下一個(gè)中間件,而這里則是在return之后進(jìn)行操作,像這樣:
return next.handle(clonedRequest)
在響應(yīng)處理的過(guò)程中,包含多種情況,你需求將正確的請(qǐng)求返回到相應(yīng)的組件,將異常的請(qǐng)求進(jìn)行統(tǒng)一處理,而這個(gè)過(guò)程則是一種observable模式,我們需要用mergeMap, do等rxjs操作符來(lái)進(jìn)行處理。
return next.handle(clonedRequest)
.mergeMap((event: any) => {
// 處理異常
reurn bservable.create(Observable => Observable.next(event));
})
.catch((res: HttpResponse<any>) => {
return Observable.throw(res);
})
使用catch進(jìn)行捕獲,返回到組件中。下面是整個(gè)攔截器的代碼,需要的話可以進(jìn)行引入,當(dāng)然,你還需要現(xiàn)在主Module中進(jìn)行引入,才能夠正常生效:
import { HTTP_INTERCEPTORS } from '@angular/common/http';
providers: [MyService,
{
provide: LocationStrategy,
useClass: HashLocationStrategy
},
{
provide: HTTP_INTERCEPTORS,
useClass: NoopInterceptor,
multi: true,
},
ApiModule]
攔截器的代碼:
import { Injectable } from '@angular/core';
import { Observable } from "rxjs/Observable";
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/mergeMap';
// thorw方法需要單獨(dú)引入
import 'rxjs/add/observable/throw';
import {HttpEvent, HttpInterceptor, HttpHandler, HttpRequest, HttpResponse} from '@angular/common/http';
@Injectable()
export class NoopInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const clonedRequest = req.clone({
headers: req.headers.set('Content-Type', 'text/plain;charset=UTF-8')
});
// console.log("new headers", clonedRequest.headers.keys());
return next.handle(clonedRequest)
.mergeMap((event: any) => {
// if (event instanceof HttpResponse) {
// return Observable.create(Observable => Observable.error(event));
// }
return Observable.create(Observable => Observable.next(event));
})
.catch((res: HttpResponse<any>) => {
return Observable.throw(res);
})
}
}
關(guān)于mergeMap和整個(gè)攔截器的用法,sf上的大神們也進(jìn)行了詳細(xì)的說(shuō)明:
引入http服務(wù)進(jìn)行通訊
當(dāng)你引入angular的攔截器之后,你就可以統(tǒng)一管理所以請(qǐng)求的請(qǐng)求頭,并且可以集中處理所有請(qǐng)求的響應(yīng)體和異常情況了。那么http請(qǐng)求就變的非常簡(jiǎn)單了。關(guān)于請(qǐng)求的寫法,官網(wǎng)和網(wǎng)上有很多的例子,你也可以封裝請(qǐng)求方法來(lái)進(jìn)行使用。
引入service服務(wù)與后臺(tái)進(jìn)行交互
在使用angular4的時(shí)候,我想將service做為存儲(chǔ)公共數(shù)據(jù)的地方,那么不同組件的公共的數(shù)據(jù)和參數(shù),可以存儲(chǔ)在service中,那如果共用的數(shù)據(jù)總有某些場(chǎng)景下不是最新的,既然是這樣,為什么不按照官方的demo那樣,將數(shù)據(jù)源放在service中,之后通過(guò)訂閱或者promise的形式去拿到數(shù)據(jù)呢,這樣不同組件在使用一些共用數(shù)據(jù)的情況下,可以保證是最新數(shù)據(jù),使用起來(lái)也更方便了。
既然提到了訂閱,就不得不說(shuō)觀察者模式了。觀察者模式又被稱為發(fā)布訂閱模式。它定義了一種一對(duì)一對(duì)多的關(guān)系網(wǎng)絡(luò)。簡(jiǎn)單來(lái)說(shuō)就是讓多個(gè)觀察者去觀察一個(gè)對(duì)象,當(dāng)被觀察對(duì)象發(fā)生任何改變的時(shí)候,所有訂閱了他的觀察者們都會(huì)及時(shí)的收到消息,并及時(shí)得到更新。這種感覺(jué)很像訂閱報(bào)紙一樣,訂閱報(bào)紙后,每當(dāng)有新報(bào)紙出版都會(huì)送到你手里,讓你知道最新的消息,但是如果你取消訂閱報(bào)紙,那么你就不會(huì)收到最新版的報(bào)紙了。那么這兩個(gè)角色被觀察者和觀察者們用什么來(lái)表示呢?其實(shí)就是subject與observer。關(guān)于subject與observer在使用上,sf上面有很好很全面的介紹:點(diǎn)擊打開鏈接
具體怎么的使用也非常簡(jiǎn)單,直接上代碼:
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { ApiModule } from '../api/api';
import { Subject } from 'rxjs/Subject';
import 'rxjs/add/operator/retry';
@Injectable()
// 登錄的方法
public LoginSubject = new Subject<any>();
public getUserInfo(name, pwd):void {
var data = {"username":name,"password":pwd};
var val = this.HOST.host;
this.$http
.post(`${val}/login`, data)
.retry(3)
.subscribe( res => {
this.LoginSubject.next(res)
});
}
subject是通過(guò)new的形式去創(chuàng)建的,那么當(dāng)你服務(wù)端的數(shù)據(jù)返回之后,你可以使用next將相應(yīng)流傳遞到你所定義的subject當(dāng)中。服務(wù)層的寫法就是這樣,那么在組件中如何訂閱呢?上代碼:
this.service.getUserInfo(name, password)
this.subscript = this.service.LoginSubject.subscribe( data => { here is your code }
service需要在構(gòu)造函數(shù)中去聲明,這里就不寫了。service中的getUserInfo方法接受兩個(gè)參數(shù),name與password,在這里進(jìn)行發(fā)布操作,接下來(lái)就可以訂閱了。由于有些時(shí)候,我們會(huì)希望在第二次訂閱的時(shí)候,不會(huì)從頭開始接收 Observable 發(fā)出的值,而是從第一次訂閱當(dāng)前正在處理的值開始發(fā)送,那么就需要對(duì)整個(gè)過(guò)程進(jìn)行相應(yīng)的處理。一般來(lái)說(shuō),我們不會(huì)主動(dòng)去取消訂閱,但是根據(jù)業(yè)務(wù)情況不同我們可能需要去取消訂閱,怎么做呢?直接上代碼:
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { HttpClient, HttpHeaders } from '@angular/common/http'
import { ApiModule } from '../api/api';
import { MyService } from '../myService/service.component';
import {NzMessageService} from 'ng-zorro-antd';
import { Subscription } from 'rxjs/Subscription';
@Component({
selector: 'login-component',
templateUrl: './login.component.html',
styleUrls: [
'./login.component.less'
]
})
export class LoginComponent implements OnInit {
subscript: Subscription
constructor (private router:Router,
private service: MyService,
private _message: NzMessageService,) {
this.subscript = new Subscription()
}
}
ngOnInit ():void {
this.service.getUserInfo(name, password)
this.subscript = this.service.LoginSubject.subscribe( data => {
// here is your code
}
this.subscript.unsubscribe()
}
}
這就是從創(chuàng)建被觀察者oberserver => 發(fā)布 => 訂閱 => 取消訂閱的整個(gè)流程。
拆分service服務(wù)
當(dāng)你的業(yè)務(wù)越來(lái)越多的時(shí)候,你不可能只用一個(gè)service來(lái)支撐服務(wù),你需要引入多個(gè)service進(jìn)行與服務(wù)端的通訊。service模塊化其實(shí)很簡(jiǎn)單,只要注意service進(jìn)行provider的位置就行了,由于項(xiàng)目不同,具體的例子就不列舉了。
章節(jié)四:打包發(fā)布
每次總是小手發(fā)抖,擔(dān)心打包過(guò)程中會(huì)出現(xiàn)各種各樣的問(wèn)題。我就列舉一下一些簡(jiǎn)單的常見的打包后可能會(huì)出現(xiàn)的問(wèn)題,如果大家沒(méi)遇到可以去程序員老黃歷查查你今天可能適合打包提測(cè),如果你遇到了那太好了,我就將這些坑分享給道友們。
(1)版本問(wèn)題
由于整個(gè)項(xiàng)目是結(jié)合ng-zorro來(lái)做的,可能由于cli的版本問(wèn)題,打包過(guò)后如果遇到了部門按鈕失效,或者部門樣式丟失的問(wèn)題,那么你可以嘗試去更新一下你全局的cli版本和項(xiàng)目中的cli版本,具體更新的方法,我在最前面已經(jīng)寫過(guò)了。
(2)服務(wù)端刷新路由丟失的問(wèn)題(hash/histroy模式)
導(dǎo)入 HashLocationStrategy 及 HashLocationStrategy,開啟hash模式。
import {HashLocationStrategy , LocationStrategy} from '@angular/common';
@NgModule({
declarations: [AppCmp],
bootstrap: [AppCmp],
imports: [BrowserModule, routes],
providers: [{provide: LocationStrategy, useClass: HashLocationStrategy}]
});
再次打包就不會(huì)出現(xiàn)刷新后404的問(wèn)題了。
(3) 服務(wù)端打開后無(wú)法加載的問(wèn)題
如果你部署后,根本就打不開,可以檢查一下你是否放在服務(wù)器根目錄的文件中了,如果不是,你可以修改打包后文件中的index.html,找到 <base href="/" rel="external nofollow" >修改href為'./' 就OK啦
(4) 文件體積過(guò)大,優(yōu)化問(wèn)題。
你可以通過(guò)ng build --prod去開啟細(xì)編譯,他會(huì)將你用不到的模塊和代碼都刪掉,--pord默認(rèn)會(huì)開啟-aot編譯。
你還可以通過(guò)nginx gzip去進(jìn)行優(yōu)化操作,這里有一篇道友的文章,對(duì)優(yōu)化進(jìn)行了很多的處理,很牛,分享給大家:點(diǎn)擊打開鏈接
結(jié)尾:
這是這次做angular 項(xiàng)目中遇到一些我個(gè)人比較印象深刻的問(wèn)題,記錄下來(lái),也分享給大家。都是一些我自己理解的東西和百度學(xué)來(lái)的??赡軙?huì)有錯(cuò)誤,有些代碼可能也只供大家參考用。也希望道友們能指出不足之處積極溝通
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
AngularJS基礎(chǔ) ng-non-bindable 指令詳細(xì)介紹
本文主要講解AngularJS ng-non-bindable 指令,這里幫大家整理了ng-non-bindable指令的基本知識(shí)資料,有需要的小伙伴可以參考下2016-08-08
AngularJS中$http服務(wù)常用的應(yīng)用及參數(shù)
大家都知道,AngularJS中的$http有很多參數(shù)和調(diào)用方法,所以本文只記錄了比較常用的應(yīng)用及參數(shù),方便大家以后使用的時(shí)候參考學(xué)習(xí),下面一起來(lái)看看吧。2016-08-08
詳解angularjs4部署文件過(guò)大解決過(guò)程
這篇文章主要介紹了詳解angularjs4部署文件過(guò)大解決過(guò)程,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-12-12
angular5 httpclient的示例實(shí)戰(zhàn)
本篇文章主要介紹了angular5 httpclient的示例實(shí)戰(zhàn),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-03-03
angular6根據(jù)environments配置文件更改開發(fā)所需要的環(huán)境的方法
這篇文章主要介紹了angular6根據(jù)environments配置文件更改開發(fā)所需要的環(huán)境的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2019-03-03
Angular實(shí)現(xiàn)的敏感文字自動(dòng)過(guò)濾與提示功能示例
這篇文章主要介紹了Angular實(shí)現(xiàn)的敏感文字自動(dòng)過(guò)濾與提示功能,結(jié)合實(shí)例形式分析了AngularJS針對(duì)字符串的輸入判定及實(shí)時(shí)顯示相關(guān)操作技巧,需要的朋友可以參考下2017-12-12
Angular如何在應(yīng)用初始化時(shí)運(yùn)行代碼詳解
這篇文章主要給大家介紹了關(guān)于Angular如何在應(yīng)用初始化時(shí)運(yùn)行代碼的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2018-06-06
angularjs數(shù)組判斷是否含有某個(gè)元素的實(shí)例
下面小編就為大家分享一篇angularjs數(shù)組判斷是否含有某個(gè)元素的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-02-02
Angular4學(xué)習(xí)筆記之準(zhǔn)備和環(huán)境搭建項(xiàng)目
這篇文章主要介紹了Angular4學(xué)習(xí)筆記之準(zhǔn)備和環(huán)境搭建項(xiàng)目,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-08-08

