目录
前言
一、搭建项目
1.安装Angular CLI
2.创建项目
3.集成Element Angular
二、设置路由
1.创建路由模块
2.导入、导出RouterModule类
3.添加路由占位标签
4.创建路由跳转组件
5.添加路由跳转规则
6、实现跳转
7、获取当前路由参数
三、Elment Angular使用示例
四、总结
前言
Angular是目前主流的前端三大框架之一,它的上手稍有些难度,需要理解的概念很多,很多教程显得颇为复杂难懂。本文在作者自己探索的基础上,力求用通俗易懂的语言叙述Angular开发环境的基本配置,对一些不易理解的概念进行略去。
在开始使用angular之前,你可能需要一点点TypeScript
的基础,即便没有,也不是太大的问题,本文并不涉及高深的语法,如有需要,只需查阅TypeScript官方文档即可。
本文使用到的框架及版本:
- Angular 7.1
- Element Angular 0.7.6
需要说明的是,Element Angular作为与Angular配套的UI框架之一,并不是搭建项目必须的,当然可以选择其他UI框架甚至不用也可以,为了更完善的展现Angular开发环境搭建过程,本文使用了Element Angular。
一、搭建项目
1.安装Angular CLI
假设你已经安装了Node.js
和npm包管理器
,假设你还没安装,请按照相关教程安装。
现在,使用npm全局安装Angular CLI:
npm install -g @angular/cli
当全局安装了Angular CLI之后,我们便能够在命令行中使用ng
命令,使用以下命令可以查看Angular版本:
ng version
2.创建项目
命令行中定位到适当位置, 输入以下命令:
ng new ngtest
时间略久,大约花费几分钟,待命令执行完毕,将在该位置生成一个名为ngtest
的项目文件夹,里面是该项目的文件。
命令行中定位到项目根目录,输入以下命令:
ng serve --open
这时,将对项目进行打包,完成后启动一个本地服务器,端口为4200
,并且自动打开浏览器预览。这种预览是实时的,每当修改项目中的代码,浏览器都将会进行自动刷新。
- – open 是否自动打开浏览器。不带该参数时,不会自动打开浏览器,需要手动打开
当4200端口已经被占用时,无法启动本地服务器,这时可以指定其他端口:
ng serve --open --port=4201
- – port 指定本地服务器的端口。不带该参数时,默认为4200
3.集成Element Angular
本节也可以在后面步骤完成之后再进行,也可以选用其他UI组件,本文使用Element Angular作为示例。
命令行定位到项目根目录,执行以下命令:
npm install --save element-angular
注意,建议不要使用cnpm安装,这可能出现各种未知的问题,以下同。
在app.module.ts文件中引入:
import { ElModule } from 'element-angular';
在在app.module.ts
文件中注册:
@NgModule({
// 其他代码
imports: [
// 其他代码
ElModule
// 其他代码
]
// 其他代码
})
在src/styles.css
中引入Element Angular的样式文件:
@import "~element-angular/theme/index.css";
二、设置路由
1.创建路由模块
命令行中定位到项目根目录,执行以下命令:
ng generate module app-routing --flat --module=app
含义:
- ng: 代表使用Angular CLI执行本条命令
- generate: 英文含义"生成",顾名思义,可用于创建组件、指令、模块、服务等
- module: 代表使用generate命令创建一个模块
- app-routing: 创建的模块名字叫做app-routing
- –flat :额外参数,加上该参数不会单独一个创建一个文件夹。若不加该参数则会额外创建一个文件夹
具体来说,带上该额外参数,创建的模块将放入 src/app 中,而不是单独的目录中。
- –module=app: 额外参数,将创建的模块注册到app模块中
具体来说,它告诉 CLI 把它注册到 AppModule 的 imports 数组中,这样便不用手动去注册
此时,将在src目录下生成app-routing.module.ts
文件,它的内容如下:
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
@NgModule({
imports: [
CommonModule
],
declarations: []
})
export class AppRoutingModule { }
app-routing.module.ts
中定义了一个类,这个类中导入了两个模块NgModule
和CommonModule
,带有一个@NgModule
装饰器,然后导出AppRoutingModule
类。
装饰器的作用在于,描述应用的各部分如何组织到一起。假设没有装饰器,那么这个文件中导入的两个模块NgModule
和CommonModule
,它们便没有与AppRoutingModule
类产生联系,应用无法正常工作。@NgModule
是模块的装饰器,其他常用的装饰器还有@Component
等,顾名思义,@Component
是组件的装饰器。
@NgModule
中的几个参数作用如下:
- declaration: 该应用所拥有的组件、指令或管道
- imports: 该模块想要正常工作,还需要哪些模块
- providers: 该应用所需的服务
- bootstrap: 引导过程创建这些组件,并插入到浏览器的DOM中
2.导入、导出RouterModule类
首先,删除多余代码:
(1)CommonModule
的作用是提供NgIf
和NgFor
这样的指令,由于AppRoutingModule
作为路由模块,不需要使用NgIf和NgFor这样的指令,因此删除对 CommonModule
的引用;
(2)由于通常不会在路由模块中声明组件(一般在app.module.ts
中声明),故可以删除 @NgModule.declarations
。
删除后代码如下:
import { NgModule } from '@angular/core';
@NgModule({})
export class AppRoutingModule { }
导入RouterModule
类,添加一个 @NgModule.exports
数组,其中放入 RouterModule
,代码如下:
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
@NgModule({
exports: [RouterModule]
})
export class AppRoutingModule {}
@NgModule.exports
的作用在于,提供导入了自己的模块使用的可声明对象(组件、指令、管道类)的列表。
3. 添加路由占位标签
在app.component.html
之中,加入<router-outlet></router-outlet>
标签,如下:
<!--其他html代码 -->
<router-outlet></router-outlet>
<!--其他html代码 -->
<router-outlet>
标签的作用是,当监听到浏览器url的变化,就在该标签处显示对应的组件,它起到了占位的作用。
另外,由于Angular的根组件是app,其他组件都将嵌套在这个根组件之中,因此,我们将<router-outlet></router-outlet>
占位标签放在根组件app中而不是别处。
4.创建路由跳转组件
命令行中定位到项目根目录,执行以下命令:
ng generate component product-list
该命令创建了一个名为product-list的组件。这里我们可以发现,在src/app
目录下生辰了一个product-list
文件夹,该文件夹中结构如下:
- product-list.component.html (书写html结构)
- product-list.component.scss (书写scss样式)
- product-list.component.spec.ts(书写单元测试代码)
- product-list.component.ts (书写ts代码)
并且,在app.module.ts文件中发现多了以下代码:
import { ProductListComponent } from './product-list/product-list.component';
@NgModule({
declarations: [
ProductListComponent
]
}),
本节已完成,下面是其他方式的补充说明。
内联模板和样式
如果加上额外参数--inlineStyle
和--inlineTemplate
(两者可分别简写为--t
, --s
),即
ng generate component product-list --inlineStyle --inlineTemplate
这样,product-list文件夹下将只会生成两个文件:
- product-list.component.spec.ts
- product-list.component.ts
product-list.component.ts文件中代码如下:
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-product-list',
template: `
<p>
product-list works!
</p>
`,
styles: []
})
export class ProductListComponent implements OnInit {
constructor() { }
ngOnInit() {
}
}
由上可以看到,@component装饰器的对象中增加了两个属性, template
和styles
,他们分别起到了单独的product-list.component.html
文件和product-list.component.scss
文件的作用,又由于都被放置于product-list.component.ts
文件中,因此可分别叫做内联模板和内联样式,这种方式可以叫做内联模式。
当然,--inlineStyle
和--inlineTemplate
可以只使用一个,这样便只会创建一个内联模板或者内联样式,另一个仍会单独创建文件(html文件
或者scss文件
)。
手工创建
如果手工创建,也并不复杂,步骤如下:
首先,在src/app
目录下创建一个product-list文件夹,并新建上面几个文件。
然后,在procuct-list.component.ts
中完成类的定义,如下:
import { Component, OnInit } from "@angular/core";
@Component({
selector: 'app-procuct-list',
templateUrl: './procuct-list.component.html',
styleUrls: ['./procuct-list.component.scss']
})
export class ProcuctListComponent implements OnInit {
constructor() { }
ngOnInit() { }
}
在app.module.ts
中完成该组件的注册:
import { ProductListComponent } from './product-list/product-list.component';
@NgModule({
declarations: [
ProductListComponent
]
})
当我们使用Angular CLI创建组件时,以上代码将会自动生成,这是其便捷之处。
另外,这里使用的是generate命令,前面已经叙述过,generate可以创建模块、组件、指令、服务等,后面还将看到,创建服务同样使用该命令,仅仅是所携带的参数不同。
5.添加路由跳转规则
组件创建好之后,便可以配置路由规则了。
在app-routing.module.ts
文件中导入Routes
模块,导入需要配置路由的组件,在@NgModule
之前增加一个路由数组,并添加一条路由规则,这里以product-list
组件为例,代码如下:
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { ProductListComponent } from './product-list/product-list.component';
const appRoutes: Routes = [
{ path: 'product-list', component: ProductListComponent }
];
@NgModule({
exports: [RouterModule]
})
export class AppRoutingModule { }
其中:
- path: 浏览器中URL中的路径
- component:显示哪个组件
将路由规则数组appRoutes
添加到@NgModule.imports中,代码如下:
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { ProductListComponent } from './product-list/product-list.component';
const appRoutes: Routes = [
{ path: 'product-list', component: ProductListComponent }
];
@NgModule({
imports: [RouterModule.forRoot(appRoutes)],
exports: [RouterModule]
})
export class AppRoutingModule {}
这样,当URL匹配到该条规则时,就会将<router-outlet>
标签替换为ProductListComponent
的内容,整个页面就是替换后的app.component.html
的内容,你可以在product-list.component.html
文件中书写你的内容。
额外说明
(1)如果我们的路由跳转需要带参数,那么路由跳转规则形如:
{ path: 'detail/:id', component: ProductDetailComponent }
(2)默认路由几乎是必须的:
{ path: '', redirectTo: '/product-list', pathMatch: 'full' },
- redirectTo: 重定向至哪个路径
- pathMatch: 是否需要完全匹配。它的取值:
– ‘prefix’: 当URL的前缀与该条规则的path参数匹配,该条规则就会生效,例如http://localhost:4200/a
可以匹配该条规则,引发重定向
– ‘full’: 仅当URL与该条规则的path参数完全匹配时,该条规则才会生效,这时http://localhost:4200/a
不能匹配该条规则,因为仅有http://localhost:4200
才能与path: ''
匹配
该条规则表明,当URL与空路径“完全匹配” ,将重定向到路径 /product-list
,这时 { path: 'product-list', component: ProductListComponent }
的路由规则将会生效。
使用默认路由的意义在于,当我们的Angular应用启动时,浏览器的地址栏指向了网站的根路径,如果不使用默认路由,它匹配不到到任何现存路由,因此路由器也不会导航到任何地方, <router-outlet>
占位处将没有任何内容,所以默认路由几乎是必须的。
(3)错误路由也是必须的:
{ path: '**', component: PageNotFoundComponent }
以上的例子都需要你创建对应的组件。
6、实现跳转
属性跳转
在app.component.html文件中,加入一个导航链接:
<h2><a routerLink="/product-list">导航到产品列表</a></h2>
当然,这里routerLink
并不是只能作用于a
标签,它可以作用于各类标签。
现在,点击导航到产品列表
超链接,app.component.html
中<router-outlet>
标签处将被替换为product-list.component.html
的内容,而后app.component.html
的内容渲染在浏览器中。
函数跳转
如果要在函数中实现跳转,以在产品列表组件中跳转到首页为例,示例如下:
在product-list.component.html
文件中添加如下代码:
<el-button (click)='navHome()'>导航到首页</el-button>
函数navHome
的作用是跳转至首页,这时函数尚未定义。
在product-list.component.ts
中引入Router
模块:
import { Router } from "@angular/router";
在product-list.component.ts
文件构造函数中初始化路由:
constructor(private router: Router) { }
现在定义navHome
函数:
navHome() {
this.router.navigateByUrl('');
}
或者
navHome() {
this.router.navigate(['']);
}
navigate()
方法使用非常灵活强大,限于篇幅此处不再深入,读者可自行探索。
7、获取当前路由参数
在要获取路由参数的组件的ts
文件中引入ActivatedRoute
模块:
import { ActivatedRoute } from "@angular/router";
在构造函数中初始化一个属性:
constructor(private route: ActivatedRoute) { }
在需要获取当前路由参数的函数中调用,如:
ngOnInit() {
console.log(this.route.snapshot.params['id']);
}
或者
ngOnInit() {
this.route.queryParams.subscribe(params => {
console.log(params);
});
}
三、Elment Angular使用示例
本节定位是完全没使用过UI框架的初学者,作者仅作简要示例,如果你使用过UI框架,查看官方文档即可快速上手,无需查看本节。
Elment Angular的定位是UI框架,它的作用在于为我们定义好了日常常用的一系列组件的样式和交易效果。比如,简单的button组件,直接使用html创建:
<button>点我<button>
这时,将会发现原生样式相当难看。而当我们使用了Elment Angular之后,可使用它封装好的button组件:
<el-button>点我</el-button>
这时,样式将会比元生button美观很多,大大节省了修改样式的时间。我们还可以给el-button
加入各种属性,比如size
、type
、plain
,使按钮更加符合我们的需求。
更复杂一些,如时间选择器组件,若由开发者自己封装,则相当不容易做出一个成熟的组件,且容易出现隐藏的bug(因为bug的多少需要时间和案例来检验),UI框架相对成熟,比如在Elment Angular中,可使用如下标签:
<el-date-picker></el-date-picker>
再加上各种属性和方法,即可完成大部分业务需求。
当然,笔者并非是说要依赖于UI框架,对于部分业务需求,即便是使用了成熟的UI框架也无法实现,这时封装自己的组件仍然是必需的。
四、总结
本次项目搭建基本完成,需要说明的是:
本文只是大概完成了一个骨架,然而对于一些实际开发中可能是必需的部分仍未涉及。比如HTTP的设置
、懒加载的设置,各页面通用的菜单,服务、指令、管道的创建与使用,Angular的调试
等,然而在一篇开发环境配置的文章中将各方面都叙述到,一方面会加大阅读难度,令人产生畏难感,另一方面也会使得文章篇幅过长,这并非笔者初衷。
对于遗漏的一些问题,笔者会继续探索,择日再将后续的成果分享。