您好,登錄后才能下訂單哦!
寫在前面
由于現在網絡上Angular 4的相關技術文檔不是很充分,我寫出這個采坑的記錄文檔,一方面是想給自己在項目中遇到的各種問題與個人的理解記錄下來,另一方面也想著某些坑大家可能也會遇到,也可以給道友做一個參考。文檔中的很多地方多有不足,后期我會慢慢完善,也希望道友們能夠及時指出文檔中不正確的與可以優化的地方。
我計劃將該幫助文檔分為4個章節:
章節一:
關于angular 4 + ng-zorro在基礎布局與模塊拆分上的一些問題與操作步驟
章節二:
angular 4 引入路由=> 組件模塊化#module模塊化=> 路由模塊化(路由按需加載)
章節三:
引入攔截器,統一管理請求與相應=>引入http服務進行通訊=>引入service服務與后臺進行通訊=>拆分service服務=> 應用觀察者模式對數據進行發布與訂閱
章節四:
項目打包=>優化
============================= Begin ===============================
章節一:關于angular 4 + ng-zorro在基礎布局與模塊拆分上的一些問題與操作步驟
在使用阿里爸爸推出的Ng-zorro前,希望你先確保本地的angular-cli版本是最新的版本,目前最新的版本為1.6.3(2018/1/10) *兼容問題可能會導致后期項目打包后部門js丟失
如果你本地已經全局安裝了cli或者已經使用相對較舊的版本創建了angular 的項目,那么你可以按照下面的命令去更新你本地與項目中的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
你可以通過cmd命令行,使用 ng -v 去看到本地目前cli的版本。如果你已經安裝了最新的版本,你可以使用新版本的ng命令: [ng new "項目名稱" ]來創建一個新的angular 項目。如果你已經有angular項目了,那你需要去更新項目中的cli版本。具體的命令如下:
rmdir -rf node_modules dist npm install --save-dev @angular/cli@latest npm install
如果你完成了上面的操作,你可以打開package.json來看到你項目中的cli版本已經更換到了最新版本了。
在使用ng-zorro的過程中,需要注意兩點:
Ng-zorro并不能一次引入在多組件里進行使用,如果你的項目中存在子module,相關的依賴包需要在子module里進行引入。需要注意的是,你必須在module里通過forRoot()方法去使用。
//主module imports: [ BrowserModule, FormsModule, HttpClientModule, NgZorroAntdModule.forRoot(), BrowserAnimationsModule ]
在子module里,就不再需要forRoot()方法了:
//子module imports: [ CommonModule, HttpClientModule, NgZorroAntdModule ]
當你引入了所需的這些文件后,你就可以開始使用ng-zorro了。
章節二: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 導入module了之后,一般情況下會將路由單獨放在一個文件中進行引入。你需要在主module中進行引入,然后在主module里進行導出,如果你有子module,那么你需要在子module中進行導入,在子module中進行導出,因為Routermodule作為作為管理路由的工作,會將多個模板導入到同一模板中。如果你的項目中需要將路由文件拆分或者如要按需加載與懶加載相關功能,那么這時候你可能需要將路由進行相互關聯,在Vue中你可以通過ES6的一些語法進行鏈接,而angular 4提供了loadChildren來進行響應的相應的鏈接。具體的代碼如下:
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 包含兩個關鍵方法,forRoot(),forChild()
這兩個方法,做為控制多個模塊在同一模塊進行展示,分別在父子module中起到了關鍵作用,這也是LoadChildren生效的關鍵步驟。
//路由配置文件 { 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 } ] }
如果你的項目比較大,需要將路由進行模塊化或者進行一些懶加載或者按需加載的相關功能,你需要通過loadChildren將路由進行聯系。由于loadChildren是需要依賴到最外層路由導入的文件中的,所以你需要將你導入的模塊的路徑寫在路由參數中,而不是通過import的形式導入,并且你需要使用#去分割路徑,和導入的模塊名。
章節三:引入攔截器,統一管理請求與相應
如果你使用axios,你可能用過他的攔截功能,允許我們把身份認證,錯誤處理和服務器狀態碼等相關問題進行統一處理,而不需要在每個頁面去單獨處理,angular在實現攔截器功能的過程中也非常簡單,只需要實現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方法則有兩個參數,一個是 request,一個是next來調用下一個"中間件"。
按照angular 官網文檔的寫法,request有一個clone方法,可以去處理我們的請求,并在請求中加入響應的參數,如token, header, 瀏覽器cookie等
最后,你需要將你的請求參數傳遞到下一個中間件,而這里則是在return之后進行操作,像這樣:
return next.handle(clonedRequest)
在響應處理的過程中,包含多種情況,你需求將正確的請求返回到相應的組件,將異常的請求進行統一處理,而這個過程則是一種observable模式,我們需要用mergeMap, do等rxjs操作符來進行處理。
return next.handle(clonedRequest) .mergeMap((event: any) => { // 處理異常 reurn bservable.create(Observable => Observable.next(event)); }) .catch((res: HttpResponse<any>) => { return Observable.throw(res); })
使用catch進行捕獲,返回到組件中。下面是整個攔截器的代碼,需要的話可以進行引入,當然,你還需要現在主Module中進行引入,才能夠正常生效:
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方法需要單獨引入 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); }) } }
關于mergeMap和整個攔截器的用法,sf上的大神們也進行了詳細的說明:
點擊打開鏈接
引入http服務進行通訊
當你引入angular的攔截器之后,你就可以統一管理所以請求的請求頭,并且可以集中處理所有請求的響應體和異常情況了。那么http請求就變的非常簡單了。關于請求的寫法,官網和網上有很多的例子,你也可以封裝請求方法來進行使用。
引入service服務與后臺進行交互
在使用angular4的時候,我想將service做為存儲公共數據的地方,那么不同組件的公共的數據和參數,可以存儲在service中,那如果共用的數據總有某些場景下不是最新的,既然是這樣,為什么不按照官方的demo那樣,將數據源放在service中,之后通過訂閱或者promise的形式去拿到數據呢,這樣不同組件在使用一些共用數據的情況下,可以保證是最新數據,使用起來也更方便了。
既然提到了訂閱,就不得不說觀察者模式了。觀察者模式又被稱為發布訂閱模式。它定義了一種一對一對多的關系網絡。簡單來說就是讓多個觀察者去觀察一個對象,當被觀察對象發生任何改變的時候,所有訂閱了他的觀察者們都會及時的收到消息,并及時得到更新。這種感覺很像訂閱報紙一樣,訂閱報紙后,每當有新報紙出版都會送到你手里,讓你知道最新的消息,但是如果你取消訂閱報紙,那么你就不會收到最新版的報紙了。那么這兩個角色被觀察者和觀察者們用什么來表示呢?其實就是subject與observer。關于subject與observer在使用上,sf上面有很好很全面的介紹:點擊打開鏈接
具體怎么的使用也非常簡單,直接上代碼:
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是通過new的形式去創建的,那么當你服務端的數據返回之后,你可以使用next將相應流傳遞到你所定義的subject當中。服務層的寫法就是這樣,那么在組件中如何訂閱呢?上代碼:
this.service.getUserInfo(name, password) this.subscript = this.service.LoginSubject.subscribe( data => { here is your code }
service需要在構造函數中去聲明,這里就不寫了。service中的getUserInfo方法接受兩個參數,name與password,在這里進行發布操作,接下來就可以訂閱了。由于有些時候,我們會希望在第二次訂閱的時候,不會從頭開始接收 Observable 發出的值,而是從第一次訂閱當前正在處理的值開始發送,那么就需要對整個過程進行相應的處理。一般來說,我們不會主動去取消訂閱,但是根據業務情況不同我們可能需要去取消訂閱,怎么做呢?直接上代碼:
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() } }
這就是從創建被觀察者oberserver => 發布 => 訂閱 => 取消訂閱的整個流程。
拆分service服務
當你的業務越來越多的時候,你不可能只用一個service來支撐服務,你需要引入多個service進行與服務端的通訊。service模塊化其實很簡單,只要注意service進行provider的位置就行了,由于項目不同,具體的例子就不列舉了。
章節四:打包發布
每次總是小手發抖,擔心打包過程中會出現各種各樣的問題。我就列舉一下一些簡單的常見的打包后可能會出現的問題,如果大家沒遇到可以去程序員老黃歷查查你今天可能適合打包提測,如果你遇到了那太好了,我就將這些坑分享給道友們。
(1)版本問題
由于整個項目是結合ng-zorro來做的,可能由于cli的版本問題,打包過后如果遇到了部門按鈕失效,或者部門樣式丟失的問題,那么你可以嘗試去更新一下你全局的cli版本和項目中的cli版本,具體更新的方法,我在最前面已經寫過了。
(2)服務端刷新路由丟失的問題(hash/histroy模式)
導入 HashLocationStrategy 及 HashLocationStrategy,開啟hash模式。
import {HashLocationStrategy , LocationStrategy} from '@angular/common'; @NgModule({ declarations: [AppCmp], bootstrap: [AppCmp], imports: [BrowserModule, routes], providers: [{provide: LocationStrategy, useClass: HashLocationStrategy}] });
再次打包就不會出現刷新后404的問題了。
(3) 服務端打開后無法加載的問題
如果你部署后,根本就打不開,可以檢查一下你是否放在服務器根目錄的文件中了,如果不是,你可以修改打包后文件中的index.html,找到 <base href="/" rel="external nofollow" >修改href為'./' 就OK啦
(4) 文件體積過大,優化問題。
你可以通過ng build --prod去開啟細編譯,他會將你用不到的模塊和代碼都刪掉,--pord默認會開啟-aot編譯。
你還可以通過nginx gzip去進行優化操作,這里有一篇道友的文章,對優化進行了很多的處理,很牛,分享給大家:點擊打開鏈接
結尾:
這是這次做angular 項目中遇到一些我個人比較印象深刻的問題,記錄下來,也分享給大家。都是一些我自己理解的東西和百度學來的。可能會有錯誤,有些代碼可能也只供大家參考用。也希望道友們能指出不足之處積極溝通
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持億速云。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。