快速起步 (JavaScript)

这个快速起步向导向你演示了如何创建并运行一个简单的Angular应用。

让我们从零开始,创建一个超级简单的用JavaScript的Angular应用。

看它运行

运行该示例是查看一个Angular应用变成现实的最快捷的方式。

点击链接触发一个浏览器,载入plunker中的案例,显示一条简单的消息:

Output of quickstart app

这上文件结构:

angular-quickstart
├ node_modules
├ app
│ ├app.component.js
│ ├app.module.js
│ └main.js
├ index.html
├ package.json
└ styles.css

从功能上说,它是一个index.htmlstyles.css和三个在app/文件夹中的JavaScript文件。我们可以处理它!

当然,如果只运行在plunker中,我们不能创建很多应用。让我们跟随着一个接近于现实生活中所用的处理过程。

  1. 在我们的开发环境中设置
  2. 为我们的应用编写Angular根组件
  3. 添加一个Angular模块
  4. 给它添加Bootstrap,以控制主页
  5. 编写主页
  6. 添加一些CSS (styles.css)

如果我们跟随着这些指令,忽略掉注释,我们确实可以从草图开始,用5分钟创建快速起步。

我们的大多数人将对“Why”和“How”感兴趣,但这将很耗时。

开发环境

我们需要立足的地方(存放应用项目文件夹),一些库和你选择的编辑。

创建一个新项目文件夹

mkdir angular-quickstart
cd    angular-quickstart

添加我们需要的库

我们建议使用npm包管理器,用来取得并管理我们的开发库。

还没有用上npm吗?立马走起!因为在本文档中,我们将反复地使用它。

向项目文件夹添加一个package.json,复制、粘贴下面的内容:

package.json

{
  "name": "angular-quickstart",
  "version": "1.0.0",
  "scripts": {
    "start": "npm run lite",
    "lite": "lite-server"
  },
  "licenses": [
    {
      "type": "MIT",
      "url": "https://github.com/angular/angular.io/blob/master/LICENSE"
    }
  ],
  "dependencies": {
    "@angular/common": "~2.1.0",
    "@angular/compiler": "~2.1.0",
    "@angular/core": "~2.1.0",
    "@angular/forms": "~2.1.0",
    "@angular/http": "~2.1.0",
    "@angular/platform-browser": "~2.1.0",
    "@angular/platform-browser-dynamic": "~2.1.0",
    "@angular/router": "~3.1.0",
    "@angular/upgrade": "~2.1.0",

    "angular-in-memory-web-api": "~0.1.5",
    "bootstrap": "^3.3.7",
    "core-js": "^2.4.1",
    "reflect-metadata": "^0.1.8",
    "rxjs": "5.0.0-beta.12",
    "zone.js": "^0.6.25"
  },
  "devDependencies": {
    "concurrently": "^3.0.0",
    "lite-server": "^2.2.2"
  }
}

渴望了解更多细节?我们在下面附录中作解释。

通过打开一个终端窗口(Windows中的命令窗口)并运行这个npm命令来安装这些包。

npm install

在安装过程中,可能会出现红色的糟糕消息。请忽略它们。安装会成功。参见下面附录以了解更多信息。

我们的第一个Angular组件

组件是一个十分基础的Angular概念。一个组件管理一个视图——一块Web网页,在那里向用户显示信息,响应用户的反馈。

从技术上来说,一个组件是一个类,控制了一个视图的模板。在我们创建Angular应用过程中将详细写它。这是我们第一次尝试,所以我们要保持它的简洁。

创建一个应用源子文件夹

我们想要在子文件夹中保存我们的应用代码,而不是保存在称为app/的根目录下。在控制台窗口中执行下面的命令:

mkdir app
cd    app

添加组件文件

现在,添加一个命名为app.component.js的文件,并粘贴以下代码行:

app/app.component.js

(function(app) {
  app.AppComponent =
    ng.core.Component({
      selector: 'my-app',
      template: '<h1>My First Angular App</h1>'
    })
    .Class({
      constructor: function() {}
    });
})(window.app || (window.app = {}));

