您好,欢迎来到尚车旅游网。
搜索
您的当前位置:首页详解关于Angular4ng-zorro使用过程中遇到的问题

详解关于Angular4ng-zorro使用过程中遇到的问题

来源:尚车旅游网
详解关于Angular4ng-zorro使⽤过程中遇到的问题

写在前⾯

由于现在⽹络上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/clinpm 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@latestnpm install

如果你完成了上⾯的操作,你可以打开package.json来看到你项⽬中的cli版本已经更换到了最新版本了。在使⽤ng-zorro的过程中,需要注意两点:

Ng-zorro并不能⼀次引⼊在多组件⾥进⾏使⽤,如果你的项⽬中存在⼦module,相关的依赖包需要在⼦module⾥进⾏引⼊。需要注意的是,你必须在module⾥通过forRoot()⽅法去使⽤。

//主moduleimports: [

BrowserModule,FormsModule,HttpClientModule,

NgZorroAntdModule.forRoot(),

BrowserAnimationsModule]

在⼦module⾥,就不再需要forRoot()⽅法了:

//⼦moduleimports: [

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';//主moduleimports: [

BrowserModule,FormsModule,HttpClientModule,

NgZorroAntdModule.forRoot(),BrowserAnimationsModule]

//⼦moduleimports: [

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, next: HttpHandler): Observable> { 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) => {

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, next: HttpHandler): Observable> { const clonedRequest = req.clone({

headers: req.headers.set('Content-Type', 'text/plain;charset=UTF-8') });

// console.log(\"new headers\ 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) => {

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();

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,找到 修改href为'./' 就OK啦(4) ⽂件体积过⼤,优化问题。

你可以通过ng build --prod去开启细编译,他会将你⽤不到的模块和代码都删掉,--pord默认会开启-aot编译。你还可以通过nginx gzip去进⾏优化操作,这⾥有⼀篇道友的⽂章,对优化进⾏了很多的处理,很⽜,分享给⼤家:结尾:

这是这次做angular 项⽬中遇到⼀些我个⼈⽐较印象深刻的问题,记录下来,也分享给⼤家。都是⼀些我⾃⼰理解的东西和百度学来的。可能会有错误,有些代码可能也只供⼤家参考⽤。也希望道友们能指出不⾜之处积极沟通以上就是本⽂的全部内容,希望对⼤家的学习有所帮助,也希望⼤家多多⽀持。

因篇幅问题不能全部显示,请点此查看更多更全内容

Copyright © 2019- sceh.cn 版权所有

违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务