[关闭]
@adonia 2016-09-21T15:00:17.000000Z 字数 6612 阅读 214

angular2(2): 第一个应用

angular2


说明: 完整的示例应用,参考github

  1. 创建工程目录

    首先,创建一个空目录,例如reddit,作为工程的根目录。

  2. 增加Node.js工程描述文件

    应用基于Node.js开发,需要在工程的根目录创建工程的描述文件---package.json。可以通过npm init命令初始化工程,根据交互式提示填写相关信息,即可完成初始的package.json。但为了方便,可以直接将下面的内容拷贝至package.json中并保存。

    1. {
    2. "name": "reddit",
    3. "version": "1.0.0",
    4. "private": true,
    5. "scripts": {
    6. "tsc": "./node_modules/.bin/tsc",
    7. "tsc:w": "./node_modules/.bin/tsc -w",
    8. "serve": "./node_modules/.bin/live-server --host=localhost --port=8080 .",
    9. "go": "concurrent \"npm run tsc:w\" \"npm run serve\" "
    10. },
    11. "license": "ISC",
    12. "dependencies": {
    13. "angular2": "2.0.0-beta.0",
    14. "systemjs": "0.19.6",
    15. "es6-promise": "^3.0.2",
    16. "es6-shim": "^0.33.3",
    17. "reflect-metadata": "0.1.2",
    18. "rxjs": "5.0.0-beta.0",
    19. "zone.js": "0.5.10"
    20. },
    21. "devDependencies": {
    22. "concurrently": "^1.0.0",
    23. "live-server": "^0.9.0",
    24. "typescript": "1.7.5"
    25. }
    26. }

    说明:

    • 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编译时就会报错。

  3. 下载依赖

    在命令行中,进入工程根目录,执行 cnpm install 命令,下载工程依赖。

    命令中可能会有warning信息,提示es6-module-loader已被废弃。这个警告信息不会影响程序的正常运行,可以暂时不关注,等后续对angular2的各个依赖模块了解清楚之后,再做处理。

    依赖下载完成之后,会在工程的根目录下多一个node_modules目录,即为上面介绍的依赖放置目录,可以查看package.json中指定的./node_modules/.bin/tsc运行文件是否存在。

  4. 添加网页文件

    在工程的根目录添加index.html文件,内容如下:

    1. <!DOCTYPE html>
    2. <html lang="en">
    3. <head>
    4. <meta charset="UTF-8">
    5. <title>Reddit</title>
    6. <script src="node_modules/es6-shim/es6-sham.js"></script>
    7. <script src="node_modules/angular2/bundles/angular2-polyfills.js"></script>
    8. <script src="node_modules/systemjs/dist/system.src.js"></script>
    9. <script src="node_modules/rxjs/bundles/Rx.js"></script>
    10. <script src="node_modules/angular2/bundles/angular2.dev.js"></script>
    11. </head>
    12. <body>
    13. </body>
    14. </html>

    Note:

    • es6-shim是为旧版的javascript引擎提供ES6的行为,在最新版本的chrome等浏览器中是不需要的。

    • angular2-polyfills提供了angular2的行为能力,例如angular1中的数据绑定,监听属性变化;还有就是typescript中的注解,下面就会接触到。

    • systemjs是模块加载器,帮助解决创建模块和依赖模块的复杂度。

    • rxjs则是在javascript中提供反应式编程的能力,例如HTTP中的异步处理请求。

  5. 添加typescript

    在工程根目录下创建typescript文件---app.ts,内容如下:

    1. import {bootstrap} from "angular2/platform/browser";
    2. import {Component} from "angular2/core";
    3. @Component({
    4. selector: 'reddit',
    5. template: `
    6. <div>Hello world</div>
    7. `
    8. })
    9. class Reddit {
    10. }
    11. bootstrap(Reddit);

    简单说明下typescript的属性,后续会详细介绍:

    • import指明了需要在代码中引用的外部组件,例如这里的bootstrapComponent;以及如何去查找这些组件。可以查看 node_modules/angular2/core/core.d.ts,其中定义了该模块export的组件。

    • @Component即为typescript中的注解特性,用于创建一个组件。

    • class为typescript中的类定义,constructor()为构造方法。

  6. 创建一个组件

    angular2中的组件,类似于angular1中的指令;等同于html中常见的、等标签。

    那么,如何在HTML中使用自定义的标签呢,比如,我们想要在HTML中直接使用时,能够展示出Hello reddit信息,该如何做呢?

    组件的定义包括两个部分:

    • 使用@Component注解

    • 定义一个组件的类

    现在,先简化下上述的app.ts中的内容。

    • 首先,创建一个Reddit类,用于抽象描述我们需要展示的信息。如下:
    1. class Reddit {
    2. }
    • 其次,在类Reddit上,使用@Component修饰。如下:
    1. @Component
    2. class Reddit {
    3. }

    现在,我们就把Reddit类装饰成为了一个组件。

    按照之前的描述,我们想要在HTML中使用闭合标签<reddit></reddit>时,能够展示出来点信息。那么,就需要在我们定义的组件中,指明对应的选择器,这里的selector的概念,可以类比JQuery。如下:

    1. @Component({
    2. selector: 'reddit'
    3. })
    4. class Reddit {
    5. }

    这样,标签<reddit>和组件Reddit就关联起来了,每次在DOM中使用<reddit>标签,都会被编译成一个Reddit组件。现在就剩下信息的展示了。

    • 再次,定义一个模板(template),模板内容的定义,会使用到ES6中的反引号,包含在其中的内容,会保留原有的格式,包括空格和换行,还可以进行变量替换。如下:
    1. @Component({
    2. selector: 'reddit',
    3. template: `
    4. <div>Hello world</div>
    5. `
    6. })
    7. class Reddit {
    8. }
    • 最后,使用bootstrap(Reddit)来引导定义的组件类。
  7. 使用定义的模板

    按照我们之前的预期,定义好了组件,那么,就可以在DOM中使用<reddit>标签了。在之前创建的index.html的body中增加如下内容:

    1. <body>
    2. <script>
    3. System.config({
    4. packages: {
    5. app: {
    6. format: 'register',
    7. defaultExtension: 'js'
    8. }
    9. }
    10. });
    11. System.import('app.js').then(null, console.error.bind(console));
    12. </script>
    13. <reddit></reddit>
    14. </body>

    Note:

    • <script>标签中,使用了Systemjs来加载模块,最重要的是System.import('app.js'),告诉Systemjs加载app.js作为程序的入口。

    你可以会疑惑了,我们程序中并没有app.js呀!还记得之前说过的,typescript无法直接被浏览器解析,需要转换成javascript;以及介绍的tsc工具吗?

    在运行应用之前,先来介绍下,如何编译应用。

  8. 编译应用

    之前已经介绍了,typescript的编译,要使用tsc工具。再来看下package.json中的定义:

    1. {
    2. "name": "reddit",
    3. "version": "1.0.0",
    4. "private": true,
    5. "scripts": {
    6. "tsc": "./node_modules/.bin/tsc",
    7. "tsc:w": "./node_modules/.bin/tsc -w",
    8. "serve": "./node_modules/.bin/live-server --host=localhost --port=8080 .",
    9. "go": "concurrent \"npm run tsc:w\" \"npm run serve\" "
    10. },
    11. "license": "ISC",
    12. "dependencies": {
    13. "angular2": "2.0.0-beta.0",
    14. "systemjs": "0.19.6",
    15. "es6-promise": "^3.0.2",
    16. "es6-shim": "^0.33.3",
    17. "reflect-metadata": "0.1.2",
    18. "rxjs": "5.0.0-beta.0",
    19. "zone.js": "0.5.10"
    20. },
    21. "devDependencies": {
    22. "concurrently": "^1.0.0",
    23. "live-server": "^0.9.0",
    24. "typescript": "1.7.5"
    25. }
    26. }

    其中,scripts定义了两个关于编译的指令---tsctsc:w。其中tsc是编译typescript;tsc:w则在编译typescript的基础上,还会监控typescript文件,当文件发生变化时,会重新编译。

    在编译之前,先再工程根目录增加文件---tsconfig.json,内容如下:

    1. {
    2. "compilerOptions": {
    3. "target": "ES5",
    4. "module": "system",
    5. "moduleResolution": "node",
    6. "sourceMap": true,
    7. "emitDecoratorMetadata": true,
    8. "experimentalDecorators": true,
    9. "removeComments": false,
    10. "noImplicitAny": false
    11. },
    12. "exclude": [
    13. "node_modules"
    14. ]
    15. }

    其中定义了typescript编译选项。

    在工程的根目录运行 npm run tsc 命令,如果命令执行成功,在工程的根目录下,会看到多出两个文件---app.js以及app.js.map。此处的app.js则是之前引入到Systemjs中的。

  9. 运行应用

    应用的运行,需要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,如下:

  1. class Reddit {
  2. name: string;
  3. constructor() {
  4. this.name = 'reddit';
  5. }
  6. }