通过连缀ComponentClass方法,我们创建了一个命名为AppComponent的视觉组件,这两种方法从属于全局Angular核心命名空间ng.core

app/app.component.js (component schema)

  app.AppComponent =
    ng.core.Component({
    })
    .Class({
    });

Component 方法取用了一个带有三个属性的配置对象。我们在Class方法中实现了组件本身,给它创建了视图的属性和方法,指定哪种行为适合于这部分UI。

让我们详细回顾这件文件

模块

Angular应用都是模块。它由很多文件构成,每个致力于各自的目的。

ES5 JavaScript并没有原生的模块系统。但是我们可以用一些流行的第三方模块系统。另外,为了简化和避免挑选最爱,我们将为我们的应用创建一个简单的全局命名空间。

我们将称它为app,我们将把我们所的代码手工添加到这个全局对象中。

我们并不想用别的东西污染全局命名空间。所以,在每个文件内部,我们用IIFE(“即时调用的函数表达式”)包围了代码。

app/app.component.js (IIFE)

(function(app) {
})(window.app || (window.app = {}));

我们把全局app命名空间对象传递给IIFE,用一个空对象小心地初始化它,如果该对象尚不存在的话。

很多应用文件利用把某物添加到app导出这样东西。我们的app.component.js文件导出了AppComponent

app/app.component.js (export)

app.AppComponent =

一个更复杂的应用会具有子组件,在一个视觉树上继承自AppComponent。一个更复杂的应用将具有更多的文件和模块,至少它有多个组件。

快速起步并不复杂;我们只需要一个组件。虽然在这个小应用中,模块扮演了基础的组织角色

模块依赖于其它模块。在JavaScript Angular应用中,如果我们需要一些由别的模块提供的东西,我们可以从app对象中取得它。如果别的模块需要引用AppComponent,就从app.AppComponent中取得它,如下所示:

app/app.module.js (import)

declarations: [ app.AppComponent ],

Angular本身也是模块化的。它是很多库模块的一个集合。每个库本身是一个由很多相关功能的模块构成的模块。

如果我们需要从Angular中取得一些东西,就使用ng对象。

类定义对象

在文件的底下是一个空的,不作任何事情的类,为我们的AppComponent类定义了对象。如果我们已经准备好创建一个实质的应用,我们可以用属性和应用逻辑来扩展这个对象。我们的AppComponent类构造器内空无一物,因为在快速起步中,我们不需要它做什么事情。

app.component.js (class)

    .Class({
      constructor: function() {}
    });

组件定义对象

ng.core.Component()告诉Angular,这个类定义对象是一个Angular组件。把这个配置对象传递给ng.core.Component()方法,具有两个字段,一个selector和一个template

app.component.js (component)

    ng.core.Component({
      selector: 'my-app',
      template: '<h1>My First Angular App</h1>'
    })

selector为一个被命名为my-app的宿主HTML元素指定了一个简单的CSS选择器。Angular创建并显示了一个AppComponent的实例,位于宿主HTML中出现一个my-app元素的地方。

请记住my-app选择器!当我们编写index.html时,我们将需要那些信息。

template属性控住了组件的伴同模板。一个模板是一个HTML形式,它告诉Angular如何呈现一个视图。我们的模板是一个单行HTML声明"My First Angular App"。

现在,我们需要用一些东西来告诉Angular载入这个组件。

添加一个NgModule

Angular应用是由Angular模块构成的,它看起来包含了我们所有的组件,以及我们在应用中需要的所有东西。

用下面的内容创建app/app.module.js文件:

app/app.module.js

(function(app) {
  app.AppModule =
    ng.core.NgModule({
      imports: [ ng.platformBrowser.BrowserModule ],
      declarations: [ app.AppComponent ],
      bootstrap: [ app.AppComponent ]
    })
    .Class({
      constructor: function() {}
    });
})(window.app || (window.app = {}));

