让我们从零开始用JavaScript创建一个Angular 2应用。(教程同时也有TypeScript版和Dart版。)
看一下运行效果
运行在线案例是最快的看Angular 2应用演示的方法。
点击链接会打开浏览器,载入plunker,并显示一个消息。
下面是文件结构
angular2-quickstart
L------app
L------app.component.js
L------boot.js
L------index.html
L------license.md
从功能上说,它就是一个index.html文件和app/文件夹下的两个JavaScript文件。我们完全可以搞定。
当然我们也不会构建许多只能在plunker运行的应用的。让我们跟随一个实际中的流程。
- 搭建开发环境
- 写我们的应用的Angular根组件
- 启动它控制主页面
- 书写主页面(index.html)
如果我们按照下面的步骤,忽略解释,我们完全可以在5分钟内构建Quickstart这个应用。
但大多数人还是对why和how比较感兴趣的,这个会多花一些时间。
开发环境
我们需要一个立足之地(应用的项目文件夹),一些库还有一个编辑器。
创建一个项目文件夹
mkdir angular2-quickstart
cd angular2-quickstart
添加我们需要的库文件
我们推荐使用npm包管理器来获取和管理我们的开发库。
添加一个package.json文件到项目文件夹。
package.json
{
"name": "angular2-quickstart",
"version": "1.0.0",
"scripts": {
"start": "npm run lite",
"lite": "lite-server"
},
"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": {
"lite-server": "^1.3.1"
}
}
想详细了解?看下面附录的解释。
通过npm install命令安装所有包。遇到红色的出错信息直接忽略,安装会成功的。详细看附录。
我们的第一个Angular组件
组件是最基本的Angular概念。一个组件管理一个视图(视图也就是我们向用户展示信息并对用户反馈作出回应的一块网页)
从技术上讲,组件就是一个控制视图模板的类。在创建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 2 App</h1>'
})
.Class({
constructor: function() {}
});
})(window.app || (window.app = {}));
我们通过链式调用全局Angular core命名空间ng.core中的Component和Class方法创建了一个名为AppComponent的可视化组件。
app/app.component.js (component schema)
app.AppComponent =
ng.core.Component({
})
.Class({
});
Component方法接受一个包含两个属性的配置对象;Class方法是我们实现组件本身的地方,在Class方法中我们给组件添加属性和方法,它们会绑定到相应的视图和行为。
模块
Angular应用都是模块化的。包含的每个文件都有相应的功能。
ES5没有内置的模块化系统。但第三方提供的模块系统有几个可以使用。但是,为了简单并免于为选择纠结,我们会为我们的应用创建一个单独全局命名空间。
我们把它命名为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的子组件。一个更加复杂的应用会有更多文件和模块,至少和它的组件数一样多。
Quickstart并不复杂,我们只需要一个组件。然而即使在这么小的应用中模块依然扮演一个组织角色。
模块依赖其他模块。在JavaScript Angular应用中,当我们需要其他组件提供的一些东西时,我们从app对象中获取它。当其他组件需要引用AppComponent,它用下面方法从app.AppComponent获取。
app/boot.js (import)
ng.platform.browser.bootstrap(app.AppComponent);
Angular也是模块化的,它是一个库模块集合。每一个库本身就是一个由多个相关功能模块构成的模块。
当我们需要Angular的东西时,我们使用ng对象。
Class定义对象
文件底部是一个空的AppComponent的类定义对象。当我们要创建一个是有实际意义的应用时,我们可以使用属性和应用逻辑来扩展这个对象。我们的AppComponent类只有一个空的构造函数,因为在Quickstart这个例子中我们不需要它做什么。
app.component.js (class)
.Class({
constructor: function() {}
});
Component定义对象
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 2 App</h1>'
})
selector为一个宿主HTML元素定义了一个简单的CSS选择器my-app。当Angular在宿主HTML中遇到一个my-app元素时它创建并显示一个AppComponent实例。
一定要记住my-app选择器,我们在创建index.html时需要这个信息。
template属性容纳着组件的模板。一个模板以HTML的形式告诉Angular如何渲染一个视图。我们的模板是一行显示“My First Angular App”的HTML代码。
现在我们需要一些东西告诉Angular来加载这个组件。
让它启动
给app/文件夹中添加一个新文件boot.js
app/boot.js
(function(app) {
document.addEventListener('DOMContentLoaded', function() {
ng.platform.browser.bootstrap(app.AppComponent);
});
})(window.app || (window.app = {}));
我们需要两件东西来启动应用。
- Angular的browser bootstrap函数
- 我们刚刚写的应用根组件
我们把他们都放到我们的命名空间下。然后调用bootstrap,将根组件类型AppComponent作为参数传进去。
在下面的附录中学习为什么我们需要ng.platform.browser的bootstrap方法以及为什么我们要创建一个单独的boot.js文件。
我们已经让Angular在浏览器中用我们在根部的组件启动应用了。Angular会把它放到哪里呢?
添加index.html
Angular在我们的index.html上的特定位置显示我们的应用。现在是创建它的时候了。
index.html
<html>
<head>
<title>Angular 2 QuickStart</title>
<!-- 1. Load libraries -->
<!-- IE required polyfill -->
<script src="node_modules/es6-shim/es6-shim.min.js"></script>
<script src="node_modules/angular2/bundles/angular2-polyfills.js"></script>
<script src="node_modules/rxjs/bundles/Rx.umd.js"></script>
<script src="node_modules/angular2/bundles/angular2-all.umd.js"></script>
<!-- 2. Load our 'modules' -->
<script src='app/app.component.js'></script>
<script src='app/boot.js'></script>
</head>
<!-- 3. Display the application -->
<body>
<my-app>Loading...</my-app>
</body>
</html>
以上代码有3部分值得注意:
- 载入我们需要的JavaScript库;在下面学习它们。
- 载入我们自己的JavaScript文件,注意它们的顺序(boot.js需要app.component.js文件在前面)。
- 我们在<body>标签中添加<my-app>标签。这就是我们的应用生活的地方。
当Angular在boot.js中调用bootstrap函数时,它读取AppComponent的元数据,找到my-app选择器,定位到一个名字为my-app的元素,然后再这个标签之间载入我们的应用。
运行!
打开一个终端,输入以下命令
npm start
这个命令运行了一个叫做lite-server的静态服务器。它在浏览器中加载index.html页面并在应用文件改变时刷新浏览器。
稍等片刻,浏览器标签就会打开并显示
恭喜!我们已经上手了。
如果你看到显示的是Loading...查看一下浏览器ES2015支持附录。
做一些改变
试着将消息改成“我的第二个Angular 2应用”。
lite-server一直在监听,因此它会知道这个改变,并刷新浏览器,然后显示修改后的消息。
这是一个有趣的开发应用的方法。
结束后关掉终端窗口就关掉了服务器。
文件结构
我们最终的文件结构是这样的:
angular2-quickstart
L------node_modules
L------app
L------app.component.js
L------boot.js
L------index.html
L------package.json
下面是对应的文件代码。
app/app.component.js
(function(app) {
app.AppComponent =
ng.core.Component({
selector: 'my-app',
template: '<h1>My First Angular 2 App</h1>'
})
.Class({
constructor: function() {}
});
})(window.app || (window.app = {}));
app/boot.js
(function(app) {
document.addEventListener('DOMContentLoaded', function() {
ng.platform.browser.bootstrap(app.AppComponent);
});
})(window.app || (window.app = {}));
index.html
<html>
<head>
<title>Angular 2 QuickStart</title>
<!-- 1. Load libraries -->
<!-- IE required polyfill -->
<script src="node_modules/es6-shim/es6-shim.min.js"></script>
<script src="node_modules/angular2/bundles/angular2-polyfills.js"></script>
<script src="node_modules/rxjs/bundles/Rx.umd.js"></script>
<script src="node_modules/angular2/bundles/angular2-all.umd.js"></script>
<!-- 2. Load our 'modules' -->
<script src='app/app.component.js'></script>
<script src='app/boot.js'></script>
</head>
<!-- 3. Display the application -->
<body>
<my-app>Loading...</my-app>
</body>
</html>
package.json
{
"name": "angular2-quickstart",
"version": "1.0.0",
"scripts": {
"start": "npm run lite",
"lite": "lite-server"
},
"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": {
"lite-server": "^1.3.1"
}
}
打包
我们的第一个应用并没有做太多事情。它基本上就是Angular 2版本的“Hello World”。
我们在第一次尝试中尽量简单:我们写了一个小Angular组件,向index.html添加一些JavaScript库,并启动一个静态文件服务器。这就是我们对一个“Hello World”的期望吧。
我们有更远大的目标。
好消息是我们已经入门了。我们可能仅仅碰了一下package.json文件来更新库。添加我们的应用模块文件之后,我们仅仅在需要添加库和样式表时打开index.html。
我们要进行下一步学习,并使用Angular 2创建一个有很棒功能的小应用。
加入我们的英雄教程之旅吧!
附录
附录的作用就是对上面一些简单略过的点详细讲解。
这里没有必要的资料。如果好奇可以往下看。
附录:库
我们载入下面的脚本:
index.html
<!-- IE required polyfill -->
<script src="node_modules/es6-shim/es6-shim.min.js"></script>
<script src="node_modules/angular2/bundles/angular2-polyfills.js"></script>
<script src="node_modules/rxjs/bundles/Rx.umd.js"></script>
<script src="node_modules/angular2/bundles/angular2-all.umd.js"></script>
我们首先载入了一个IE polyfill。IE需要一个polyfill来运行那些依赖ES2015 promise和动态模块加载的应用。大部分应用都需要这些功能,而且大部分应用都能在IE中运行。
然后是Angular 2的polyfill,angular2-polyfills.js,再然后是Reactive Extensions RxJS库。
我们的Quickstart并不使用Reactive Extensions,但后面的实用的应用要用到它。所以我们提前加入。
最后我们加载Angular 2的网络开发版本。
当我们经验丰富并对加载时间内存使用等产品性能比较关注时我们可以选择不同的库。
附录:package.json
npm是一个流行的包管理器,Angular应用开发者依靠它来获取和关系应用需要的库。
我们在一个npm package.json文件中指明我们需要的包。
Angular团队建议我们使用在dependencies和devDependencies部分列出的包。
package.json (dependencies)
{
"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": {
"lite-server": "^1.3.1"
}
}
也可以选择其他包。我们推荐这些包是因为这些在一起工作的很好。现在和我们一起摇摆吧。之后你可以根据自己的爱好和经验随意替换。
package.json文件有一个可选的scripts部分,在这里我们可以定义一些帮助命令来执行开发和构建任务。我们已经在我们的package.json文件中包含了一些这样的scripts。
package.json (scripts)
{
"scripts": {
"start": "npm run lite",
"lite": "lite-server"
}
}
我们已经知道使用<code>npm start</code>来运行服务器。
我们指定的是<code>npm start</code>命令,但实际运行的是<code>npm run lite</code>命令。
我们用如下形式执行npm scripts:
npm run + script-name
<code>npm run lite</code>的作用-运行lite-server,它是John Papa书写并维护的一个轻量的静态文件服务器,对使用路由的Angular应用支持很好。
附录:npm错误和警告
如果在执行<code>npm install</code>命令时没有出现<code>npm ERR!</code>,一起都好。可能会有几个<code>npm WARN</code>消息,这也没有影响。
我们会经常在一连串的<code>gyp ERR!</code>(gyp: generate your project)消息后看到一个<code>npm WARN</code>消息。不用管他们。一个包会使用<code>node-gyp</code>重新编译自己。如果重新编译失败,包会恢复(场使用一个预编译版本),一切正常工作。
只要保证在<code>npm install</code>命令最后面没有<code>npm ERR!</code>就好。
附录:boot.js
启动是平台相关的。
我们使用的bootstrap函数来自ng.platform.browser而不是ng.core。这是有原因的。
创建一个单独boot.js的原因:
- 这是容易的
分离boot.js很容易。这对大多数应用都有益处,虽然在Quickstart中没什么用。让我们从最开始就养成好习惯。 - 可测试性
即使我们不用测试Quickstart我们也因该总一开始就考虑可测试性。
如果同一个文件中有一个对bootstrap的调用,要测试这个组件就比较困难。只要我们载入组件文件来测试组件,bootstrap函数就会尝试着在浏览器中载入应用。它会抛出一个错误,因为我们只想测试组件而不是运行整个应用。
在boot.js文件中找到bootstrap函数,删除这个似是而非的错误,留下一个干净的组件模块文件。 - 重用性
在应用的演化过程中,我们要重构、重命名和重定位文件。当文件调用bootstrap时我们对这些文件不能做上面任何操作。我们不能移动,不能在其他应用中重用组件。我们不能为提升性能在服务器端预渲染组件。 - 关注分离
一个组件的责任就是展示和管理一个视图。
启动应用和视图管理没有一点关系。这是一个分离的关注点。我们在测试和重用中遇到的很多问题都源于这种责任的混淆。 - 引入/输出
当分离boot.js时我们学会了Angular一个关键技能:如何通过简单的命名空间抽象输出一个模块和如何导入另一个模块。在后续学习中会经常这么做的。