Angular2是Google下一代MV*框架括丁,用于構(gòu)建復(fù)雜瀏覽器應(yīng)用。Angular2給WEB或移動Apps帶來了一攬子解決方案伶选,從模板渲染到數(shù)據(jù)綁定史飞,從Http服務(wù)到表單處理等等,總之仰税,你想要的构资,Angular2基本上都已封裝好了。
萬事始于Hello World陨簇,不論Angular2怎么牛吐绵,咱們先得把官方的例子跑起來再說。這里使用基于webpack創(chuàng)建Angular應(yīng)用河绽。
環(huán)境準備
node v5.x.x
npm v3.x.x
step1:創(chuàng)建并配置本項目
創(chuàng)建項目目錄:
mkdir angular2Demo
cd angular2Demo
創(chuàng)建配置文件
典型的 Angular 項目需要一系列配置文件
package.json 用來標(biāo)記出本項目所需的 npm 依賴包拦赠。
tsconfig.json 定義了 TypeScript 編譯器如何從項目源文件生成 JavaScript 代碼。
typings.json 為那些 TypeScript 編譯器無法識別的庫提供了額外的定義文件葵姥。
webpack.config.js為構(gòu)建Angular應(yīng)用所進行的一系列webpack配置。
package.json
{
"name": "angular2demo",
"version": "1.0.0",
"description": "Angular 2 demo.",
"main": "index.js",
"scripts": {
"start": "webpack-dev-server --inline --progress --port 8080",
"test": "karma start",
"build": "rimraf dist && webpack --config config/webpack.prod.js --progress --profile --bail",
"postinstall": "typings install"
},
"dependencies": {
"@angular/common": "2.0.0",
"@angular/compiler": "2.0.0",
"@angular/core": "2.0.0",
"@angular/forms": "2.0.0",
"@angular/http": "2.0.0",
"@angular/platform-browser": "2.0.0",
"@angular/platform-browser-dynamic": "2.0.0",
"@angular/router": "3.0.0",
"core-js": "^2.4.1",
"rxjs": "5.0.0-beta.12",
"zone.js": "^0.6.23"
},
"devDependencies": {
"angular2-template-loader": "^0.4.0",
"awesome-typescript-loader": "^2.2.4",
"css-loader": "^0.23.1",
"extract-text-webpack-plugin": "^1.0.1",
"file-loader": "^0.8.5",
"html-loader": "^0.4.3",
"html-webpack-plugin": "^2.15.0",
"jasmine-core": "^2.4.1",
"karma": "^1.2.0",
"karma-jasmine": "^1.0.2",
"karma-phantomjs-launcher": "^1.0.2",
"karma-sourcemap-loader": "^0.3.7",
"karma-webpack": "^1.8.0",
"null-loader": "^0.1.1",
"phantomjs-prebuilt": "^2.1.7",
"raw-loader": "^0.5.1",
"rimraf": "^2.5.2",
"style-loader": "^0.13.1",
"typescript": "^2.0.2",
"typings": "^1.3.2",
"webpack": "^1.13.0",
"webpack-dev-server": "^1.14.1",
"webpack-merge": "^0.14.0"
},
"repository": {
"type": "git",
"url": "git+https://github.com/HalZhan/angular2Demo.git"
},
"author": "halzhan",
"license": "MIT",
"bugs": {
"url": "https://github.com/HalZhan/angular2Demo/issues"
},
"homepage": "https://github.com/HalZhan/angular2Demo#readme"
}
tsconfig.json
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"moduleResolution": "node",
"sourceMap": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"removeComments": false,
"noImplicitAny": true,
"suppressImplicitAnyIndexErrors": true
}
}
typings.json
{
"globalDependencies": {
"core-js": "registry:dt/core-js#0.0.0+20160725163759",
"jasmine": "registry:dt/jasmine#2.2.0+20160621224255",
"node": "registry:dt/node#6.0.0+20160909174046"
}
}
webpack.config.js
module.exports = require('./config/webpack.dev.js'); // 待補充
karma.conf.js
module.exports = require('./config/karma.conf.js'); // 待補充
下面我們繼續(xù)完善配置文件句携。
公共配置
我們可以為開發(fā)榔幸、產(chǎn)品和測試環(huán)境定義分別各自的配置文件。 但三者總會有一些公共配置矮嫉。 于是我們把那些公共的配置收集到一個名叫 webpack.common.js 的獨立文件中削咆。
config/webpack.common.js
var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var helpers = require('./helpers');
module.exports = {
entry: {
'polyfills': './src/polyfills.ts', // 運行Angular時所需的一些標(biāo)準js
'vendor': './src/vendor.ts', // Angular、Lodash蠢笋、bootstrap.css......
'app': './src/main.ts' // 應(yīng)用代碼
},
resolve: {
extensions: ['', '.js', '.ts'] // 加載的文件類型(明確的擴展名拨齐、.js、.ts)
},
module: {
loaders: [
{
test: /\.ts$/,
loaders: ['awesome-typescript-loader',
'angular2-template-loader' // 用于加載 Angular 組件的模板和樣式
]
}, // ts - 將typescript代碼轉(zhuǎn)譯成es5的加載器昨寞,由tsconfig.json文件指導(dǎo)
{
test: /\.html$/,
loader: 'html' // 為組件模板準備的加載器
},
{
test: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico)$/,
loader: 'file?name=assets/[name].[hash].[ext]' // 圖片和字體文件也能被打包
},
{
test: /\.css$/,
exclude: helpers.root('src', 'app'),
loader: ExtractTextPlugin.extract('style', 'css?sourceMap')
}, // 模式匹配應(yīng)用級樣式
{
test: /\.css$/,
include: helpers.root('src', 'app'),
loader: 'raw'
} // 模式匹配組件局部樣式 ( 在組件元數(shù)據(jù)的 styleUrls 屬性中指定的那些 )
]
},
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: ['app', 'vendor', 'polyfills']
}), // 提取公共代碼
new HtmlWebpackPlugin({
template: 'src/index.html'
}) // 自動向目標(biāo).html文件注入script和link標(biāo)簽
]
};
config/helpers.js
var path = require('path');
var _root = path.resolve(__dirname, '..');
function root(args) {
args = Array.prototype.slice.call(arguments, 0);
return path.join.apply(path, [_root].concat(args));
}
exports.root = root;
開發(fā)環(huán)境配置
config/webpack.dev.js
var webpackMerge = require('webpack-merge');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var commonConfig = require('./webpack.common.js');
var helpers = require('./helpers');
module.exports = webpackMerge(commonConfig, {
devtool: 'cheap-module-eval-source-map',
output: {
path: helpers.root('dist'),
publicPath: 'http://localhost:8080/',
filename: '[name].js',
chunkFilename: '[id].chunk.js'
},
plugins: [
new ExtractTextPlugin('[name].css')
],
devServer: {
historyApiFallback: true,
stats: 'minimal'
}
});
產(chǎn)品環(huán)境配置
config/webpack.prod.js
var webpack = require('webpack');
var webpackMerge = require('webpack-merge');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var commonConfig = require('./webpack.common.js');
var helpers = require('./helpers');
const ENV = process.env.NODE_ENV = process.env.ENV = 'production';
module.exports = webpackMerge(commonConfig, {
devtool: 'source-map',
output: {
path: helpers.root('dist'),
publicPath: '/',
filename: '[name].[hash].js',
chunkFilename: '[id].[hash].chunk.js'
},
htmlLoader: {
minimize: false // workaround for ng2
},
plugins: [
new webpack.NoErrorsPlugin(), // 如果出現(xiàn)任何錯誤瞻惋,就終止構(gòu)建
new webpack.optimize.DedupePlugin(), // 檢測完全相同 ( 以及幾乎完全相同 ) 的文件厦滤,并把它們從輸出中移除
new webpack.optimize.UglifyJsPlugin({ // https://github.com/angular/angular/issues/10618
// 最小化 (minify) 生成的包
mangle: {
keep_fnames: true
}
}),
new ExtractTextPlugin('[name].[hash].css'), // 把內(nèi)嵌的 css 抽取成外部文件,并為其文件名添加“緩存無效哈霞呃牵”
new webpack.DefinePlugin({ // 用來定義環(huán)境變量掏导,以便我們在自己的程序中引用它
'process.env': {
'ENV': JSON.stringify(ENV)
}
})
]
});
(可選)測試環(huán)境配置
config/webpack.test.js
var helpers = require('./helpers');
module.exports = {
devtool: 'inline-source-map',
resolve: {
extensions: ['', '.ts', '.js']
},
module: {
loaders: [
{
test: /\.ts$/,
loaders: ['awesome-typescript-loader', 'angular2-template-loader']
},
{
test: /\.html$/,
loader: 'html'
},
{
test: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico)$/,
loader: 'null'
},
{
test: /\.css$/,
exclude: helpers.root('src', 'app'),
loader: 'null'
},
{
test: /\.css$/,
include: helpers.root('src', 'app'),
loader: 'raw'
}
]
}
}
(可選)Karma單元測試配置
config/karma.conf.js
var webpackConfig = require('./webpack.test');
module.exports = function (config) {
var _config = {
basePath: '',
frameworks: ['jasmine'],
files: [
{pattern: './config/karma-test-shim.js', watched: false}
],
preprocessors: {
'./config/karma-test-shim.js': ['webpack', 'sourcemap']
},
webpack: webpackConfig,
webpackMiddleware: {
stats: 'errors-only'
},
webpackServer: {
noInfo: true
},
reporters: ['progress'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: false,
browsers: ['PhantomJS'],
singleRun: true
};
config.set(_config);
};
config/karma-test-shim.js
告訴 Karma 哪些文件需要預(yù)加載,首要的是:帶有“測試版提供商”的 Angular 測試框架是每個應(yīng)用都希望預(yù)加載的羽峰。
Error.stackTraceLimit = Infinity;
require('core-js/es6');
require('core-js/es7/reflect');
require('zone.js/dist/zone');
require('zone.js/dist/long-stack-trace-zone');
require('zone.js/dist/proxy');
require('zone.js/dist/sync-test');
require('zone.js/dist/jasmine-patch');
require('zone.js/dist/async-test');
require('zone.js/dist/fake-async-test');
var appContext = require.context('../src', true, /\.spec\.ts/);
appContext.keys().forEach(appContext);
var testing = require('@angular/core/testing');
var browser = require('@angular/platform-browser-dynamic/testing');
testing.TestBed.initTestEnvironment(browser.BrowserDynamicTestingModule, browser.platformBrowserDynamicTesting());
step2:編寫源碼
src/index.html
<!DOCTYPE html>
<html>
<head>
<base href="/">
<title>Angular With Webpack</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<my-app>Loading...</my-app>
</body>
</html>
src/main.ts
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { enableProdMode } from '@angular/core';
import { AppModule } from './app/app.module';
if (process.env.ENV === 'production') {
enableProdMode();
}
platformBrowserDynamic().bootstrapModule(AppModule);
public/css/styles.css
body {
background: #0147A7;
color: #fff;
}
src/app/app.component.ts
import { Component } from '@angular/core';
import '../../public/css/styles.css';
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent { }
src/app/app.component.html
這里需要用到Angular Logo趟咆,下載后放入"public/images/"目錄下。
<main>
<h1>Hello from Angular App with Webpack</h1>
<img src="../../public/images/angular.png">
</main>
src/app/app.component.css
main {
padding: 1em;
font-family: Arial, Helvetica, sans-serif;
text-align: center;
margin-top: 50px;
display: block;
}
(用于單元測試梅屉,可選)src/app/app.component.spec.ts
import { TestBed } from '@angular/core/testing';
import { AppComponent } from './app.component';
describe('App', () => {
beforeEach(() => {
TestBed.configureTestingModule({ declarations: [AppComponent]});
});
it ('should work', () => {
let fixture = TestBed.createComponent(AppComponent);
expect(fixture.componentInstance instanceof AppComponent).toBe(true, 'should create AppComponent');
});
});
src/app/app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
@NgModule({
imports: [
BrowserModule
],
declarations: [
AppComponent
],
bootstrap: [ AppComponent ]
})
export class AppModule { }
src/vendor.ts
// Angular
import '@angular/platform-browser';
import '@angular/platform-browser-dynamic';
import '@angular/core';
import '@angular/common';
import '@angular/http';
import '@angular/router';
// RxJS
import 'rxjs';
// Other vendors for example jQuery, Lodash or Bootstrap
// You can import js, ts, css, sass, ...
src/polyfills.ts
import 'core-js/es6';
import 'core-js/es7/reflect';
require('zone.js/dist/zone');
if (process.env.ENV === 'production') {
// Production
} else {
// Development
Error['stackTraceLimit'] = Infinity;
require('zone.js/dist/long-stack-trace-zone');
}
step3:編譯運行
- 在項目根目錄下值纱,執(zhí)行
webpack --progress --colors
(--progress --colors是為了查看進度,可以不用加)
- 全局安裝webpack-dev-server
npm install -g webpack-dev-server --verbose
(--verbose可以顯示安裝詳細信息坯汤,可以不用加上)
- 啟動webpack server
webpack-dev-server --content-base dist/
(設(shè)置靜態(tài)文件訪問路徑)
step4:查看結(jié)果
訪問localhost:8080虐唠,如果一切順利,我們能夠看到最終的結(jié)果:
Bingo !
樣例源碼已托管至github玫霎,如有興趣可自行clone
git clone https://github.com/HalZhan/angular2Demo.git