请参阅下面附录中的NgModule配置以了解详情。

加入Bootstrap

app/文件夹添加一个新文件app/,如下所示:

app/main.js

(function(app) {
  document.addEventListener('DOMContentLoaded', function() {
    ng.platformBrowserDynamic
      .platformBrowserDynamic()
      .bootstrapModule(app.AppModule);
  });
})(window.app || (window.app = {}));

我们需要两样东西来启动这个应用:

  1. Angular的platformBrowserDynamic().bootstrapModule函数。
  2. 我们刚刚写的应用根模块。

两者都在'namespaces'内。然后我们调用bootstrapModule,传入root app moduleAppModule

下面附录中可以让你知道为什么我们需要来自ng.platformBrowserDynamicbootstrapModule,以及为什么我们创建了一个分开的js文件。

我们已经要求Angular在浏览器中启动我们在根目录中的组件应用。Angular会把它放在哪里?

添加index.html

Angular会在index.html中的某个位置显示我们的应用。现在该创建那个文件了。

我们不会把我们的index.html放在app/文件夹中。我们会把它放在项目根文件夹内的上一层文件夹中

cd ..

现在创建index.html文件并粘贴入下面的行:

index.html

<html>
  <head>
    <title>Angular QuickStart JS</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="styles.css">

    <!-- 1. Load libraries -->
    <!-- IE required polyfill -->
    <script src="node_modules/core-js/client/shim.min.js"></script>

    <script src="node_modules/zone.js/dist/zone.js"></script>
    <script src="node_modules/reflect-metadata/Reflect.js"></script>

    <script src="node_modules/rxjs/bundles/Rx.js"></script>
    <script src="node_modules/@angular/core/bundles/core.umd.js"></script>
    <script src="node_modules/@angular/common/bundles/common.umd.js"></script>
    <script src="node_modules/@angular/compiler/bundles/compiler.umd.js"></script>
    <script src="node_modules/@angular/platform-browser/bundles/platform-browser.umd.js"></script>
    <script src="node_modules/@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js"></script>

    <!-- 2. Load our 'modules' -->
    <script src='app/app.component.js'></script>
    <script src='app/app.module.js'></script>
    <script src='app/main.js'></script>

  </head>

  <!-- 3. Display the application -->
  <body>
    <my-app>Loading...</my-app>
  </body>

</html>

在HTML中有三个值得注意的部分:

  1. 我们载入了我们需要的JavaScript库;在下面了解它们。
  2. 我们载入了我们的JavaScript文件。
  3. 我们在<body>添加了<my-app>标签。这是我们的应用生存的地方!

如果Angular在main.js中调用了bootstrapModule函数,它读取了AppModule元数据,看到bootstrap组件AppComponent,找到 my-app选择器,定位一个命名为my-app的元素标签,并在那些标签之间呈现我们的应用的视图。

添加一些样式

样式并非是至关重要的,但是有它会比较好,而且index.html假定我们具有一个称为styles.css的样式表。

project root文件夹内创建一个styles.css文件,开始样式化,可能带有最小化的样式,显示如下。欲完整地设置文档案例所用的专家样式,参见styles.css

styles.css

h1 {
  color: #369;
  font-family: Arial, Helvetica, sans-serif;
  font-size: 250%;
}
body {
  margin: 2em;
}

 /*
  * See https://github.com/angular/angular.io/blob/master/public/docs/_examples/styles.css
  * for the full set of master styles used by the documentation samples
  */

Run!

打开一个终端窗口,并输入这条命令:

npm start

这条命令运行了一个称为lite-server的静态服务器,它在浏览器中载入了index.html,当应用文件改变的时候,刷新该浏览器。

在一些时候,一个浏览器选项卡必须打开并显示。

Output of quickstart app

好赞!我们投入实战了。

If you see Loading... displayed instead, see the Browser ES2015 support appendix.

做一些改变

试一试把消息改成“My SECOND Angular APP”。

lite-server是一个观察器,所以它必须侦测变化,刷新浏览器,并显示经修订的消息。

