@adonia
2016-09-21T15:00:17.000000Z
字数 6612
阅读 259
angular2
说明: 完整的示例应用,参考github。
创建工程目录
首先,创建一个空目录,例如reddit,作为工程的根目录。
增加Node.js工程描述文件
应用基于Node.js开发,需要在工程的根目录创建工程的描述文件---package.json。可以通过npm init命令初始化工程,根据交互式提示填写相关信息,即可完成初始的package.json。但为了方便,可以直接将下面的内容拷贝至package.json中并保存。
{"name": "reddit","version": "1.0.0","private": true,"scripts": {"tsc": "./node_modules/.bin/tsc","tsc:w": "./node_modules/.bin/tsc -w","serve": "./node_modules/.bin/live-server --host=localhost --port=8080 .","go": "concurrent \"npm run tsc:w\" \"npm run serve\" "},"license": "ISC","dependencies": {"angular2": "2.0.0-beta.0","systemjs": "0.19.6","es6-promise": "^3.0.2","es6-shim": "^0.33.3","reflect-metadata": "0.1.2","rxjs": "5.0.0-beta.0","zone.js": "0.5.10"},"devDependencies": {"concurrently": "^1.0.0","live-server": "^0.9.0","typescript": "1.7.5"}}
说明:
name为工程名,version为版本号,可以修改为适合的字段,方便区分。
scripts中定义了工程的指令,在工程目录下运行npm run [指令名]即可运行指定的命令。例如,运行npm run tsc,则会运行./node_modules/.bin/tsc,作用呢,则是会编译工程目录下的typescript为javascript。此处的node_modules是Node.js工程的依赖放置目录,目前还不存在,暂时先不管,一会再介绍。
dependencies中定义了工程的运行时依赖,devDependencies则定义了工程的开发态依赖。依赖的定义均是[模块名]: [版本号]的形式。Node.js中的依赖是通过
npm工具管理的,在这里,建议使用环境搭建介绍的cnpm。需要了解相关的依赖包,可以前往淘宝的镜像库中搜索查看。angular2的版本,跟各个依赖之间可能会存在不兼容的问题,所以,尽管上述中的依赖的版本并不是最新的,建议还是直接参照。例如,1.8.0版本的typescript和2.0.0-beta.8版本的angular2配合,执行tsc编译时就会报错。
下载依赖
在命令行中,进入工程根目录,执行 cnpm install 命令,下载工程依赖。
命令中可能会有warning信息,提示
es6-module-loader已被废弃。这个警告信息不会影响程序的正常运行,可以暂时不关注,等后续对angular2的各个依赖模块了解清楚之后,再做处理。
依赖下载完成之后,会在工程的根目录下多一个node_modules目录,即为上面介绍的依赖放置目录,可以查看package.json中指定的./node_modules/.bin/tsc运行文件是否存在。
添加网页文件
在工程的根目录添加index.html文件,内容如下:
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>Reddit</title><script src="node_modules/es6-shim/es6-sham.js"></script><script src="node_modules/angular2/bundles/angular2-polyfills.js"></script><script src="node_modules/systemjs/dist/system.src.js"></script><script src="node_modules/rxjs/bundles/Rx.js"></script><script src="node_modules/angular2/bundles/angular2.dev.js"></script></head><body></body></html>
Note:
es6-shim是为旧版的javascript引擎提供ES6的行为,在最新版本的chrome等浏览器中是不需要的。
angular2-polyfills提供了angular2的行为能力,例如angular1中的数据绑定,监听属性变化;还有就是typescript中的注解,下面就会接触到。
systemjs是模块加载器,帮助解决创建模块和依赖模块的复杂度。
rxjs则是在javascript中提供反应式编程的能力,例如HTTP中的异步处理请求。
添加typescript
在工程根目录下创建typescript文件---app.ts,内容如下:
import {bootstrap} from "angular2/platform/browser";import {Component} from "angular2/core";@Component({selector: 'reddit',template: `<div>Hello world</div>`})class Reddit {}bootstrap(Reddit);
简单说明下typescript的属性,后续会详细介绍:
import指明了需要在代码中引用的外部组件,例如这里的bootstrap和Component;以及如何去查找这些组件。可以查看node_modules/angular2/core/core.d.ts,其中定义了该模块export的组件。
@Component即为typescript中的注解特性,用于创建一个组件。
class为typescript中的类定义,constructor()为构造方法。
创建一个组件
angular2中的组件,类似于angular1中的指令;等同于html中常见的、等标签。
那么,如何在HTML中使用自定义的标签呢,比如,我们想要在HTML中直接使用时,能够展示出Hello reddit信息,该如何做呢?
组件的定义包括两个部分:
使用@Component注解
定义一个组件的类
现在,先简化下上述的app.ts中的内容。
class Reddit {}
@Component修饰。如下:
@Componentclass Reddit {}
现在,我们就把Reddit类装饰成为了一个组件。
按照之前的描述,我们想要在HTML中使用闭合标签<reddit></reddit>时,能够展示出来点信息。那么,就需要在我们定义的组件中,指明对应的选择器,这里的selector的概念,可以类比JQuery。如下:
@Component({selector: 'reddit'})class Reddit {}
这样,标签<reddit>和组件Reddit就关联起来了,每次在DOM中使用<reddit>标签,都会被编译成一个Reddit组件。现在就剩下信息的展示了。
template),模板内容的定义,会使用到ES6中的反引号,包含在其中的内容,会保留原有的格式,包括空格和换行,还可以进行变量替换。如下:
@Component({selector: 'reddit',template: `<div>Hello world</div>`})class Reddit {}
bootstrap(Reddit)来引导定义的组件类。使用定义的模板
按照我们之前的预期,定义好了组件,那么,就可以在DOM中使用<reddit>标签了。在之前创建的index.html的body中增加如下内容:
<body><script>System.config({packages: {app: {format: 'register',defaultExtension: 'js'}}});System.import('app.js').then(null, console.error.bind(console));</script><reddit></reddit></body>
Note:
<script>标签中,使用了Systemjs来加载模块,最重要的是System.import('app.js'),告诉Systemjs加载app.js作为程序的入口。
你可以会疑惑了,我们程序中并没有app.js呀!还记得之前说过的,typescript无法直接被浏览器解析,需要转换成javascript;以及介绍的tsc工具吗?
在运行应用之前,先来介绍下,如何编译应用。
编译应用
之前已经介绍了,typescript的编译,要使用tsc工具。再来看下package.json中的定义:
{"name": "reddit","version": "1.0.0","private": true,"scripts": {"tsc": "./node_modules/.bin/tsc","tsc:w": "./node_modules/.bin/tsc -w","serve": "./node_modules/.bin/live-server --host=localhost --port=8080 .","go": "concurrent \"npm run tsc:w\" \"npm run serve\" "},"license": "ISC","dependencies": {"angular2": "2.0.0-beta.0","systemjs": "0.19.6","es6-promise": "^3.0.2","es6-shim": "^0.33.3","reflect-metadata": "0.1.2","rxjs": "5.0.0-beta.0","zone.js": "0.5.10"},"devDependencies": {"concurrently": "^1.0.0","live-server": "^0.9.0","typescript": "1.7.5"}}
其中,scripts定义了两个关于编译的指令---tsc和tsc:w。其中tsc是编译typescript;tsc:w则在编译typescript的基础上,还会监控typescript文件,当文件发生变化时,会重新编译。
在编译之前,先再工程根目录增加文件---tsconfig.json,内容如下:
{"compilerOptions": {"target": "ES5","module": "system","moduleResolution": "node","sourceMap": true,"emitDecoratorMetadata": true,"experimentalDecorators": true,"removeComments": false,"noImplicitAny": false},"exclude": ["node_modules"]}
其中定义了typescript编译选项。
在工程的根目录运行 npm run tsc 命令,如果命令执行成功,在工程的根目录下,会看到多出两个文件---app.js以及app.js.map。此处的app.js则是之前引入到Systemjs中的。
运行应用
应用的运行,需要webserver,此处使用的是live-server,即devDependencies定义的依赖。
在工程根目录下,运行 npm run serve 命令,则会启动 live-server,端口为package.json中定义的8080。在浏览器中,访问localhost:8080,应该就可以看到Hello reddit信息了。
另外,可以使用 npm run go 指令,保持工程的持续运行,在修改工程中的文件时,能够实时动态加载,无需重新发布。
Tips:
运行
live-server之前,一定要保证app.js存在。如果页面展现不出来,可以在浏览器端,使用
F12打开开发者工具,查看错误。
下面对Reddit应用做下扩展。
在上述定义的组件中,template中是固定的字符。那如何为组件设置一个可变的属性呢?
我们在Reddit类中,新增一个属性字段---name,如下:
class Reddit {name: string;constructor() {this.name = 'reddit';}}
这里,定义了一个字段---name,并声明其类型为string。constructor()方法为类的构造方法,以为着每次实例化Reddit类时,都会调用该方法。在该方法中,通过this.name将name字段的值设置为reddit。
这样,我们就可以在@Component注解的模板中,通过name属性来渲染了。如下:
@Component({selector: 'reddit',template: `<div>Hello {{name}}</div>`})class Reddit {name: string;constructor() {this.name = 'reddit';}}
在模板中,使用了{{...}}(模板标识)将属性括起来。被模板标识包含的任何内容都会被扩展为表达式。在DOM中加载<reddit>标签时,就会初始化Reddit组件,将实例化的Reddit类中的name属性传递给组件模板。从而,模板中的name被替换成reddit。
那如果要在模板中处理一个数组,该怎么做呢?
首先,跟上面类似,在类中新增一个属性---names,不过呢,类型声明为string[]或者Array[string]。
为了体现两者的差异,我们打算重新创建一个组件---Reddit02
类Reddit02的定义如下:
class Reddit02 {names: string[];constructor() {this.names = ['Ari', 'Carlos', 'Felipe', 'Nate'];}}
在@Component注解的模板中,需要对数组进行遍历,进而渲染。在angular1中,对数组遍历,是使用的ng-repeat指令;在angular2中,需要引入NgFor组件。
首先,将NgFor引入到应用中。如下:
import {NgFor} from "angular2/common";@Component({selector: 'reddit02',template: `<ul><li *ngFor="#name of names">{{name}}</li></ul>`})class Reddit02 {names: string[];constructor() {this.names = ['Ari', 'Carlos', 'Felipe', 'Nate'];}}bootstrap(Reddit02);
如上,在@Component中重新定义了个选择器---reddit02。需要注意下模板中对NgFor模块的使用。
<li *ngFor="#name of names">{{name}}</li>
*ngFor类似于ng-repeat,注意前面的*等号右边的内容,类似于typescript中的
for...of语法结构,只不过这里定义的遍历元素name,不是用let声明的,而是用#。语句中的
names对应于类中的names属性,两者必须要一致。遍历元素name可以随意定义,只要跟{{...}}中的保持一致就可以了。
然后,引导Reddit02类,并在index.html的body中添加<reddit02></reddit02>标签。
具体代码,见github。