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


Posted in Javascript onDecember 05, 2018

写在前面

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

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

Javascript 相关文章推荐
js cookies 常见网页木马挂马代码 24小时只加载一次
Apr 13 Javascript
javascript eval和JSON之间的联系
Dec 31 Javascript
浅析showModalDialog数据缓存问题(用禁止浏览器缓存解决)
Jul 09 Javascript
jQuery菜单插件用法实例
Jul 25 Javascript
AngularJS中实现显示或隐藏动画效果的方式总结
Dec 31 Javascript
简单实现js放大镜效果
Jul 24 Javascript
详解Vue路由History mode模式中页面无法渲染的原因及解决
Sep 28 Javascript
原生JS实现ajax与ajax的跨域请求实例
Dec 01 Javascript
微信小程序如何获取openid及用户信息
Jan 26 Javascript
js+css3实现炫酷时钟
Aug 18 Javascript
Vue+element+cookie记住密码功能的简单实现方法
Sep 20 Javascript
夯基础之手撕javascript继承详解
Nov 09 Javascript
JS实现简单的点赞与踩功能示例
Dec 05 #Javascript
node.js实现为PDF添加水印的示例代码
Dec 05 #Javascript
vue组件之间通信实例总结(点赞功能)
Dec 05 #Javascript
JS获取今天是本月第几周、本月共几周、本月有多少天、是今年的第几周、是今年的第几天的示例代码
Dec 05 #Javascript
JS获取月的第几周和年的第几周实例代码
Dec 05 #Javascript
JavaScript实现学生在线做题计时器功能
Dec 05 #Javascript
vue-cli3搭建项目的详细步骤
Dec 05 #Javascript
You might like
php支付宝接口用法分析
2015/01/04 PHP
php+ajax实时刷新简单实例
2015/02/25 PHP
php中mysql操作buffer用法详解
2015/03/19 PHP
php实现微信公众号无限群发
2015/10/11 PHP
PHP的PDO常用类库实例分析
2016/04/07 PHP
PHP使用ajax的post方式下载excel文件简单示例
2019/08/06 PHP
正则表达式判断是否存在中文和全角字符和判断包含中文字符串长度
2008/09/27 Javascript
js 动态选中下拉框
2009/11/26 Javascript
学习js在线html(富文本,所见即所得)编辑器
2012/12/18 Javascript
如何获取select下拉框的值(option没有及有value属性)
2013/11/08 Javascript
Js实现动态添加删除Table行示例
2014/04/14 Javascript
JQuery设置获取下拉菜单某个选项的值(比较全)
2014/08/05 Javascript
jQuery根据用户电脑是mac还是pc加载对应样式的方法
2015/06/26 Javascript
jQuery动画显示和隐藏效果实例演示(附demo源码下载)
2015/12/31 Javascript
javascript和jQuery实现网页实时聊天的ajax长轮询
2016/07/20 Javascript
Three.js实现3D机房效果
2018/12/30 Javascript
typescript nodejs 依赖注入实现方法代码详解
2019/07/21 NodeJs
Net微信网页开发 使用微信JS-SDK获取当前地理位置过程详解
2019/08/26 Javascript
Vue项目打包压缩的实现(让页面更快响应)
2020/03/10 Javascript
在vue项目中封装echarts的步骤
2020/12/25 Vue.js
[01:11:11]Alliance vs RNG 2019国际邀请赛淘汰赛 败者组BO1 8.20.mp4
2020/07/19 DOTA
python+matplotlib实现礼盒柱状图实例代码
2018/01/16 Python
Python3实现取图片中特定的像素替换指定的颜色示例
2019/01/24 Python
Python3中编码与解码之Unicode与bytes的讲解
2019/02/28 Python
教你如何编写、保存与运行Python程序的方法
2019/07/12 Python
python自动识别文本编码格式代码
2019/12/26 Python
应届毕业生求职信范文分享
2013/12/26 职场文书
家佳咖啡店创业计划书
2013/12/27 职场文书
医院办公室主任职责
2013/12/29 职场文书
先进集体事迹材料
2014/02/17 职场文书
小班下学期评语
2014/05/04 职场文书
大学生求职自荐信
2015/03/24 职场文书
公务员处分决定书
2015/06/25 职场文书
Golang 如何实现函数的任意类型传参
2021/04/29 Golang
Python实现列表拼接和去重的三种方式
2021/07/02 Python
Anaconda安装pytorch和paddle的方法步骤
2022/04/03 Python