初识Angular2
Angular 2
是Google推出的一个跨平台全终端的框架
主要有八大架构组成,分别是:
module (模块)
component (组件)
template (模板)
Metadata (元数据)
data binding (数据绑定)
directive (指令)
service (服务)
dependency injection (依赖注入)
这几个构架,可以用一张图展示下之间的关系
我们去写一个Angular应用:用Angular扩展语法写HTML模板,写组件类去管理这些模板,用服务添加应用逻辑,用模块打包组件和服务。
说了这些概念性的东西你可能还是有点懵,没关系,以上内容只是让你对Angular有个大致的印象。因为,它玩来玩去就是围绕以上图片提到的几个概念展开的,接下来我们通过具体的环境搭建来了解下,Angular2是如何运行的。
环境搭建
mkdir demo cd demo npm init
根据npm init
,创建package.json
文件,创建后去掉不必要的字段,添加你所需要导入的包以及运行时的一些命令:
{ "name": "demo","version": "1.0.0","description": "","scripts": { "postinstall": "./node_modules/.bin/typings install" },"author": "","license": "ISC","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": { "ts-loader": "^0.8.2","typescript": "^2.0.3","typings": "^1.4.0","path": "^0.12.7","webpack": "^1.13.2","webpack-dev-server": "^1.16.1" } }
简单解释下:postinstall
:来进行安装后的脚本处理dependencies
是运行时所需要的依赖;devDependencies
是开发时所需要的依赖,@angular
是 Angular 的核心库,Angular2 将它分成了很多包,这样方便开发者灵活的按需引入。@angular/common
: 常用的那些由 Angular 开发组提供的服务、管道和指令@angular/compiler
: Angular 的 模板编译器 。 它会理解模板,并且把模板转化成代码,以供应用程序运行和渲染。 开发人员通常不会直接跟这个编译器打交道,而是通过 platform-browser-dynamic 或离线模板编译器间接使用它。@angular/core
:框架中关键的运行期部件,每一个应用都需要它。 包括所有的元数据装饰器: Component 、 Directive ,依赖注入系统,以及组件生命周期钩子。@angular/platform-browser
: 与 DOM 和浏览器相关的每样东西,特别是帮助往 DOM 中渲染的那部分。 这个包还包含 bootstrapStatic 方法,用来引导那些在产品构建时需要离线预编译模板的应用程序。@angular/platform-browser-dynamic
: 为应用程序提供一些 提供商 和 bootstrap 方法,以便在客户端编译模板。不要用于离线编译。 我们使用这个包在开发期间引导应用,以及引导 plunker 中的范例。@angular/upgrade
: 一组用于升级 Angular 1 应用的工具。angular-in-memory-web-api
: 一个 Angular 的支持库,它能模拟一个远端服务器的 Web API ,而不需要依赖一个真实的服务器或发起真实的 HTTP 调用。 对演示、文档范例和开发的早期阶段 ( 那时候我们可能还没有服务器呢 ) 非常有用。 reflect-Metadata
、rxjs
、zone.js
、core-js
这些都是为 Angular 提供核心功能支持。reflect-Metadata
:Angular和TypeScript共享的一个依赖。为了使用 ES7风格的装饰器准备的,就像@Component 。core-js
: javascript的一个标准库实现,包含了大量ES2015,ES2016的ES5实现。rxjs
: 一个Reactive Programming(响应式)JavaScript实现。这里对她的依赖是因为angular2支持多种数据更新模式,比如:flux、Rx.js。zone.js
: 用来对异步任务提供Hooks支持,使得在异步任务运行之前/之后做额外操作成为可能。在angular2里的主要应用场景是提高脏检查效率、降低性能损耗。webpack-dev-server
是支持 webpack 的静态服务器调试工具,它可以实时监控项目中文件,文件改动后使浏览器自动刷新或进行热替换,我们的项目将运行在这个小型服务器上。ts-loader
是 webpack 将 TypeScript 编译成 ES5 的编译器,本文里 我们使用 TypeScript 来写例子,它也是 Angular 官方推荐的开发语言。typings
是 TypeScript 定义的文件管理系统.
创建其他的配置文件
配置文件tsconfig.json
,(定义 TypeScript 编译成 ES5 的参数):
`
touch tsconfig.json
`
{ "compilerOptions": { "target": "es5","module": "commonjs","moduleResolution": "node","sourceMap": true,"emitDecoratorMetadata": true,"experimentalDecorators": true,"removeComments": true,"noImplicitAny": true,"declaration": false } }
配置文件typings.json
:
一些js库扩展了JavaScript的特性和语法,但是TypeScript编译器并不识别,
通过typings.json配置可以辅助IDE,给出有智能的提示信息,以及重构的依据。
因此需要在typings.json文件中配置TypeScript类型定义文件(文件名后缀为.d.ts)touch typings.json
{ "globalDependencies": { "es6-shim": "registry:dt/es6-shim#0.31.2+20160602141504" } }
现在运行 npm install 安装 package.json 里所有的依赖包,过程可能会有些慢(如果你开了 VPN 就不存在这问题),这里推荐使用淘宝镜像命令加快安装速度 npm install --registry=https://registry.npm.taobao.org/
安装完,你的项目目录应该是这样的
如果在运行 npm install 之后没有出现 typings 目录,你就需要通过命令手动安装它:npm run typings install
创建应用入口
在项目目录中创建 app 子目录,接着在 app 目录下创建 app.module.ts,也就是目前项目的根模块。
(Angular提倡的文件命名方式是这样的:组件名称.component.ts ,服务命名为: 服务名称.service.ts,模块: 模块名称.module.ts,大家在编码中尽量遵循Google的官方建议。 )
mkdir app touch app/app.module.ts
在刚创建的 app.module.ts 文件中添加下面代码:
// zone 为异步任务提供hook支持,主要应用于提高脏检查效率和降低性能损耗 // reflect-Metadata 为Decorator提供反应射API import 'zone.js/dist/zone'; import 'reflect-Metadata'; // 引入NgModule装饰器 import { NgModule } from '@angular/core'; // 引入浏览器模块 import { BrowserModule } from '@angular/platform-browser'; @NgModule({ imports: [ BrowserModule ] }) export class AppModule {}
NgModules 把 Angular 应用组织成了一些功能相关的代码块。 Angular 本身也被拆成了一些独立的 Angular 模块。 这让你可以只导入你应用中所需的 Angular 部件,以得到较小的文件体积。每个 Angular 应用都至少有一个模块: 根模块 ,在这里它叫做 AppModule 。
这个文件是整个应用的入口,由于应用需要运行在浏览器中,所以需要从 @angular/platform-browser 中导入BrowserModule,并将它添加到 imports 数组中,我们需要使用BrowserModule
浏览器模块,提供浏览器加载页面所需要的关键服务(Service)和指令(Directive),这个模块是所有需要在浏览器中跑的应用都必须引用的。@
(装饰器)是一种特殊的类型声明,它可以被附加到类声明,方法,属性或参数上,这里我们不做具体介绍。
创建组件,并添加到应用中
每个 Angular 应用都至少有一个根组件,整个应用由一个个组件组成,而组件化(Component)则是 Angular 的核心灵魂。
创建组件 AppComponent,即文件 app.component.ts,在其中添加代码:touch app/app.component.ts
import { Component } from '@angular/core'; // 为 AppComponent 组件类添加注解 @Component({ selector: 'my-app',template: '<h1>My First Angular App</h1>' }) export class AppComponent {}
@Component
装饰器 会把一份 元数据 关联到 AppComponent 组件类上
selector
用来为该组件的 HTML 元素指定简单的 CSS 选择器template
用来告诉 Angular 如何渲染该组件的视图
class AppComponent 之所以是空的,因为我们的例子暂时不需要应用逻辑。
编辑 app.module.ts
文件,导入 AppComponent ,并把它添加到 NgModule 装饰器中的 declarations 和 bootstrap 字段:
// zone 为异步任务提供hook支持,主要应用于提高脏检查效率和降低性能损耗 // reflect-Metadata 为Decorator提供反应射API,注意:Decorator是ES6的提案 import 'zone.js/dist/zone'; import 'reflect-Metadata'; // 引入NgModule装饰器 import { NgModule } from '@angular/core'; // 引入浏览器模块 import { BrowserModule } from '@angular/platform-browser'; //引入AppComponent组件 import {AppComponent} from './app.component'; @NgModule({ imports: [ BrowserModule ],declarations:[AppComponent],bootstrap:[AppComponent] }) export class AppModule {}
declarations
列出了应用中的顶层组件,包括引导性组件AppComponentbootstrap
指明哪个组件为引导性组件
启动应用文件
创建 main.ts 让 Angular 加载这个启动文件,在其中添加如下代码:touch main.ts
// 引入启动器 import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; // 引入入口文件 import { AppModule } from './app.module'; // 启动应用 const platform = platformBrowserDynamic(); platform.bootstrapModule(AppModule);
为什么要分别创建 main.ts 、应用模块 和应用组件的文件呢?
因为应用的引导过程与 创建模块或者 展现视图是相互独立的。
创建应用的宿主页面 index.htmltouch index.html
<!DOCTYPE html> <html lang="en"> <head> <Meta charset="UTF-8"> <Meta name="viewport" content="width=device-width,initial-scale=1"> <title>Angular2 QuickStart</title> </head> <body> <!-- 对应 @Component({ selector: 'my-app',... }) --> <my-app></my-app> <!-- 编译打包后的文件 --> <script src="dist/bundle.js"></script> </body> </html>
创建webpack.config.js
我们使用 webpack 作为编译打包工具,就需要创建 webpack.config.js,touch webpack.config.js
var path = require('path'); module.exports = { entry: { main: './app/main.ts' },output: { path: path.resolve(__dirname,'dist'),publicPath: '/dist/',filename: 'bundle.js' },module: { loaders: [ { test: /\.ts$/,loader: 'ts' } ] },// require 文件时可省略后缀 .js 和 .ts resolve: { extensions: ['','.js','.ts'] },// 配置 webpack-dev-server devServer:{ historyApiFallback: true,hot: true,inline: true,port: 3000 // 修改端口,一般默认是8080 } };
"scripts": { "start": "webpack-dev-server --inline --hot --colors --progress","postinstall": "./node_modules/.bin/typings install" },
一切就绪,先不急着启动 webpack-dev-server,初学者经常遇到的坑,关于 webpack-dev-server 无法让浏览器在代码修改后进行热替换,首页要确保是否全局安装了 webpack-dev-server,如果没有就运行下面命令:npm install webpack-dev-server -g --registry=https:\\registry.npm.taobao.org
更重要的是 webpack-dev-server 只会将静态文件打包在内存中 而非你的磁盘里 这是为了方便开发环境调试,所以初学者会疑惑为什么运行了 webpack-dev-server,dist 文件夹下确无文件产生,想产生文件就运行 webpack。
ok!运行项目
npm start
打开浏览器 localhost:3000
,就会看到我们的第一个angular2应用了
简单的数据绑定
我们来修改下现在页面上的内容,改为程序员常见的hello world
,并且我要让hello world变成红色,在AppComponent
中添加一个属性title,用angular2自带的插值表达式来展示title的内容:<h1>Hello {{title}}</h1>
整个代码:
import { Component } from '@angular/core'; // 为 AppComponent 组件类添加注解 @Component({ selector: 'my-app',template: '<h1>Hello {{title}}</h1>',styles:['h1{color:red}'] }) export class AppComponent { title='World'; }
styles
顾名思义,这个就是样式的定义。当然我们也可以使用styleUrls来定义样式文件的路径。
这里我们将h1内容变为红色,我们多次提到Angular2是组件化的,组件之间是不会相互影响,也就是说我们对当前组件定义的样式只会在当前组件对应的模板内生效。
好了,我们保存一下,页面上就能看到对应的变化。
可是这样,title还是我自己定义的,我们要改动还是需要到代码里面去改,如果我给一个输入框,输什么就显示什么,并且hello保持不变?
首先,我们向页面加入一个input框,用angular2自带的模板语法#
给input定义一个变量<h1>Hello {{title}}</h1><input type="text" #userRef (keyup)="onKeyup1(userRef.value)">
,
这个模板引用变量名叫userRef,在<input>
元素内声明,它引用<input>
元素本身。 使用userRef获得输入元素的value值,并通过插值表达式把它显示在<h1>
标签中。
输入触发input的keyup事件,在angular2中,对于元素的事件绑定,我们会对目标事件会用()
起来,属性绑定对目标属性用[]
包起来,我们给keyup
事件绑定了一个方法onKeyup1
,这个方法用来将用户输入的值赋给title
:
export class AppComponent { title='World'; onKeyup1(userInput:string){ //这是typescript的写法,定义你的参数为string类型,并在调用的时候做检查 this.title=userInput; } }
保存更改的内容你会发现,我们实现了之前想要的效果;这个时候我们回过头来看最开始的那张图,看看angular2中的八大构架,大概能理解几个,我想除了依赖注入,其他的大致都有了了解,对于angular是如何运作的应该清楚了,那就动手试试吧!