转载

Angular2 初探

Angular2初探

盼星星盼月亮终于把angular2 beta盼来了,在beta前一个月快一天一个release都把小G折磨坏了,小G公司用的angular2,然后就跟着Google那些工程师一天一个update,然后还要看update把以前的啥功能break了没,当时改rxjs的api的时候真的是想死的心都有了。还有rxjs带来的各种bug,终于迎来了angular2 beta,码农翻身做主人,真是这个月最开心的事情了。接下来就是把所有code改好build pass就好。小G用angular已经两年多了,一开始用的angular做了一个officeapp,公司看不错就migrate到angular2的webapp,所以小G是经历过angular1和angular2的人,看过angular1的各种丑和angular2的各种帅。所以这篇文章主要是强推angular2的软文,主要快速上手angular2,介绍一下angular2的主要优势,快速接触angular2的一些语法及Component,Service的用法。还有就是介绍小G最喜欢的rxjs,因为angular2已经基于rxjs了,把所有东西变成stream然后各种神操作。当然一篇blog不足够介绍这么多,所以小G会分以下几块:

  1. 本篇blog会初步介绍angular2的一些简单语法及快速上手。
  2. 下一篇blog会介绍angular form 的用法
  3. 再下一篇blog会介绍angular2里面的dependency injection的主要用法以及用ood的方法来做dom manipulation。
  4. 下下篇最重要会介绍小G最爱的rxjs。
  5. 小G还会写个cheatsheet,关于angular2的主要用法以及rxjs的主要用法。rxjs的主要用法按decision tree的方法展示。

参考资料

我是第一次见有公司能把framework的文档写的这么好的,google真是给了我惊喜。如果大家不想从头开始写,小G我写了个简单app,囊括了google angular2文档里的tutorial。这里上链接:

Angular 2 quick start . 所以这个app的文件结构是这样的:

├──angular2/                                    
│ │
│ ├──app/
│ │ ├──boot.ts
│ │ ├──app.component.ts
│ │ ├──hero/ //这篇博客用
│ │ | |──hero.component.ts
│ │ ├──hero-form/ //form blog 用
│ │ | ├──hero.ts
│ │ | |──hero-form.component.ts
│ │ | |──hero-form.component.html
│ │ ├──hero-list/ //dependency injection 用
│ │ | ├──...
│ ├──index.html
│ ├──package.json //用来安装我们需要的库, 以及node的一些命令
│ ├──tsconfig.json //用来配置typescript
│ ├──style.css //这个playground的css

文件构成

package.json

用过前端的都知道,npm拯救了世界。所以我们用angular2的时候肯定也会用上npm来帮我们加各种库。所以对node或者npm熟悉的肯定也知道package.json。 在package.json里面,你可以定义script:

"scripts": {
"tsc": "tsc",
"tsc:w": "tsc -w",
"lite": "lite-server",
"start": "concurrent /"npm run tsc:w/" /"npm run lite/" "
},

在这里你可以设置你一会要用npm跑的命令,以这个为例,如果你跑npm start,那他就会先跑npm run tsc用tsc compile typescript到javascript,然后跑npm run lite跑server来host整个app。

还有dependencies和devdepdencies,你可以在里面加你想要使用的库,这样你就可以用npm install安装你所有想要的库。这里有个小trick,如果你想把这个库装到系统里面,就要跑

npm install -g [test]

如果你只想随便使用一下,就跑:

npm install [test]

如果你不仅想用还想加到packge.json里面,那就要使用

npm install --save [test]

typescript.json

这个文件主要用来配置typescript的compiler,比如用啥target,用啥module,是不是要设置sourcemap,是不是要要设输出路径什么的。小G在这里配置一个输出dist,这样生成的js和map就不会和tsfile合在一起,他们会到一个单独的文件夹,然后index.html会去查找。

index.html

这个是整个app存在的html支持,angular2说白了还是javascript,它是不能脱离html的存在。所以跟普通的javascript一样,需要html来load这个框架。在这个例子中小G就使用了systemjs,这个库能找到angular2 的入口然后把整个app load出来。当然不仅仅可以使用systemjs,还有一些其他的框架比如webpack,可以bundle你的库让你的库不再成为一团乱麻。不过代价是他会有个很奇怪的build 方式,他有个server来查看你的code的改变然后在memory里面compile typescript然后把编译好的javascript放到memory里面然后用webpack dev server host然后让index.html直接找,这样的坏处是在debug的时候sourcemap很难找。虽然chrome debugger可以很容易解决这个问题。但是其他编译器比如vscode的chrome debugger extension就没有那么智能了。小G曾经尝试用vscode debug angular webpack starter,过程非常痛苦。

boot

