Angular2是Google下一代MV*框架,用于构建复杂浏览器应用。Angular2给WEB或移动Apps带来了一揽子解决方案,从模板渲染到数据绑定,从Http服务到表单处理等等,总之,你想要的,Angular2基本上都已封装好了。
万事始于Hello World,不论Angular2怎么牛,咱们先得把官方的例子跑起来再说。这里使用基于webpack创建Angular应用。
环境准备
node v5.x.x
npm v3.x.x
step1:创建并配置本项目
创建项目目录:
mkdir angular2Demo
cd@H_403_17@ angular2Demo
创建配置文件
典型的 Angular 项目需要一系列配置文件
package.json 用来标记出本项目所需的 npm 依赖包。
tsconfig.json 定义了 TypeScript 编译器如何从项目源文件生成 JavaScript 代码。
typings.json 为那些 TypeScript 编译器无法识别的库提供了额外的定义文件。
webpack.config.js为构建Angular应用所进行的一系列webpack配置。
package.json
{
"name@H_403_17@": "angular2demo"@H_403_17@@H_403_17@,"version@H_403_17@": "1.0.0"@H_403_17@@H_403_17@,"description@H_403_17@": "Angular 2 demo."@H_403_17@@H_403_17@,"main@H_403_17@": "index.js"@H_403_17@@H_403_17@,"scripts@H_403_17@": { "start@H_403_17@": "webpack-dev-server --inline --progress --port 8080"@H_403_17@@H_403_17@,"test@H_403_17@": "karma start"@H_403_17@@H_403_17@,"build@H_403_17@": "rimraf dist && webpack --config config/webpack.prod.js --progress --profile --bail"@H_403_17@@H_403_17@,"postinstall@H_403_17@": "typings install"@H_403_17@ @H_403_17@}@H_403_17@,"dependencies@H_403_17@": { "@angular/common@H_403_17@": "2.0.0"@H_403_17@@H_403_17@,"@angular/compiler@H_403_17@": "2.0.0"@H_403_17@@H_403_17@,"@angular/core@H_403_17@": "2.0.0"@H_403_17@@H_403_17@,"@angular/forms@H_403_17@": "2.0.0"@H_403_17@@H_403_17@,"@angular/http@H_403_17@": "2.0.0"@H_403_17@@H_403_17@,"@angular/platform-browser@H_403_17@": "2.0.0"@H_403_17@@H_403_17@,"@angular/platform-browser-dynamic@H_403_17@": "2.0.0"@H_403_17@@H_403_17@,"@angular/router@H_403_17@": "3.0.0"@H_403_17@@H_403_17@,"core-js@H_403_17@": "^2.4.1"@H_403_17@@H_403_17@,"rxjs@H_403_17@": "5.0.0-beta.12"@H_403_17@@H_403_17@,"zone.js@H_403_17@": "^0.6.23"@H_403_17@ @H_403_17@}@H_403_17@,"devDependencies@H_403_17@": { "angular2-template-loader@H_403_17@": "^0.4.0"@H_403_17@@H_403_17@,"awesome-typescript-loader@H_403_17@": "^2.2.4"@H_403_17@@H_403_17@,"css-loader@H_403_17@": "^0.23.1"@H_403_17@@H_403_17@,"extract-text-webpack-plugin@H_403_17@": "^1.0.1"@H_403_17@@H_403_17@,"file-loader@H_403_17@": "^0.8.5"@H_403_17@@H_403_17@,"html-loader@H_403_17@": "^0.4.3"@H_403_17@@H_403_17@,"html-webpack-plugin@H_403_17@": "^2.15.0"@H_403_17@@H_403_17@,"jasmine-core@H_403_17@": "^2.4.1"@H_403_17@@H_403_17@,"karma@H_403_17@": "^1.2.0"@H_403_17@@H_403_17@,"karma-jasmine@H_403_17@": "^1.0.2"@H_403_17@@H_403_17@,"karma-phantomjs-launcher@H_403_17@": "^1.0.2"@H_403_17@@H_403_17@,"karma-sourcemap-loader@H_403_17@": "^0.3.7"@H_403_17@@H_403_17@,"karma-webpack@H_403_17@": "^1.8.0"@H_403_17@@H_403_17@,"null-loader@H_403_17@": "^0.1.1"@H_403_17@@H_403_17@,"phantomjs-prebuilt@H_403_17@": "^2.1.7"@H_403_17@@H_403_17@,"raw-loader@H_403_17@": "^0.5.1"@H_403_17@@H_403_17@,"rimraf@H_403_17@": "^2.5.2"@H_403_17@@H_403_17@,"style-loader@H_403_17@": "^0.13.1"@H_403_17@@H_403_17@,"typescript@H_403_17@": "^2.0.2"@H_403_17@@H_403_17@,"typings@H_403_17@": "^1.3.2"@H_403_17@@H_403_17@,"webpack@H_403_17@": "^1.13.0"@H_403_17@@H_403_17@,"webpack-dev-server@H_403_17@": "^1.14.1"@H_403_17@@H_403_17@,"webpack-merge@H_403_17@": "^0.14.0"@H_403_17@ @H_403_17@}@H_403_17@,"repository@H_403_17@": { "type@H_403_17@": "git"@H_403_17@@H_403_17@,"url@H_403_17@": "git+https://github.com/HalZhan/angular2Demo.git"@H_403_17@ @H_403_17@}@H_403_17@,"author@H_403_17@": "halzhan"@H_403_17@@H_403_17@,"license@H_403_17@": "MIT"@H_403_17@@H_403_17@,"bugs@H_403_17@": { "url@H_403_17@": "https://github.com/HalZhan/angular2Demo/issues"@H_403_17@ @H_403_17@}@H_403_17@,"homepage@H_403_17@": "https://github.com/HalZhan/angular2Demo#readme"@H_403_17@ @H_403_17@}
tsconfig.json
{
"compilerOptions@H_403_17@": { "target@H_403_17@": "es5"@H_403_17@@H_403_17@,"module@H_403_17@": "commonjs"@H_403_17@@H_403_17@,"moduleResolution@H_403_17@": "node"@H_403_17@@H_403_17@,"sourceMap@H_403_17@": true@H_403_17@@H_403_17@,"emitDecoratorMetadata@H_403_17@": true@H_403_17@@H_403_17@,"experimentalDecorators@H_403_17@": true@H_403_17@@H_403_17@,"removeComments@H_403_17@": false@H_403_17@@H_403_17@,"noImplicitAny@H_403_17@": true@H_403_17@@H_403_17@,"suppressImplicitAnyIndexErrors@H_403_17@": true@H_403_17@ @H_403_17@} @H_403_17@}
typings.json
{
"globalDependencies@H_403_17@": { "core-js@H_403_17@": "registry:dt/core-js#0.0.0+20160725163759"@H_403_17@@H_403_17@,"jasmine@H_403_17@": "registry:dt/jasmine#2.2.0+20160621224255"@H_403_17@@H_403_17@,"node@H_403_17@": "registry:dt/node#6.0.0+20160909174046"@H_403_17@ @H_403_17@} @H_403_17@}
webpack.config.js
module@H_403_17@.exports@H_403_17@ = require@H_403_17@('./config/webpack.dev.js'@H_403_17@); //@H_403_17@ 待补充
karma.conf.js
module@H_403_17@.exports@H_403_17@ = require@H_403_17@('./config/karma.conf.js'@H_403_17@); //@H_403_17@ 待补充
下面我们继续完善配置文件。
公共配置
我们可以为开发、产品和测试环境定义分别各自的配置文件。 但三者总会有一些公共配置。 于是我们把那些公共的配置收集到一个名叫 webpack.common.js 的独立文件中。
config/webpack.common.js
var@H_403_17@ webpack = require@H_403_17@('webpack'@H_403_17@);
var@H_403_17@ HtmlWebpackPlugin = require@H_403_17@('html-webpack-plugin'@H_403_17@);
var@H_403_17@ ExtractTextPlugin = require@H_403_17@('extract-text-webpack-plugin'@H_403_17@);
var@H_403_17@ helpers = require@H_403_17@('./helpers'@H_403_17@);
module.exports = {
entry: {
'polyfills'@H_403_17@: './src/polyfills.ts'@H_403_17@,// 运行Angular时所需的一些标准js@H_403_17@
'vendor'@H_403_17@: './src/vendor.ts'@H_403_17@,// Angular、Lodash、bootstrap.css......@H_403_17@
'app'@H_403_17@: './src/main.ts'@H_403_17@ // 应用代码@H_403_17@
},resolve: {
extensions: [''@H_403_17@,'.js'@H_403_17@,'.ts'@H_403_17@] // 加载的文件类型(明确的扩展名、.js、.ts)@H_403_17@
},module: {
loaders: [
{
test: /\.ts$/@H_403_17@,loaders: ['awesome-typescript-loader'@H_403_17@,'angular2-template-loader'@H_403_17@ // 用于加载 Angular 组件的模板和样式@H_403_17@
]
},// ts - 将typescript代码转译成es5的加载器,由tsconfig.json文件指导@H_403_17@
{
test: /\.html$/@H_403_17@,loader: 'html'@H_403_17@ // 为组件模板准备的加载器@H_403_17@
},{
test: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico)$/@H_403_17@,loader: 'file?name=assets/[name].[hash].[ext]'@H_403_17@ // 图片和字体文件也能被打包@H_403_17@
},{
test: /\.css$/@H_403_17@,exclude: helpers.root('src'@H_403_17@,'app'@H_403_17@),loader: ExtractTextPlugin.extract('style'@H_403_17@,'css?sourceMap'@H_403_17@)
},// 模式匹配应用级样式@H_403_17@
{
test: /\.css$/@H_403_17@,include: helpers.root('src'@H_403_17@,loader: 'raw'@H_403_17@
} // 模式匹配组件局部样式 ( 在组件元数据的 styleUrls 属性中指定的那些 ) @H_403_17@
]
},plugins: [
new@H_403_17@ webpack.optimize.CommonsChunkPlugin({
name: ['app'@H_403_17@,'vendor'@H_403_17@,'polyfills'@H_403_17@]
}),// 提取公共代码@H_403_17@
new@H_403_17@ HtmlWebpackPlugin({
template: 'src/index.html'@H_403_17@
}) // 自动向目标.html文件注入script和link标签@H_403_17@
]
};
config/helpers.js
var@H_403_17@ path = require@H_403_17@('path'@H_403_17@);
var@H_403_17@ _root = path.resolve(__dirname,'..'@H_403_17@);
function@H_403_17@ root@H_403_17@(args)@H_403_17@ {@H_403_17@
args = Array@H_403_17@.prototype.slice.call(arguments@H_403_17@,0@H_403_17@);
return@H_403_17@ path.join.apply(path,[_root].concat(args));
}
exports.root = root;
开发环境配置
config/webpack.dev.js
var@H_403_17@ webpackMerge = require@H_403_17@('webpack-merge'@H_403_17@);
var@H_403_17@ ExtractTextPlugin = require@H_403_17@('extract-text-webpack-plugin'@H_403_17@);
var@H_403_17@ commonConfig = require@H_403_17@('./webpack.common.js'@H_403_17@);
var@H_403_17@ helpers = require@H_403_17@('./helpers'@H_403_17@);
module@H_403_17@.exports@H_403_17@ = webpackMerge(commonConfig,{
devtool@H_403_17@: 'cheap-module-eval-source-map'@H_403_17@,output@H_403_17@: {
path@H_403_17@: helpers.root('dist'@H_403_17@),publicPath@H_403_17@: 'http://localhost:8080/'@H_403_17@,filename@H_403_17@: '[name].js'@H_403_17@,chunkFilename@H_403_17@: '[id].chunk.js'@H_403_17@
},plugins@H_403_17@: [
new@H_403_17@ ExtractTextPlugin('[name].css'@H_403_17@)
],devServer@H_403_17@: {
historyApiFallback@H_403_17@: true@H_403_17@,stats@H_403_17@: 'minimal'@H_403_17@
}
});
产品环境配置
config/webpack.prod.js
var@H_403_17@ webpack = require@H_403_17@('webpack'@H_403_17@);
var@H_403_17@ webpackMerge = require@H_403_17@('webpack-merge'@H_403_17@);
var@H_403_17@ ExtractTextPlugin = require@H_403_17@('extract-text-webpack-plugin'@H_403_17@);
var@H_403_17@ commonConfig = require@H_403_17@('./webpack.common.js'@H_403_17@);
var@H_403_17@ helpers = require@H_403_17@('./helpers'@H_403_17@);
const@H_403_17@ ENV = process.env.NODE_ENV = process.env.ENV = 'production'@H_403_17@;
module.exports = webpackMerge(commonConfig,{
devtool: 'source-map'@H_403_17@,output: {
path: helpers.root('dist'@H_403_17@),publicPath: '/'@H_403_17@,filename: '[name].[hash].js'@H_403_17@,chunkFilename: '[id].[hash].chunk.js'@H_403_17@
},htmlLoader: {
minimize: false@H_403_17@ // workaround for ng2@H_403_17@
},plugins: [
new@H_403_17@ webpack.NoErrorsPlugin(),// 如果出现任何错误,就终止构建@H_403_17@
new@H_403_17@ webpack.optimize.DedupePlugin(),// 检测完全相同 ( 以及几乎完全相同 ) 的文件,并把它们从输出中移除@H_403_17@
new@H_403_17@ webpack.optimize.UglifyJsPlugin({ // https://github.com/angular/angular/issues/10618@H_403_17@
// 最小化 (minify) 生成的包@H_403_17@
mangle: {
keep_fnames: true@H_403_17@
}
}),new@H_403_17@ ExtractTextPlugin('[name].[hash].css'@H_403_17@),// 把内嵌的 css 抽取成外部文件,并为其文件名添加“缓存无效哈希”@H_403_17@
new@H_403_17@ webpack.DefinePlugin({ // 用来定义环境变量,以便我们在自己的程序中引用它@H_403_17@
'process.env'@H_403_17@: {
'ENV'@H_403_17@: JSON@H_403_17@.stringify(ENV)
}
})
]
});
(可选)测试环境配置
config/webpack.test.js
var@H_403_17@ helpers = require@H_403_17@('./helpers'@H_403_17@);
module@H_403_17@.exports@H_403_17@ = {
devtool@H_403_17@: 'inline-source-map'@H_403_17@,resolve@H_403_17@: {
extensions@H_403_17@: [''@H_403_17@,'.ts'@H_403_17@,'.js'@H_403_17@]
},module@H_403_17@: {
loaders@H_403_17@: [
{
test@H_403_17@: /\.ts$/@H_403_17@,loaders@H_403_17@: ['awesome-typescript-loader'@H_403_17@,'angular2-template-loader'@H_403_17@]
},{
test@H_403_17@: /\.html$/@H_403_17@,loader@H_403_17@: 'html'@H_403_17@
},{
test@H_403_17@: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico)$/@H_403_17@,loader@H_403_17@: 'null'@H_403_17@
},{
test@H_403_17@: /\.css$/@H_403_17@,exclude@H_403_17@: helpers.root('src'@H_403_17@,include@H_403_17@: helpers.root('src'@H_403_17@,loader@H_403_17@: 'raw'@H_403_17@
}
]
}
}
(可选)Karma单元测试配置
config/karma.conf.js
var@H_403_17@ webpackConfig = require@H_403_17@('./webpack.test'@H_403_17@);
module.exports = function@H_403_17@ (config)@H_403_17@ {@H_403_17@
var@H_403_17@ _config = {
basePath: ''@H_403_17@,frameworks: ['jasmine'@H_403_17@],files: [
{pattern: './config/karma-test-shim.js'@H_403_17@,watched: false@H_403_17@}
],preprocessors: {
'./config/karma-test-shim.js'@H_403_17@: ['webpack'@H_403_17@,'sourcemap'@H_403_17@]
},webpack: webpackConfig,webpackMiddleware: {
stats: 'errors-only'@H_403_17@
},webpackServer: {
noInfo: true@H_403_17@
},reporters: ['progress'@H_403_17@],port: 9876@H_403_17@,colors: true@H_403_17@,logLevel: config.LOG_INFO,autoWatch: false@H_403_17@,browsers: ['PhantomJS'@H_403_17@],singleRun: true@H_403_17@
};
config.set(_config);
};
config/karma-test-shim.js
告诉 Karma 哪些文件需要预加载,首要的是:带有“测试版提供商”的 Angular 测试框架是每个应用都希望预加载的。
Error@H_403_17@.stackTraceLimit = Infinity@H_403_17@;
require@H_403_17@('core-js/es6'@H_403_17@);
require@H_403_17@('core-js/es7/reflect'@H_403_17@);
require@H_403_17@('zone.js/dist/zone'@H_403_17@);
require@H_403_17@('zone.js/dist/long-stack-trace-zone'@H_403_17@);
require@H_403_17@('zone.js/dist/proxy'@H_403_17@);
require@H_403_17@('zone.js/dist/sync-test'@H_403_17@);
require@H_403_17@('zone.js/dist/jasmine-patch'@H_403_17@);
require@H_403_17@('zone.js/dist/async-test'@H_403_17@);
require@H_403_17@('zone.js/dist/fake-async-test'@H_403_17@);
var@H_403_17@ appContext = require@H_403_17@.context('../src'@H_403_17@,true@H_403_17@,/\.spec\.ts/@H_403_17@);
appContext.keys().forEach(appContext);
var@H_403_17@ testing = require@H_403_17@('@angular/core/testing'@H_403_17@);
var@H_403_17@ browser = require@H_403_17@('@angular/platform-browser-dynamic/testing'@H_403_17@);
testing.TestBed.initTestEnvironment(browser.BrowserDynamicTestingModule,browser.platformBrowserDynamicTesting());
step2:编写源码
src/index.html
<!DOCTYPE html>@H_403_17@
<html@H_403_17@>@H_403_17@
<head@H_403_17@>@H_403_17@
<base@H_403_17@ href@H_403_17@="/"@H_403_17@>@H_403_17@
<title@H_403_17@>@H_403_17@Angular With Webpack</title@H_403_17@>@H_403_17@
<Meta@H_403_17@ charset@H_403_17@="UTF-8"@H_403_17@>@H_403_17@
<Meta@H_403_17@ name@H_403_17@="viewport"@H_403_17@ content@H_403_17@="width=device-width,initial-scale=1"@H_403_17@>@H_403_17@
</head@H_403_17@>@H_403_17@
<body@H_403_17@>@H_403_17@
<my-app@H_403_17@>@H_403_17@Loading...</my-app@H_403_17@>@H_403_17@
</body@H_403_17@>@H_403_17@
</html@H_403_17@>@H_403_17@
src/main.ts
import@H_403_17@ { platformBrowserDynamic } from@H_403_17@ '@angular/platform-browser-dynamic'@H_403_17@;
import@H_403_17@ { enableProdMode } from@H_403_17@ '@angular/core'@H_403_17@;
import@H_403_17@ { AppModule } from@H_403_17@ './app/app.module'@H_403_17@;
if@H_403_17@ (process.env.ENV === 'production'@H_403_17@) {
enableProdMode();
}
platformBrowserDynamic().bootstrapModule(AppModule);
public/css/styles.css
body@H_403_17@ { background@H_403_17@: #0147A7@H_403_17@@H_403_17@@H_403_17@; color@H_403_17@: #fff@H_403_17@@H_403_17@@H_403_17@; }@H_403_17@@H_403_17@
src/app/app.component.ts
import@H_403_17@ { Component } from '@angular/core'@H_403_17@;
import@H_403_17@ '../../public/css/styles.css'@H_403_17@;
@Component@H_403_17@({
selector@H_403_17@: 'my-app'@H_403_17@,templateUrl@H_403_17@: './app.component.html'@H_403_17@,styleUrls@H_403_17@: ['./app.component.css'@H_403_17@]
})
export@H_403_17@ class@H_403_17@ AppComponent@H_403_17@ { }@H_403_17@
src/app/app.component.html
这里需要用到Angular Logo,下载后放入”public/images/”目录下。
<main@H_403_17@>@H_403_17@
<h1@H_403_17@>@H_403_17@Hello from Angular App with Webpack</h1@H_403_17@>@H_403_17@
<img@H_403_17@ src@H_403_17@="../../public/images/angular.png"@H_403_17@>@H_403_17@
</main@H_403_17@>@H_403_17@
src/app/app.component.css
main@H_403_17@ { padding@H_403_17@: 1@H_403_17@em@H_403_17@@H_403_17@; font-family@H_403_17@: Arial,Helvetica,sans-serif@H_403_17@@H_403_17@; text-align@H_403_17@: center@H_403_17@@H_403_17@; margin-top@H_403_17@: 50@H_403_17@px@H_403_17@@H_403_17@; display@H_403_17@: block@H_403_17@@H_403_17@; }@H_403_17@@H_403_17@
(用于单元测试,可选)src/app/app.component.spec.ts
import@H_403_17@ { TestBed } from '@angular/core/testing'@H_403_17@;
import@H_403_17@ { AppComponent } from './app.component'@H_403_17@;
describe('App'@H_403_17@,() => { beforeEach(() => { TestBed.configureTestingModule({ declarations: [AppComponent]}); }); it ('should work'@H_403_17@,() => { let@H_403_17@ fixture = TestBed.createComponent(AppComponent); expect(fixture.componentInstance instanceof@H_403_17@ AppComponent).toBe(true@H_403_17@,'should create AppComponent'@H_403_17@); }); })@H_403_17@;@H_403_17@
src/app/app.module.ts
import@H_403_17@ { NgModule } from '@angular/core'@H_403_17@;
import@H_403_17@ { BrowserModule } from '@angular/platform-browser'@H_403_17@;
import@H_403_17@ { AppComponent } from './app.component'@H_403_17@;
@NgModule@H_403_17@({
imports@H_403_17@: [
BrowserModule
],declarations@H_403_17@: [
AppComponent
],bootstrap@H_403_17@: [ AppComponent ]
})
export@H_403_17@ class@H_403_17@ AppModule@H_403_17@ { }@H_403_17@
src/vendor.ts
// Angular@H_403_17@
import@H_403_17@ '@angular/platform-browser'@H_403_17@;
import@H_403_17@ '@angular/platform-browser-dynamic'@H_403_17@;
import@H_403_17@ '@angular/core'@H_403_17@;
import@H_403_17@ '@angular/common'@H_403_17@;
import@H_403_17@ '@angular/http'@H_403_17@;
import@H_403_17@ '@angular/router'@H_403_17@;
// RxJS@H_403_17@
import@H_403_17@ 'rxjs'@H_403_17@;
// Other vendors for example jQuery,Lodash or Bootstrap@H_403_17@
// You can import js,ts,css,sass,...@H_403_17@
src/polyfills.ts
import 'core-js/es6'@H_403_17@;
import 'core-js/es7/reflect'@H_403_17@;
require@H_403_17@('zone.js/dist/zone'@H_403_17@);
if@H_403_17@ (process.env.ENV === 'production'@H_403_17@) {
// Production@H_403_17@
} else@H_403_17@ {
// Development@H_403_17@
Error@H_403_17@['stackTraceLimit'@H_403_17@] = Infinity@H_403_17@;
require@H_403_17@('zone.js/dist/long-stack-trace-zone'@H_403_17@);
}
step3:编译运行
1. 在项目根目录下,执行
webpack@H_403_17@ -@H_403_17@-@H_403_17@progress@H_403_17@ -@H_403_17@-@H_403_17@colors@H_403_17@
(–progress –colors是为了查看进度,可以不用加)
2. 全局安装webpack-dev-server
npm install -g@H_403_17@ webpack-dev@H_403_17@-server@H_403_17@ @H_403_1423@--@H_403_17@verbose
(–verbose可以显示安装详细信息,可以不用加上)
3. 启动webpack server
webpack-dev@H_403_17@-server@H_403_17@ @H_403_1423@--@H_403_17@content-base@H_403_17@ dist@H_403_1423@/@H_403_17@
(设置静态文件访问路径)
step4:查看结果
访问localhost:8080,如果一切顺利,我们能够看到最终的结果:
样例源码已托管至github,如有兴趣可自行clone
git clone@H_403_17@ https://github.com/HalZhan/angular2Demo.git@H_403_17@