这种开发应用的方式很酷!

如果我们已经搞定终端到服务器了,我们关掉这个终端窗口。

最终的结构

我们最终的项目文件夹结构看起来如下:

angular-quickstart
├ node_modules
├ app
│ ├ app.component.js
│ ├ app.module.js
│ └ main.js
├ index.html
├ package.json
└ style.css

这里是一些文件:

app/app.component.js

(function(app) {
  app.AppComponent =
    ng.core.Component({
      selector: 'my-app',
      template: '<h1>My First Angular App>/h1>'
    })
    .Class({
      constructor: function() {}
    });
})(window.app || (window.app = {}));

app/app.module.js

(function(app) {
  app.AppModule =
    ng.core.NgModule({
      imports: [ ng.platformBrowser.BrowserModule ],
      declarations: [ app.AppComponent ],
      bootstrap: [ app.AppComponent ]
    })
    .Class({
      constructor: function() {}
    });
})(window.app || (window.app = {}));

app/main.js

(function(app) {
  document.addEventListener('DOMContentLoaded', function() {
    ng.platformBrowserDynamic
      .platformBrowserDynamic()
      .bootstrapModule(app.AppModule);
  });
})(window.app || (window.app = {}));

index.html

<html>
  <head>
    <title>Angular QuickStart JS</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="styles.css">
    <!-- 1. Load libraries -->
    <!-- IE required polyfill -->
    <script src="node_modules/core-js/client/shim.min.js"></script>
    <script src="node_modules/zone.js/dist/zone.js"></script>
    <script src="node_modules/reflect-metadata/Reflect.js"></script>
    <script src="node_modules/rxjs/bundles/Rx.js"></script>
    <script src="node_modules/@angular/core/bundles/core.umd.js"></script>
    <script src="node_modules/@angular/common/bundles/common.umd.js"></script>
    <script src="node_modules/@angular/compiler/bundles/compiler.umd.js"></script>
    <script src="node_modules/@angular/platform-browser/bundles/platform-browser.umd.js"></script>
    <script src="node_modules/@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js"></script>
    <!-- 2. Load our 'modules' -->
    <script src='app/app.component.js'></script>
    <script src='app/app.module.js'></script>
    <script src='app/main.js'></script>
  </head>
  <!-- 3. Display the application -->
  <body>
    <my-app>Loading...</my-app>
  </body>
</html>

package.json

{
  "name": "angular-quickstart",
  "version": "1.0.0",
  "scripts": {
    "start": "npm run lite",
    "lite": "lite-server"
  },
  "licenses": [
    {
      "type": "MIT",
      "url": "https://github.com/angular/angular.io/blob/master/LICENSE"
    }
  ],
  "dependencies": {
    "@angular/common": "~2.1.0",
    "@angular/compiler": "~2.1.0",
    "@angular/core": "~2.1.0",
    "@angular/forms": "~2.1.0",
    "@angular/http": "~2.1.0",
    "@angular/platform-browser": "~2.1.0",
    "@angular/platform-browser-dynamic": "~2.1.0",
    "@angular/router": "~3.1.0",
    "@angular/upgrade": "~2.1.0",
    "angular-in-memory-web-api": "~0.1.5",
    "bootstrap": "^3.3.7",
    "core-js": "^2.4.1",
    "reflect-metadata": "^0.1.8",
    "rxjs": "5.0.0-beta.12",
    "zone.js": "^0.6.25"
  },
  "devDependencies": {
    "concurrently": "^3.0.0",
    "lite-server": "^2.2.2"
  }
}

styles.css

h1 {
  color: #369;
  font-family: Arial, Helvetica, sans-serif;
  font-size: 250%;
}
body {
  margin: 2em;
}
 /*
  * See https://github.com/angular/angular.io/blob/master/public/docs/_examples/styles.css
  * for the full set of master styles used by the documentation samples
  */

包装

我们的第一个应用并不能做什么事。它是一个基础的“Hello, World”Angular应用。

