详解关于Angular4 ng-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/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 项目中遇到一些我个人比较印象深刻的问题,记录下来,也分享给大家。都是一些我自己理解的东西和百度学来的。可能会有错误,有些代码可能也只供大家参考用。也希望道友们能指出不足之处积极沟通

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • ng-zorro-antd 入门初体验

    Angular版本的 Ant Design 总算发布了,其名曰:ng-zorro-antd. 这人要是越期待,心就越着急.早在8月上旬我就开发好了 ng-zorro-vscode:一个在vscode下的snippets.因此,虽然那时未能探到源代码,但从官网的SDK中,就已经对 ng-zorro-antd 有一定全面性了解. 下面将基于我的理解,从宏观角度解析一下,或许能帮助你更好的去使用 ng-zorro-antd. 再次说一下,ng-zorro-antd 真的是非常有良心的作品. 一.安装

  • 浅谈ng-zorro使用心得

    前言 本周使用ng-zorro做了项目的原型,对它也有了一定的了解,总的来说不难,可以用强化版boostrap来理解它,由于黄庭祥初始化工作做得很好,在写的过程遇到的问题不是很麻烦,感谢祥哥. 问题一.button不起作用 问题描述:button按钮按下无响应,如下图: 代码如下: 解决思路: 首先删除button中所有的样式,保留最基本的html,发现依然无响应,说明问题不在button身上 排查栅格布局,发现table标签被我放在了button同一行新建了一个row,col放置table,问

  • Angular中使用ng-zorro图标库部分图标不能正常显示问题

    在ng-alain中,使用ng-zorro图标库,发现部分能正常显示,部分并不能显示,在控制台同时发现出错报错. ERROR Error: [@ant-design/icons-angular]: the icon redo-o does not exist or is not registered. at IconNotFoundError (ant-design-icons-angular.js:159) at MapSubscriber.project (ant-design-icons-

  • 详解在Angular4中使用ng2-baidu-map的方法

    一.引言 之前在Angular4使用过百度地图,记录一下踩过的坑 二.实现 1.安装 npm install angular2-baidu-map 2.在app.module.ts配置 ak key在http://lbsyun.baidu.com/apiconsole/key中创建 import { BrowserModule } from '@angular/platform-browser' import { NgModule } from '@angular/core' import {

  • 详解MySQL 查询语句的执行过程

    首先先简单的将一个查询语句背后MySQL做了什么捋一捋: 客户端发送一条查询给服务器. 服务器先检查查询缓存,如果命中了缓存,则立刻返回存储在缓存中的结果.否则进入下一个阶段. 服务器端进行SQL解析,预处理,再由优化器生成对应的执行计划. MySQL根据优化器生成的执行计划,调用存储引擎的API来执行查询. 将结果返回给客户端. 接着我们就将这个过程中的这些步骤详细的进行展开. 1.客户端和服务器端之间的通信方式 客户端和服务器之间的通信是一种半双工的通信,即在同一时刻,只能有一方向另一方发送

  • 详解Android壁纸服务的启动过程

    壁纸基础 android中的壁纸分为动态壁纸和静态壁纸两种,两种类型的壁纸都以Service的类型运行在系统后台. 静态壁纸:仅以图片的形式进行展示对于静态壁纸,可以使用WallpaperManager中的getDrawable()等接口获取到当前的bitmap图像. 动态壁纸:显示的内容为动态的内容,同时可以对用户的操作做出响应对于动态壁纸的实时图像,是没办法通过android中原生的接口获取到,需要获取到动态壁纸的图像得自己修改源码. 壁纸实现时涉及的几个主要的类: WallpaperSer

  • Springboot详解如何实现SQL注入过滤器过程

    目录 1.过滤器SqlInjectFilter 2.请求装饰类CustomRequestWrapper 3.过滤器注册 4.测试辅助类 4.1 结果对象ResultObj 4.2 Restful的Controller类 5.测试 5.1 POST请求测试 5.2 GET请求测试1 5.3 GET请求测试2 场景:以过滤器(Filter)的方式,对所有http请求的入参拦截,使用正则表达式匹配入参中的字符串.存在SQL注入风险的参数,中断请求,并立即返回提示信息.不存在SQL注入风险的参数,校验通

  • 详解如何在vue+element-ui的项目中封装dialog组件

    1.问题起源 由于 Vue 基于组件化的设计,得益于这个思想,我们在 Vue 的项目中可以通过封装组件提高代码的复用性.根据我目前的使用心得,知道 Vue 拆分组件至少有两个优点: 1.代码复用. 2.代码拆分 在基于 element-ui 开发的项目中,可能我们要写出一个类似的调度弹窗功能,很容易编写出以下代码: <template> <div> <el-dialog :visible.sync="cnMapVisible">我是中国地图的弹窗&l

  • 详解Java枚举类在生产环境中的使用方式

    目录 前言 使用 1.确定业务场景状态 2.定义枚举类 3.自定义查询方法 4.测试效果 总结 前言   Java枚举在项目中使用非常普遍,许多人在做项目时,一定会遇到要维护某些业务场景状态的时候,往往会定义一个常量类,然后添加业务场景相关的状态常量.但实际上,生产环境的项目中业务状态的定义大部分是由枚举类来完成的,因为更加清晰明确,还能自定义不同的方法来获取对应的业务状态值,十分方便. 以下代码均为生产环境已上线项目的代码片段,仅供参考. 使用 大体分为确定业务场景状态.定义枚举类.自定义查询

  • 详解 Mysql查询结果顺序按 in() 中ID 的顺序排列

    详解 Mysql查询结果顺序按 in() 中ID 的顺序排列 实例代码: <select id="queryGBStyleByIDs" resultMap="styleMap"> select style_num_id ,style_id,style_title,style_pic FROM gb_style where online = 1 AND is_hide = 0 and style_num_id in <foreach collecti

  • 详解ES6 CLASS在微信小程序中的应用实例

    ES6 CLASS基本用法 class Point { constructor(x, y) { this.x = x; this.y = y; } toString() { return '(' + this.x + ', ' + this.y + ')'; } } 1.1 constructor方法 constructor方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法.一个类必须有constructor方法,如果没有显式定义,一个空的constructor方法会被默认添加.

  • 详解在Spring MVC或Spring Boot中使用Filter打印请求参数问题

    使用Spring MVC或Spring Boot中打印或记录日志一般使用AOP记录Request请求和Response响应参数,在不使用AOP的前提下,如果在Filter中打印日志,在打印或消费请求类型为Content-Type:application/json的请求时,会出现严重的问题. 在Spring体系中,过滤器的定义我们一般采用继承OncePerRequestFilter的方式,当然也可以使用原始的Filter. 错误写法一: 如果不对request和response进行处理,使用伪代码

  • 详解UDP协议格式及在java中的使用

    UDP是面向无连接的通讯协议,由于通讯不需要连接,所以可以实现广播发送.UDP通讯时不需要接收方确认,属于不可靠的传输,可能会出现丢包现象,实际应用中要求程序员编程验证. UDP适用于DNS.视频音频等多媒体通信.广播通信(广播.多播).例如我们常用的QQ,就是一个以UDP为主,TCP为辅的通讯协议. UDP报文格式如下: UDP首部有8个字节,由4个字段构成,每个字段都是两个字节, 源端口:数据发送方的端口号. 目的端口:数据接收方的端口号. 长度:UDP数据报的整个长度(包括首部和数据),其

随机推荐