Angular2 对于angular1一个最大的进步是所有的东西都模块化了,web component好像变成了现在web technology的一个趋势。比如google正在推崇的polymer就完全使用了web component的思想,他给你很多个component,就像搭积木一样把你要的component下载下来,然后搭起来搭成你想要的一个web app。小G觉得这个想法是很不错,不过小G试了下polymer反应还是有点慢,虽然那些component做的很好看很material design,但是用户体验太慢的话还是不行吧。但是我相信polymer一直在进步,总有一天可以很好的使用它。这个boot等于整个app的入口,在boot.ts里面加入了这个app的root component,然后在index里面用systemjs调用这个boot,就把这个app整个load起来了。具体如下:

index.html
|
|systemjs调用boot
boot
|boot调用模块化app的root component
|
app

而boot在调用模块化的component root的时候使用了bootstrap()的函数,我们可以importanglar2/core来得到bootstrap()这个函数。看到这里大家可能要想为啥不直接用index load app 为啥要通过一个boot呢?确实如果只想做web app,可以直接用systemjs来load app。有boot的原因是因为我们可能不仅仅想做web application,我们还想做native app, 还想做cordova或者ionic的app,这样只需要替换boot其他所有的代码都可以复用,这是angular2的一个很大的改善。angular1里面并没有很好的支持这一个功能。

app

正如小G刚才所说,angular2把所有的组建都模块化了,在angular2里面,你的整个app可以变成很多个模块,这些模块可以定义输入输出,也可以继承,可以拼接,这些模块就是不同的component,component之间的通信,数据交换就用service。在小G写的例子里,可以看到就加入了三个模块:hero,hero-form,heros-list。

@Component({
selector: 'my-app',
template:`
< hero>< /hero>
< hero-form>< /hero-form>
< heroes-list>< /heroes-list>
`,
directives:[HeroComponent, HeroFormComponent,HeroesListComponent]
})

首先@Component是angular2里面新引进的函数,通过这个函数我们可以设置selector,template,directives,styles等等。当然如果你要使用Component这个函数就要引入库:

import {Component} from 'angular2/core';

当然还可以引入子模块,用相对路径即可:

import {HeroComponent} from './hero/hero.component';
import {HeroFormComponent} from './hero-form/hero-form.component';
import {HeroesListComponent} from './hero-list/heroes-list.component';

selector

主要用来定义外部的模块如何使用这个模块。比如在app.component中selector是my-app,那在外部的index.html里面要load app.component这个模块就要使用

template