在我们的第一次传递中,我们让它保持简单:我们写了一个小小的Angular组件,向index.html添加一些JavaScript库,并用一个静态文件服务器启动。那是我们所有需要为“Hello, World”应用所做的。

我们还有更大的野心。

好消息是,设置的大部分开销在我们背后。我们将只接触package.json以更新库。除了为我们的应用“modules”添加脚本之外,我们还很有可能打开index.html,只因为我们需要添加一个库,或者一些CSS样式表。

我们即将进入下一步,建立一个小小的应用程序,演示很多我们可以用Angular来构建的东西。

Tour of Heroes Tutorial上加入我们吧!

附录

本章的余下部分是一组附录,详细阐述了我们上面快速介绍的一些要点。

这里没有什么至关重要的材料。继续阅读是为了满足好奇心。

Appendix: Libraries

我们载入了以下脚本

index.html

 <!-- IE required polyfill -->
    <script src="node_modules/core-js/client/shim.min.js"></script>

    <script src="node_modules/zone.js/dist/zone.js"></script>
    <script src="node_modules/reflect-metadata/Reflect.js"></script>

    <script src="node_modules/rxjs/bundles/Rx.js"></script>
    <script src="node_modules/@angular/core/bundles/core.umd.js"></script>
    <script src="node_modules/@angular/common/bundles/common.umd.js"></script>
    <script src="node_modules/@angular/compiler/bundles/compiler.umd.js"></script>
    <script src="node_modules/@angular/platform-browser/bundles/platform-browser.umd.js"></script>
    <script src="node_modules/@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js"></script>

我们用一个Internet Explorer polyfill开始。IE要求一个polyfill以运行依赖于ES2015约定的应用,而且模块是动态载入的。大多数应用需要那些能力,大多数应用程序需要运行在Internet Explorer中。

下面是用于Angular的polyfills,zone.jsReflect.js,跟着反应扩展ExJS库。

我们的快速起步并没有使用反应扩展,但是当你在实战中,任何实质的应用都需要它们。我们在快速起步中添加了这些库,从而以后不会忘掉。

最终,我们载入了Angular本身的Web开发版本。

等我们收获了经验,我们将作出不同的选择,变得更关注于产品品质,比如说载入时间以及内存脚印。

附录:package.json

npm是一个流行的包管理工具,Angular应用开发人员依靠它来取得并管理他们的应用所需要的库。

我们在一个npmpackage.json文件中指定需要的包。

Angular团队建议的包,列出在本文的dependenciesdevDependencies部分中:

package.json (dependencies)

{
  "dependencies": {
    "@angular/common": "~2.1.0",
    "@angular/compiler": "~2.1.0",
    "@angular/core": "~2.1.0",
    "@angular/forms": "~2.1.0",
    "@angular/http": "~2.1.0",
    "@angular/platform-browser": "~2.1.0",
    "@angular/platform-browser-dynamic": "~2.1.0",
    "@angular/router": "~3.1.0",
    "@angular/upgrade": "~2.1.0",
    "angular-in-memory-web-api": "~0.1.5",
    "bootstrap": "^3.3.7",
    "core-js": "^2.4.1",
    "reflect-metadata": "^0.1.8",
    "rxjs": "5.0.0-beta.12",
    "zone.js": "^0.6.25"
  },
  "devDependencies": {
    "concurrently": "^3.0.0",
    "lite-server": "^2.2.2"
  }
}

还存在一些其它可选的包。我们推荐这个特别设置,据我们所知它们作用很棒。现在和我们一起玩吧。以后再根据你喜好制作替代。

package.json has an optional scripts section where we can define helpful commands to perform development and build tasks. We've included a number of such scripts in our suggested package.json:

package.json (scripts)

{
  "scripts": {
    "start": "npm run lite",
    "lite": "lite-server"
  }
}

我们已经看到了我们可以如何利用命令运行服务:

npm start

我们正在使用特殊的npm start命令,但是它确实是在运行npm run lite

