Angular2 是一个All in one 的功能完备的框架,与上一版本Angular1.x 不同的地方在于:TypeScript(微软推出)成为了官方主推语言,同时引入了rxjs等框架,是程序编写更为严谨,效率更高;摒弃了$scope等复杂的元素简化为Component;内置更完整的路由功能,从而无需使用1.x时代的angular-ui-router等工具。
单页应用的特点
优势
- 在用户访问时,仅请求一次页面和静态资源。后端应用可以专注于api接口请求的处理,减少了页面跳转的路由处理压力以及页面渲染压力,减少频繁请求静态资源的压力。
- 页面切换不需要重新请求并渲染页面,减少了客户端页面切换的白屏问题,同时由于是本地路由加页面渲染,页面切换也更加迅速和流畅。
- 编写调试时,页面app实时更新并展示最新效果,调试效率高。
- 可以方便地移植到web、app中。
劣势
- 首屏需要下载体积较大的静态资源(可以通过webpack打包工具进行压缩与精减)
- 首屏需要启动应用,渲染较慢(Angular2支持预编译,起到加快启动速度的作用)
程序编写
开发环境
Angular2程序的编写需要使用nodejs(现在几乎是前端开发的必备工具了。。。),同时准备一个合适的IDE即可开始编写应用程序,我自己选用的是Visual Studio Code,插件管理和安装比较方便,功能相比Sublime Text也不差。
在nodejs和IDE准备完成后,启动一个控制台,或者直接通过Visual Studio Code的控制台操作即可。
其次,使用node的包管理工具npm安装angular-cli工具。这样在构建angular2应用和编译打包时候更加方便,否则还需要自行安装webpack并进行配置。
新建项目
通过命令行新建一个目录,比如:demoapp
mkdir demoapp
cd demo app
使用angular-cli新建一个angular2应用,ng new demoapp
,此时,node会自动下载angular2所依赖的node模块(放在node_module中)。构建完成后的项目接口如图:
- src/app文件夹中就是程序的主要代码
- src/index.html是首屏页面,src/main.ts是启动入口
- src/package.json为依赖管理配置
- src/angular-cli.json为Angular项目的配置(全局样式、工具库等引入可以放在这里)
- 其余文件请自行参考nodejs模块规范和angular文件规范
此时该项目已经是一个可以运行的初始项目了。通过执行ng serve
启动node服务,在浏览器中输入localhost:4200
即可预览。
项目启动流程大致如下:
- [main.ts]载入根模块[app.module.ts]:
platformBrowserDynamic().bootstrapModule(AppModule);
- [app.module.ts]载入根Component:
bootstrap: [AppComponent]
- AppComponent就是页面中的”app works!”。
主要概念
Component
Component包括了页面逻辑、页面模板和样式文件,其共同定义了一个页面的视图、数据和逻辑。在基础项目中我们可以编写自己的Component了。
- 在src/app文件夹下新建一个统一的Components目录。
mkdir components, cd components
- 通过angular-cli工具快速生成新Component。
ng g component component1
新生成的Component结构如下:
component.ts
文件声明了模板路径、模板标签名称、数据、逻辑代码。
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-component1',
templateUrl: './component1.component.html',
// 也可以使用template:`<p>template content</p>`的方法使用模板,但是单独放在一个html文件中更方便编辑
styleUrls: ['./component1.component.css']
})
export class Component1Component implements OnInit {
constructor() { }
ngOnInit() {
}
}
该文件表明了html中<app-compoment1>
是模板起作用的地方,数据和业务逻辑在该标签下起作用。这点和Angular1.x的directive很像。为方便了解这种ts文件的写法,推荐先自学了解一下TypeScript对于模块的声明和使用。看起来是不是很想Java或者C#?
component.html
文件内容如下:
<p>
component1 works!
</p>
及页面中<app-component1></app-component1>
会被该模板内容取代。
component.ts和component.html的关联和绑定(数据和行为)
component.ts文件代码改为如下:
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-component1',
templateUrl: './component1.component.html',
styleUrls: ['./component1.component.css']
})
export class Component1Component implements OnInit {
constructor() { }
ngOnInit() {
}
modelValue:string = '';
message:string = 'This is a message';
buttonClicked():void {
alert('button clicked!');
}
}
ts中声明了两个string变量和一个void方法。在html中进行如下修改:
<p>
{{message}}
</p>
<input [(ngModel)]="modelValue" name="modelValue" />
<p>your input is : {{modelValue}}</p>
<button (click)="buttonClicked()">click me</button>
效果将在后面展示。首先,我们先将该component在应用中显示出来。
目前应用进展示的根Component,因此需要把Component1变为根。
方法如下:
修改src/app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { AppComponent } from './app.component';
// 引入component1
import { Component1Component } from './components/component1/component1.component';
@NgModule({
declarations: [
AppComponent,
Component1Component
],
imports: [
BrowserModule,
FormsModule,
HttpModule
],
providers: [],
//将根Component改为Component1
bootstrap: [Component1Component]
})
export class AppModule { }
修改应用首页html将标签从<app-root>
改为<app-component1>
。
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Demoapp</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
<!--<app-root>Loading...</app-root>-->
<app-component1></app-component1>
</body>
</html>
应用会自动刷新,效果如下:
可以看见message:string = 'This is a message';
绑定已经起作用,第一个<p>
已经被替换为了message内容。
然后我们在input框中输入一些文字:
可以看到在用户输入文字时,下一行就实时显示了用户输入的结果。
我们单击按钮:
可以看到函数被触发。
这里仅仅是数据和行为绑定的简单例子,Angular1.x也有。但是Angular2和Angular1.x的区别在于Angular2支持单向传递数据和事件了,大大提升了应用脏值检测和页面渲染的性能。比如这里的()
代表传出,[]
代表传入,而[()]
代表双向绑定。具体请见Angular2的文档。
路由
路由负责应用内页面的跳转,是单页应用的核心之一。Angular2中可以通过<a routerLink="href">
标签跳转页面,也可以在ts文件中通过代码的形式进行跳转。
路由的声明
路由模块在应用启动时声明和初始化完毕,我们可以为路由单独声明一个模块NgModule。代码如下
- 新建一个模块app.routing.ts,包括了路径和Component之间的关系声明
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { Component1Component } from './components/component1/component1.component';
const routes: Routes = [
{ path: 'component1', component: Component1Component }
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports:[RouterModule]
})
export class AppRoutingModule { }
在app.module.ts中引入路由模块:
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
//引入所有component
import { AppComponent } from './app.component';
import { Component1Component } from './components/component1/component1.component';
//引入路由
import { AppRoutingModule } from './app.routing';
@NgModule({
declarations: [
AppComponent,
Component1Component
],
imports: [
BrowserModule,
FormsModule,
HttpModule,
//引入路由
AppRoutingModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
同时需要在模板中添加路由管理的视图位置<router-outlet>
路由实例
我们将原根component恢复成原Component。
同时将根component的模板修改为:
<h1>
{{title}}
</h1>
<a routerLink="component1">link-component1</a>
<button (click)="showComponent()">click to component1</button>
// 路由管理的视图在这里显示
<router-outlet></router-outlet>
根component.ts文件中引入路由组件和showComponent()方法
import { Component } from '@angular/core';
//引入路由组件
import { Router, ActivatedRoute } from '@angular/router';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
//在构造函数中实例化组件
constructor(
private router:Router
) {}
title = 'app works!';
showComponent(): void {
//路由跳转
this.router.navigate(['component1']);
}
}
启动效果:
应用首页如下:
此时<router-outlet>
里没有视图。点击链接或者按钮分别通过两种方法另路由导入视图,效果如下:
可以看到地址栏由localhost:4200
变成了 localhost:4200/component1
,页面中component1页显示了出来。
其余路由功能,请见Angular2文档。
服务
Angular2通过依赖注入来注入声明好的服务,良好的服务设计可以进一步使页面与逻辑分离,达到松耦合的效果。Angular2的依赖注入页Angular1.x不同,更加简洁清晰。大致步骤如下:
- 声明服务ts文件,加入@Injectable注解。
- 在app.module.ts中引入服务作为providers
- 在使用服务的component中注入服务
实例:将路由跳转封装为服务
在上例中若对每个Component都单独引入路由组件并声明跳转方法非常费事也容易出错,我们可以将路由跳转单独封装为服务。
step1: 建立服务
//建立services文件夹,并新建routing服务
mkdir services
cd services
ng g service routing
step2:实现服务方法
import { Injectable } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
@Injectable()
export class RoutingService {
constructor(
private router: Router
) { }
showComponent(): void {
this.router.navigate(['component1']);
}
}
step3:在app.module.ts中引入服务
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { AppComponent } from './app.component';
import { Component1Component } from './components/component1/component1.component';
import { AppRoutingModule } from './app.routing';
//引入服务
import { RoutingService } from './services/routing.service';
@NgModule({
declarations: [
AppComponent,
Component1Component
],
imports: [
BrowserModule,
FormsModule,
HttpModule,
AppRoutingModule
],
//引入服务
providers: [RoutingService],
bootstrap: [AppComponent]
})
export class AppModule { }
step4:在component中注入服务
import { Component } from '@angular/core';
//引入服务
import { RoutingService } from './services/routing.service';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
//服务注入
constructor(
private routing:RoutingService
) { }
title = 'app works!';
}
step5:调用
<h1>
{{title}}
</h1>
<a routerLink="component1">link-component1</a>
<!-- 直接调用服务的方法,而不是component.ts中的方法 -->
<button (click)="routing.showComponent()">click to component1</button>
<router-outlet></router-outlet>
效果和之前相同。
程序打包
使用angular-cli工具可以进行很方便的打包
ng build
打包的程序保留了ts脚本,在浏览器进行调试时十分方便。ng build --prod
则将应用程序打包为生产环境部署的版本,仅留下混淆、压缩过的js文件,体积大大减小,应用启动也更为迅速。(在我的实际测试中,调试包为3.8兆时,生产包近1.1兆)
默认打包操作输出文件夹为src/dist。将其拷贝部署到web服务器即可。