这里,定义了一个字段---name,并声明其类型为stringconstructor()方法为类的构造方法,以为着每次实例化Reddit类时,都会调用该方法。在该方法中,通过this.namename字段的值设置为reddit

这样,我们就可以在@Component注解的模板中,通过name属性来渲染了。如下:

  1. @Component({
  2. selector: 'reddit',
  3. template: `
  4. <div>Hello {{name}}</div>
  5. `
  6. })
  7. class Reddit {
  8. name: string;
  9. constructor() {
  10. this.name = 'reddit';
  11. }
  12. }

在模板中,使用了{{...}}(模板标识)将属性括起来。被模板标识包含的任何内容都会被扩展为表达式。在DOM中加载<reddit>标签时,就会初始化Reddit组件,将实例化的Reddit类中的name属性传递给组件模板。从而,模板中的name被替换成reddit

那如果要在模板中处理一个数组,该怎么做呢?

首先,跟上面类似,在类中新增一个属性---names,不过呢,类型声明为string[]或者Array[string]

为了体现两者的差异,我们打算重新创建一个组件---Reddit02

类Reddit02的定义如下:

  1. class Reddit02 {
  2. names: string[];
  3. constructor() {
  4. this.names = ['Ari', 'Carlos', 'Felipe', 'Nate'];
  5. }
  6. }

@Component注解的模板中,需要对数组进行遍历,进而渲染。在angular1中,对数组遍历,是使用的ng-repeat指令;在angular2中,需要引入NgFor组件。

首先,将NgFor引入到应用中。如下:

  1. import {NgFor} from "angular2/common";
  2. @Component({
  3. selector: 'reddit02',
  4. template: `
  5. <ul>
  6. <li *ngFor="#name of names">{{name}}</li>
  7. </ul>
  8. `
  9. })
  10. class Reddit02 {
  11. names: string[];
  12. constructor() {
  13. this.names = ['Ari', 'Carlos', 'Felipe', 'Nate'];
  14. }
  15. }
  16. bootstrap(Reddit02);

如上,在@Component中重新定义了个选择器---reddit02。需要注意下模板中对NgFor模块的使用。

  1. <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

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注