Production
此篇文章介绍利用webpack构建线上版本的最佳实践。
The Automatic Way
一般来说,运行webpack -p命令。此命令相当于webpack --optimize-minimize --define process.env.NODE_ENV="'production'"命令。这条指令执行以下操作:
》使用UglifyJsPlugin插件压缩文件
》运行LoaderOptionsPlugin
》设置nodejs的环境变量,以触发特定包的编译
Minification 压缩
webpack自带UglifyJsPlugin插件,它会运行uglifyjs以压缩输出,这个插件支持所有的uglifyjs选项,如果我们在命令行中添加--optimize-minimize选项,下列的插件属性就会被添加:
// webpack.config.js
const webpack = require('webpack');
module.exports = {
/*...*/
plugins:[
new webpack.optimize.UglifyJsPlugin({
sourceMap: options.devtool && (options.devtool.indexOf("sourcemap") >= 0 || options.devtool.indexOf("source-map") >= 0)
})
]
};
Source Maps 资源映射
我们建议你在生产环境将source map使能,它们将有利于调试和测试。webpack可以在bundles中生成内联source map或者单独生成文件。webpack支持7种类型的source maps。其中,cheap-module-source-map是最简单的,其在每一行生成单个映射。
当我们把多个js文件转换合并一个js文件时,线上调试就成了问题,当出错误时,我们无法确定哪里出了问题,source map可以解决这个问题。(https://survivejs.com/webpack/building/source-maps/)
Node Environment Variable Node的环境变量
运行webpack -p 或者--define process.env.NODE_ENV="'production'",将会以下列方式唤醒DefinePlugin插件:
// webpack.config.js
const webpack = require('webpack');
module.exports = {
/*...*/
plugins:[
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('production')
})
]
};
DefinePlugin本质上在源码上执行查找-替换操作,任何在代码中出现的process.env.NODE_ENV被"production"替换。因此,以下代码,if (process.env.NODE_ENV !== 'production') console.log('...')相当于if (false) console.log('...'),然后利用UglifyJS压缩。
从技术上讲,NODE_ENV是nodejs暴露给执行代码的系统环境变量。它通常决定server tools、build scripts、和客户端libary的是开发环境还是生产环境。
可执行的方式
当我们针对不同的环境,执行不同的构建操作时,最简单的方式是针对不同的环境分别写webpack config文件。
最简单的方式是写2个相互独立的config文件
webpack.dev.js
module.exports = {
devtool: 'cheap-module-source-map',
output: {
path: path.join(__dirname, '/../dist/assets'),
filename: '[name].bundle.js',
publicPath: publicPath,
sourceMapFilename: '[name].map'
},
devServer: {
port: 7777,
host: 'localhost',
historyApiFallback: true,
noInfo: false,
stats: 'minimal',
publicPath: publicPath
}
}
webpack.prod.js
module.exports = {
output: {
path: path.join(__dirname, '/../dist/assets'),
filename: '[name].bundle.js',
publicPath: publicPath,
sourceMapFilename: '[name].map'
},
plugins: [
new webpack.LoaderOptionsPlugin({
minimize: true,
debug: false
}),
new webpack.optimize.UglifyJsPlugin({
beautify: false,
mangle: {
screw_ie8: true,
keep_fnames: true
},
compress: {
screw_ie8: true
},
comments: false
})
]
}
然后用以下方式调用
package.json
"scripts": {
...
"build:dev": "webpack --env=dev --progress --profile --colors",
"build:dist": "webpack --env=prod --progress --profile --colors"
}
然后封装webpack.config.js决定哪个配置文件被执行
webpack.config.js
module.exports = function(env) {
return require(`./webpack.${env}.js`)
}
高级技巧
一个更复杂的方式是,将开发环境和生产环境共有的配置写进一个基础配置文件,然后,将基础配置文件与特定环境下的文件进行合并,这样做的好处,两个配置文件不会有重复代码。
合并配置文件的工具是webpack-merge,它提供了许多合并选项。
webpack.common.js
module.exports = {
entry: {
'polyfills': './src/polyfills.ts',
'vendor': './src/vendor.ts',
'main': './src/main.ts'
},
output: {
path: path.join(__dirname, '/../dist/assets'),
filename: '[name].bundle.js',
publicPath: publicPath,
sourceMapFilename: '[name].map'
},
resolve: {
extensions: ['.ts', '.js', '.json'],
modules: [path.join(__dirname, 'src'), 'node_modules']
},
module: {
rules: [
{
test: /\.ts$/,
exclude: [/\.(spec|e2e)\.ts$/],
use: [
'awesome-typescript-loader',
'angular2-template-loader'
]
},
{
test: /\.css$/,
use: ['to-string-loader', 'css-loader']
},
{
test: /\.(jpg|png|gif)$/,
use: 'file-loader'
},
{
test: /\.(woff|woff2|eot|ttf|svg)$/,
use: {
loader: 'url-loader',
options: {
limit: 100000
}
}
}
]
},
plugins: [
new ForkCheckerPlugin(),
new webpack.optimize.CommonsChunkPlugin({
name: ['polyfills', 'vendor'].reverse()
}),
new HtmlWebpackPlugin({
template: 'src/index.html',
chunksSortMode: 'dependency'
})
]
}
然后生产环境配置文件如下
webpack.prod.js
const Merge = require('webpack-merge');
const CommonConfig = require('./webpack.common.js');
module.exports = Merge(CommonConfig, {
plugins: [
new webpack.LoaderOptionsPlugin({
minimize: true,
debug: false
}),
new webpack.DefinePlugin({
'process.env': {
'NODE_ENV': JSON.stringify('production')
}
}),
new webpack.optimize.UglifyJsPlugin({
beautify: false,
mangle: {
screw_ie8: true,
keep_fnames: true
},
compress: {
screw_ie8: true
},
comments: false
})
]
})
可以看到webpack.prod.js文件有以下3个变化:
》利用webpack-merge工具合并base文件
》将共有的配置移动到了common文件
》在webpack.prod.js中利用DefinePlugin插件设置来环境变量为‘production’