采用Webpack构建Angular应用程序以及AoT支持
Angular支持多种代码打包方式,例如:官网示列使用的System.js,还有特别好使的Rollup,目前火的不行的Webpack,还有略显过时的Browserify等等。不过打包工具就是一个工具而已,并没有什么好坏之分,只是不同的应用场景是不适合而已。
作为一个从Angular发布就一直使用至今的玩家,今天记录一下如何使用Webpack来构建Angular项目。之前一直使用的System.js(dev)和rollup(prod)来构建项目。后来由于项目不断的扩大,rollup由于目前还不支持代码分割,打出来的包也越来越大。所以决定将打包工具迁移到webpack工具链上来。这里分享一下如何简单快速的搭建基于webpack的脚手架。
PS:虽然官网提供的Angular-cli使用起来特别方便。但是,作为一个不知道底层实现细节就浑身不舒服的开发,还是喜欢自己折腾一下,这个也是为什么想要记录这篇笔记的初衷。当然,工程项目级别还是会优先考虑工期和稳定性采用最为合适的方式。
先构建一个典型的Angular应用的结构
先来看看一个基础示列的代码结构:
├── index.html
├── package.json
├── src
│ ├── app
│ │ ├── app.component.css
│ │ ├── app.component.html
│ │ ├── app.component.ts
│ │ ├── app.module.ts
│ │ └── home
│ │ ├── home.component.html
│ │ └── home.component.ts
│ └── main.ts
├── tsconfig.json
└── webpack.config.js
目录结构可以依据个人口味进行修改,这里只是为了记录知识点,方便理解使用。
nodejs项目的package.json文件用户自定的脚本部分:
"scripts": {
"build": "webpack --config webpack.config.js","build:prod": "webpack --config webpack.config.js --env.MODE=prod"
},
来看看webpack如何配置的(常规版):
module.exports = {
entry: {
main: './src/main.ts'
},output: {
path: './dist',filename: '[name].bundle.js',},resolve: {
extensions: ['.ts','.js','.html'],module: {
rules: [
{ test: /\.html$/,loader: 'raw' },{ test: /\.css$/,{ test: /\.ts/,loaders: ['awesome-typescript-loader','angular2-template-loader'] }
]
},devtool: '#source-map',};
webpack的安装这里就不介绍,最好的学习地方自然是去其官网。
如何区别开发环境和生产环境打包部署,还是直接看看webpack的配置文件吧:
const webpack = require('webpack');
module.exports = (envOptions) => {
envOptions = envOptions || {};
const config = {
entry: {
main: './src/main.ts'
},output: {
path: './dist',resolve: {
extensions: ['.ts',module: {
rules: [
{ test: /\.html$/,'angular2-template-loader'] }
]
},};
if (envOptions.MODE === 'prod') {
config.plugins = [
new webpack.optimize.UglifyJsPlugin({
beautify: false,mangle: {
screw_ie8: true,keep_fnames: true
},compress: {
warnings: false,screw_ie8: true
},comments: false
}),];
}
return config;
};
从上面的配置文件我们可以看出,webpack+angular构建项目真的出奇的简单。只需要借助特定的loader就可以很快的搭建起一个基本的脚手架。
对Angular应用进行AoT,提升应用加载速度和性能
Angualr官网中给出了如何结合Rollup对我们编写Angular应用进行AoT,这里我们还是基于官网的示列简单的变更一下就可以搭建出来我们自己的实现。
细心的童鞋会发现,Angular-cli中默认也是使用webpack进行项目构建的,只是其隐藏了底层的实现细节。仔细拆解其实现,我们会发现在AoT方面,Angular-cli底层使用了一个Webpack的插件来实现AoT,这个插件就是@ngtools/webpack。
首先我们来安装@ngtools/webpack,预编译还需要借助angualr官方的compiler-cli模块。
npm install -D @angular/compiler-cli @ngtools/webpack
const webpack = require('webpack');
const AotPlugin = require('@ngtools/webpack').AotPlugin;
module.exports = (envOptions) => {
envOptions = envOptions || {};
const config = {
entry: {
main: './src/main.ts'
},]
},};
if (envOptions.MODE === 'prod') {
config.module.rules.push(
{ test: /\.ts$/,loaders: ['@ngtools/webpack'] }
);
config.plugins = [
new AotPlugin({
tsConfigPath: './tsconfig.aot.json',entryModule: 'src/app/app.module#AppModule'
}),new webpack.optimize.UglifyJsPlugin({
beautify: false,];
} else {
config.module.rules.push(
{ test: /\.ts$/,'angular2-template-loader'] }
);
}
return config;
};
其中AotPlugin插件的两个参数进行说明一下:
需要注意的一个点,为了实现AoT,我们还需要对应的修改一个我们的Typtscript配置文件。这里我们还是直接上代码直观一些:
{
"compilerOptions": { "target": "es5","module": "es2015","moduleResolution": "node","emitDecoratorMetadata": true,"experimentalDecorators": true,"allowSyntheticDefaultImports": true,"sourceMap": true,"noEmit": true,"noEmitHelpers": true,"importHelpers": true,"strictNullChecks": false,"lib": [ "es2015","dom" ],"baseUrl": ".","paths": { "@angular/*": ["node_modules/@angular/*"] },"typeRoots": [ "node_modules/@types" ],"types": [ "hammerjs","node" ] },"exclude": [ "node_modules","dist","src/**/*.spec.ts","src/**/*.e2e.ts" ],"compileOnSave": false,"buildOnSave": false,"awesomeTypescriptLoaderOptions": { "forkChecker": true,"useWebpackText": true },"angularCompilerOptions": { "genDir": "aot/","skipMetadataEmit": true } }
我们在对比评级的tsconfig.json文件时,多出来了如下配置项:
"angularCompilerOptions": {
"genDir": "aot/","skipMetadataEmit": true
}
到这里我们就搭建起来一个简单的Angular+Webpack进行构建的项目脚手架。
后记
关于如何使用sass进行样式文件编写,使用postcss+autoprefixer进行样式完善方面,webpack的配置同正常的普通项目没有区别。但是需要注意的一个点是,在Angular组件中的html文件中的资源引用需要使用webpack的resolve字段进行别名配置,例如:
resolve: {
extensions: ['.ts','.js'],alias: {
assets: helpers.root('assets')
}
}
通过上面的配置。我们在app.component.html文件中引用资源文件时可以如下编写:
<img src="~assets/img.png" />
这里再啰嗦一下,贴上helpers.js的实现代码:
const path = require('path');
const _root = path.resolve(__dirname,'..');
exports.root = function (args) {
return path.join.apply(path,[_root].concat(Array.from(args)))
};