Angular 4.x + ng-zorro 自定义框架指南

写在前面html

因为如今网络上Angular 4-8的相关技术文档不是很充分,我写出这个采坑的记录文档,一方面是想给本身在项目中遇到的各类问题与我的的理解记录下来,另外一方面也想着某些坑你们可能也会遇到,也能够给道友作一个参考。文档中的不少地方多有不足,后期我会慢慢完善,也但愿道友们可以及时指出文档中不正确的与能够优化的地方。node

我计划将该帮助文档分为4个章节:webpack

章节一ios

关于angular 4 + ng-zorro在基础布局与模块拆分上的一些问题与操做步骤
复制代码

章节二nginx

angular 4 引入路由=> 组件模块化#module模块化=>  路由模块化(路由按需加载)
复制代码

章节三程序员

引入拦截器,统一管理请求与相应
=>
引入http服务进行通信
=>
引入service服务与后台进行通信
=>
拆分service服务
=> 
应用观察者模式对数据进行发布与订阅
复制代码

章节四web

项目打包及优化
复制代码

章节一:关于angular 4 + ng-zorro在基础布局与模块拆分上的一些问题与操做步骤npm

使用阿里爸爸推出的Ng-zorro前,但愿你先确保本地的angular-cli版本是最新的版本,目前最新的版本为1.6.3(2018/1/10) *兼容问题可能会致使后期项目打包后部门js丢失json

若是你本地已经全局安装了cli或者已经使用相对较旧的版本建立了angular 的项目,那么你能够按照下面的命令去更新你本地与项目中的cli版本去兼容ng-zorro:bootstrap

首先须要先卸载本地的angular-cli安装包:

npm uninstall -g angular-cli
    npm uninstall --save-dev angular-cli
复制代码

在全局安装最新版本的cli包:

npm uninstall -g @angular/cli
    npm cache clean(mac 电脑下可能须要 sudo命令获取权限)
    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的过程当中,须要注意两点:

  • Angular 框架采用了依赖注入及趋中心化管理模式,致使你并不能在多组件里引入直接引用 UI 组件进行使用,若是你的项目中存在子module,相关的依赖包须要在子module里进行引入。
  • 根据 angular 开发的规范,你应该提供一个公共的服务 module(一般这个模块可能叫作 shareModule,即共享模块), 你应该将你项目中使用到的全部的 UI components 在这个 shareModule 中进行引入,而后在对应的须要使用UI组件的子 Module 中引入 shareModule 使得业务组件与UI组件创建关联。

当你引入了所需的这些文件后,你就能够开始使用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的一些语法结合 webpack 等构建工具进行连接,而 angular4.0 之后提供了loadChildren来进行响应的相应的连接。具体的代码以下:

// 主 module 或 commonModule
    @NgModule({
        imports: [
            ....
            RouterModule.forRoot(appRoutes)
        ],
         exports: [
            RouterModule
        ],
    })
    // 子 module
    @NgModule({
      imports: [RouterModule.forChild(routes)],
      exports: [RouterModule],
    })
    
    routerModule 包含两个关键静态方法,forRoot(),forChild()
复制代码

这两个方法,作为控制多个模块在同一模块进行展现,分别在父子module中起到了关键做用,这也是LoadChildren生效的关键步骤。

//路由配置文件
    {
        path: 'index',
        component: NzDemoLayoutTopSide2Component,
        children: [
            {
                path: 'event',
                loadChildren: './event/eventAnalysis.module#EventAnalysisModule'
            }
        ]
    },
        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来调用下一个"中间件"。这种相似koa next 的调用方法,内部使用函数的聚合,将下一个拦截器经过 next 方法来触发并串联起来,所以你可使用多个拦截器来分别处理 auth, info, timeOut 等,这里只展现小项目的基本用法 request中包含clone方法,能够去处理咱们的请求,并在请求中加入响应的参数,如token, header, 浏览器cookie等

最后,你须要将你的请求参数传递到下一个中间件,而这里则是在return以后进行操做,像这样:

return next.handle(clonedRequest)
复制代码

在响应处理的过程当中,包含多种状况,你需求将正确的请求返回到相应的组件,将异常的请求进行统一处理,而这个过程则是一种observable模式,

咱们能够结合 rxjs 的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中进行引入,才可以正常生效:

// app.module 或者 core.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')
        });
        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="/">
复制代码

修改href为'./' 就OK啦

(4) 文件体积过大,优化问题。

你能够经过ng build --prod去开启细编译,他会将你用不到的模块和代码都删掉,--pord默认会开启-aot
复制代码

编译。

你还能够经过nginx gzip去进行优化操做,这里有一篇道友的文章,对优化进行了不少的处理,很牛,分享给你们:优化 angular 项目

结尾:

这是此次作angular 项目中遇到一些我我的比较印象深入的问题,记录下来,也分享给你们。但愿有不足之处能够及时与我进行沟通,互相学习进步。

Coding is an art of life, No matter what direction you focus on

相关文章
相关标签/搜索