我们以这种方式执行npm脚本:npm run + script-name。这几句脚本做了这些事情:

  • npm run lite - run the lite-server, a light-weight, static file server, written and maintained by John Papa with excellent support for Angular apps that use routing.

附录:npm错误和警告

All is well if there are no console messages starting with npm ERR! at the end of npm install. There might be a few npm WARN messages along the way — and that is perfectly fine.

We often see an npm WARN message after a series of gyp ERR! messages. Ignore them. A package may try to re-compile itself using node-gyp. If the re-compile fails, the package recovers (typically with a pre-built version) and everything works.

Just make sure there are no npm ERR! messages at the very end of npm install.

附录NgModule

The NgModule decorator is listing:

  1. 我们用到的别的Angular模块
  2. 我们在我们的组件中声明了哪些组件和指令
  3. 在开始所引导的组件

We import our lone app.AppComponent and add it to both declaration and bootstrap array.

Notice that we also add ng.platformBrowser.BrowserModule to the imports array. This is the Angular Module that contains all the needed Angular bits and pieces to run our app in the browser.

Angular itself is split into separate Angular Modules so we only need to import the ones we really use.

One of the most common ones is FormsModule, and soon we'll also see RouterModule and HttpModule.

Appendix: main.js

BOOTSTRAPPING IS PLATFORM-SPECIFIC

We use the platformBrowserDynamic().bootstrapModule function from ng.platformBrowserDynamic, not ng.core. There's a good reason.

我们只调用“核心”那些能力,贯穿于整个平台目标。是的,大多数Angular应用并发只运行在浏览器中,在大多数时候,我们将从这个库中调用bootstrap函数。如果我们总是为浏览器而写,它就相当“核心”。

But it is possible to load a component in a different environment. We might load it on a mobile device with Apache Cordova or NativeScript. We might wish to render the first page of our application on the server to improve launch performance or facilitate SEO.

这些目标要求不同类型的bootstrap函数,我们将从不同的库中导入它们。

WHY DO WE CREATE A SEPARATE MAIN.JSAPP.MODULE.JS AND APP.COMPONENT.JS FILES?

The main.js file is tiny. This is just a QuickStart. We could have folded its few lines into the app.module.js file, and that one into app.component.js and spared ourselves some complexity.

我们不是为我们认为是好的原因:

  1. 做对它很容易
  2. 可测试性
  3. 可重用性
  4. 关注点的分离
  5. 我们学会了导入和导出

IT'S EASY

Sure it's an extra step and an extra file. How hard is that in the scheme of things?

We'll see that a separate main.js and app.module.js is beneficial for most apps even if it isn't critical for the QuickStart. Let's develop good habits now while the cost is low.

TESTABILITY

We should be thinking about testability from the beginning even if we know we'll never test the QuickStart.

It is difficult to unit test a component when there is a call to bootstrapModule in the same file. As soon as we load the component file to test the component, the bootstrapModule function tries to load the application in the browser. It throws an error because we're not expecting to run the entire application, just test the component.

Relocating the bootstrapModule function to main.js eliminates this spurious error and leaves us with a clean component module file.

REUSABILITY

We refactor, rename, and relocate files as our application evolves. We can't do any of those things while the file calls bootstrapModule. we can't move it. We can't reuse the component in another application. We can't pre-render the component on the server for better performance.

SEPARATION OF CONCERNS

A component's responsibility is to present and manage a view, and a NgModule's reponsibility is to define the application context.

Launching the application has nothing to do with any of these. That's a separate concern. The friction we're encountering in testing and reuse stems from this unnecessary mix of responsibilities.

IMPORT/EXPORT

While writing a separate main.js and app.module.js files we learned an essential Angular skill: how to 'export' from one 'module' and 'import' into another via our simple namespace abstraction. We'll do a lot of that as we learn more Angular.

如果你喜欢这篇文章,敬请给站长打赏↑

除特别注明外,本站所有文章均为本站站长原译,转载请注明出处。