主要用来定义这个模块里面的html结构,他也可以加入内部的模块,比如这个app.component就加入了三个不同的子模块。这样写html是不是好像就像搭积木一样简单了?有一点要注意的是当template引用html的时候需要使用‘`’符号,一般这个符号在键盘上数字键1的左边。

在template里还有一个很重要的概念,用过angular1的童鞋都知道怎么在html里面引用controller里面的变量,没错,用{ { } },在angular2里面也一样。 我们在class里面定义了一个变量,在template里面就可以用{ { } }直接引用。例如:

@Component({
selector:'hero',
template: `
< div >{{title}}< /div >
`
})
export class HeroComponent {
public title = 'Tour of Heroes';
}

这样可以在template直接引用class里面的title,等于template里面是< div>Tour of Heroes< /div>

然后你就可以随意改这个变量,然后render出来的html也会跟着改变。另外我在template里面想提到的是@Input @Output factory method以及[(ngModel)]的双向绑定。

@Input

这个@Input是用来定义模块的输入的,用来让父模块往子模块传递内容:

@Component({
selector: 'bank-account',
template: `
Bank Name: {{bankName}}
Account Id: {{id}}
`

})
class BankAccount {
@Input() bankName: string;
@Input('account-id') id: string;
// this property is not bound, and won't be automatically updated by Angular
normalizedBankName: string;
}
@Component({
selector: 'app',
template: `
< bank-account bank-name="RBC" account-id="4747">< bank-account>
`,

directives: [BankAccount]
})
class App {}

再这个例子中,父模块是app,子模块是BankAccount, 在app中我们想往子模块里面传递back-name和account-id这两个信息,可以通过property的方式最简单直接,而子模块要接受这个property就要用@Input来接收。子模块会在BankAccount里面直接接收传递过来的property。传递的方式可以是驼峰法,也可以直接写在input里面,就如例子里面写的那样。要记住一点的是父模块在引用子模块的时候是用的[]。

@Output

如果我们的子模块想自定义一些event传递给父模块又该怎么做呢?这时候就要用到@Output了。

@Directive({
selector: 'interval-dir',
})
class IntervalDir {
@Output() everySecond = new EventEmitter();
@Output('everyFiveSeconds') five5Secs = new EventEmitter();
constructor() {
setInterval(() => this.everySecond.emit("event"), 1000);
setInterval(() => this.five5Secs.emit("event"), 5000);
}
}
@Component({
selector: 'app',
template: `
< interval-dir (every-second)="everySecond()" (every-five-seconds)="everyFiveSeconds()">
< /interval-dir>
`,

directives: [IntervalDir]
})
class App {
everySecond() { console.log('second'); }
everyFiveSeconds() { console.log('five seconds'); }
}

因为在普通的html里面,比如button我们会有onclick这种event来监听这个button是否被按了,然后angular2也完全允许我们自定义这种event listening的模式,我们可以给任何模块定义这种event,当触发了event之后就会从子模块往父模块传递子模块的信息。

再这个例子里,父模块是app,子模块是IntervalDir, 然后在子模块定义event触发的条件,比如每隔1s要触发事件,每隔5s要触发事件。然后当父模块监听到这些事件时可以做相应的操作。当然我们如果想传递信息,可以在new EventEmitter()里面加入我们想要传递的东西,然后在使用的时候加入函数的参数里面。比如我们想传递一个

test,我们只需要这么改:

class IntervalDir {
@Output() everySecond = new EventEmitter(test);
}
< interval-dir (every-second)="everySecond(test)" (every-five-seconds)="everyFiveSeconds()">
everySecond(test) { console.log(test); }

而在这里,父模块在引用子模块的时候是用的()。

[(ngModel)]

最后当然是最经典的angular特有的ngModel双向binding了。而这个特性是< input >特有的。我们注意到了 [(ngModel)] 既有[]也有()。没错它既有Input的特性也有Output的特性。

< input [(ngModel)]="hero.name" placeholder="name" >

在这里,我们在view里面改input里面的内容,hero.name也会跟着改变,而如果我们在class里面改变hero.name在view里面的内容也会跟着改变。然后就是mvvm的世界了。

directives

主要是加入子模块的模块,当我们定义了一个模块之后,如果我们想要它被别的模块使用,必须定义一个出口被别的模块使用,比如app.component这个模块最后就要:

export class AppComponent {}

让angular2知道它是一个AppComponent模块,然后在boot里面可以加入这个模块。同理我也export了HeroComponent,HeroFormComponent以及HeroesListComponent模块。然后在app.component的一开始就import进来,然后就要在directive加入这些模块然后在template才能识别相应的selector。如果不在directive中引入相应的模块,那在compile的时候template就不知道相应的selector是啥就不能正常的render了。

providers

这个主要用来加入service的模块。当我们想在模块之中使用service的时候,和directive相同的道理,我们需要加入provider这样这个模块就知道使用的service的provider。小G在用angular的过程中碰到最多的问题就是missing provider的问题,在angular1中可能是由于service的dependency之间有loop,表示service互相依赖,会有问题。但angular2由于模块化了这个dependency injection的问题得到了很好的解决。如果再碰到这类问题首先要检查模块是否加入了service provider。

styles

这个主要定义这个模块使用的css,不用放到外部的style.css里面。这样这个模块的css完全由自己掌控,是不是方便了很多?

这整个app的root component介绍完了,这个root component主要功能是加载了其他模块。这个博客还会介绍一个hero component。

hero component

这块主要介绍的就是template里面的input和output还有ngFor:

< li *ngFor="#hero of heroes" 
[class.selected]="hero === selectedHero"
(click)="onSelect(hero)">

< span class="badge">{{hero.id}}< /span> {{hero.name}}
< /li >

比如我们有一个list的heros,我们可以在html里面用ngFor来循环这个list然后把list里面的英雄输出到html里面。在这里的语法和angular1完全不一样了。在angular1里面,完全没有 以及#的符号,但是在angular2里面, 和#确实很关键的语法。

( )这个在ngFor的前缀表示整个< li >元素以及它的子元素合成一个master模板。如果不想使用( ),ngFor需要被包含在一个template的元素里面:

< template ngFor #hero [ngForOf]="heroes" >
< hero-detail [hero]="hero" >< /hero-detail >
< /template >

Angular2还有这个语法的就是 ngIf。如果不用( )也需要把ngIf包含在template里面。

而在hero前面的( # )表示了hero是一个本地模板变量,整个合起来就是循环heros的list,然后对于每个hero,在< li >< /li >内可以进行操作。

从上面的例子还可以看到input和output。只不过这两个都已经在li模块里面集成好了,不需要自己定义。所以对于li这个元素的input,我们可以定义这个li的class的是否被选择。对于li元素的output我们可以定义点击事件监听。然后在这个模块中进行相应的函数操作。

小结

洋洋洒洒这么多,其实angular2的主要知识点有下面几个:

  1. Angular2的文件结构,typescript的设置,package.json的设置。
  2. boot的作用,更换boot可以达到复用代码让代码工作在其他framework的作用。
  3. Angular2是模块化的,自上而下的一个树状结构。每个模块之间可以互相调用,也可以用service互相通信。
  4. Angular2里面的一些概念,比如Component,Directive,Provider,selector,template, @Input, @Output, [ ( ngModel) ], ngFor, ngIf.

如果你知道上面这些是咋回事,那差不多可以开始自己写个angular2的app啦。一方面可以在小G的简单app上随便操作看效果,另一方面小G也给个angular2的快速开发app的project: angular2-webpack-starter 。小G就是用这个来做比较大型的app开发的。这个项目是跟着angular2不断在更新的,比较伤感的是用vscode很难debug。

正文到此结束
Loading...