快速起步 (JavaScript)
这个快速起步向导向你演示了如何创建并运行一个简单的Angular应用。
让我们从零开始,创建一个超级简单的用JavaScript的Angular应用。
看它运行
运行该示例是查看一个Angular应用变成现实的最快捷的方式。
点击链接触发一个浏览器,载入plunker中的案例,显示一条简单的消息:

这上文件结构:
angular-quickstart ├ node_modules ├ app │ ├app.component.js │ ├app.module.js │ └main.js ├ index.html ├ package.json └ styles.css
从功能上说,它是一个index.html
、styles.css
和三个在app/
文件夹中的JavaScript文件。我们可以处理它!
当然,如果只运行在plunker中,我们不能创建很多应用。让我们跟随着一个接近于现实生活中所用的处理过程。
- 在我们的开发环境中设置
- 为我们的应用编写Angular根组件
- 添加一个Angular模块
- 给它添加Bootstrap,以控制主页
- 编写主页
- 添加一些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 = {}));
通过连缀Component
和Class
方法,我们创建了一个命名为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 = {}));
我们需要两样东西来启动这个应用:
- Angular的
platformBrowserDynamic().bootstrapModule
函数。 - 我们刚刚写的应用根模块。
两者都在'namespaces'内。然后我们调用bootstrapModule
,传入root app module、AppModule
。
在下面附录中可以让你知道为什么我们需要来自
ng.platformBrowserDynamic
的bootstrapModule
,以及为什么我们创建了一个分开的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中有三个值得注意的部分:
- 我们载入了我们需要的JavaScript库;在下面了解它们。
- 我们载入了我们的JavaScript文件。
- 我们在
<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
,当应用文件改变的时候,刷新该浏览器。
在一些时候,一个浏览器选项卡必须打开并显示。

好赞!我们投入实战了。
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.js
和Reflect.js
,跟着反应扩展ExJS库。
我们的快速起步并没有使用反应扩展,但是当你在实战中,任何实质的应用都需要它们。我们在快速起步中添加了这些库,从而以后不会忘掉。
最终,我们载入了Angular本身的Web开发版本。
等我们收获了经验,我们将作出不同的选择,变得更关注于产品品质,比如说载入时间以及内存脚印。
附录:package.json
npm是一个流行的包管理工具,Angular应用开发人员依靠它来取得并管理他们的应用所需要的库。
我们在一个npmpackage.json文件中指定需要的包。
Angular团队建议的包,列出在本文的dependencies
和devDependencies
部分中:
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" } }
还存在一些其它可选的包。我们推荐这个特别设置,据我们所知它们作用很棒。现在和我们一起玩吧。以后再根据你喜好制作替代。
A 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:
- 我们用到的别的Angular模块
- 我们在我们的组件中声明了哪些组件和指令
- 在开始所引导的组件
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.JS, APP.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.
我们不是为我们认为是好的原因:
- 做对它很容易
- 可测试性
- 可重用性
- 关注点的分离
- 我们学会了导入和导出
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.