@adonia
2016-09-21T15:00:17.000000Z
字数 6612
阅读 214
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
修饰。如下:
@Component
class 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。