Angular2 单页应用的编写 by 嗡

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结构如下:
这里写图片描述

扫描二维码关注公众号,回复: 2727371 查看本文章

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服务器即可。

猜你喜欢

转载自blog.csdn.net/tzdwsy/article/details/55798814