为什么我们要做三份 Webpack 配置文件
在知乎上我们常常会看到有同学发问:BAT等大型网站的前端工程是如何组织管理的?这的确是一个可以发散的很广的Q&A,我想如果要我回答这个问题,不如先从Webpack配置说起。
时至今日,Webpack已经成为前端工程必备的基础工具之一,不仅被广泛用于前端工程发布前的打包,还在开发中担当本地前端资源服务器(assetsserver)、模块热更新(hotmodulereplacement)、APIProxy等角色,结合ESLint等代码检查工具,还可以实现在对源代码的严格校验检查。
正如上文中提到的,前端从开发到部署前都离不开Webpack的参与,而Webpack的默认配置文件只有一个,即webpack.config.js,那么问题来了,开发期和部署前应该使用同一份Webpack配置吗?答案肯定是否定的,既然webpack.config.js是一个JS文件,我们当然可以在文件里写JavaScript业务逻辑,通过读取环境变量NODE_ENV来判断当前是在开发(dev)时还是最终的生产环境(production),然而很多同学习惯把这两者的配置都混写在根目录下的webpack.config.js,通过很多零散的if...else来“临时”决定某一个plugin或者某一个loader的配置项,随着loaders和plugins的不断增加,久而久之webpack.config.js变得原来越隆长,代码的可读性和可维护性也大大下降。
我想通过本文来介绍一种用3个JS文件来配置Webpack的方法,这里借鉴了很多开源项目的配置,同时也结合了我们自己在开发中碰到的种种问题解决方案。
本文中提及的配置基于Webpack2或以上,建议使用3.0及以上版本
开发环境与生产环境的区别
开发环境
·NODE_ENV为development
·启用模块热更新(hotmodulereplacement)
·额外的webpack-dev-server配置项,APIProxy配置项
·输出Sourcemap
生产环境
·NODE_ENV为production
·将React、jQuery等常用库设置为external,直接采用CDN线上的版本
·样式源文件(如css、less、scss等)需要通过ExtractTextPlugin独立抽取成css文件
·启用post-css
·启用optimize-minimize(如uglify等)
·中大型的商业网站生产环境下,是绝对不能有console.log()的,所以要为babel配置Removeconsoletransform
这里需要说明的是因为开发环境下启用了hotmodulereplacement,为了让样式源文件的修改也同样能被热替换,不能使用ExtractTextPlugin,而转为随JSBundle一起输出。
你需要三份配置文件
1.webpack.base.config.js
在base文件里,你需要将开发环境和生产环境中通用的配置集中放在这里:
constCleanWebpackPlugin=require('clean-webpack-plugin');
constpath=require('path');
constwebpack=require('webpack');
//配置常量
//源代码的根目录(本地物理文件路径)
constSRC_PATH=path.resolve('./src');
//打包后的资源根目录(本地物理文件路径)
constASSETS_BUILD_PATH=path.resolve('./build');
//资源根目录(可以是CDN上的绝对路径,或相对路径)
constASSETS_PUBLIC_PATH='/assets/';
module.exports={
context:SRC_PATH,//设置源代码的默认根路径
resolve:{
extensions:['.js','.jsx']//同时支持js和jsx
},
entry:{
//注意entry中的路径都是相对于SRC_PATH的路径
vendor:'./vendor',
a:['./entry-a'],
b:['./entry-b'],
c:['./entry-c']
},
output:{
path:ASSETS_BUILD_PATH,
publicPath:ASSETS_PUBLIC_PATH,
filename:'./[name].js'
},
module:{
rules:[
{
enforce:'pre',//ESLint优先级高于其他JS相关的loader
test:/\.jsx?$/,
exclude:/node_modules/,
loader:'eslint-loader'
},
{
test:/\.jsx?$/,
exclude:/node_modules/,
//建议把babel的运行时配置放在.babelrc里,从而与eslint-loader等共享配置
loader:'babel-loader'
},
{
test:/\.(png|jpg|gif)$/,
use:
[
{
loader:'url-loader',
options:
{
limit:8192,
name:'images/[name].[ext]'
}
}
]
},
{
test:/\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
use:
[
{
loader:'url-loader',
options:
{
limit:8192,
mimetype:'application/font-woff',
name:'fonts/[name].[ext]'
}
}
]
},
{
test:/\.(ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
use:
[
{
loader:'file-loader',
options:
{
limit:8192,
mimetype:'application/font-woff',
name:'fonts/[name].[ext]'
}
}
]
}
]
},
plugins:[
//每次打包前,先清空原来目录中的内容
newCleanWebpackPlugin([ASSETS_BUILD_PATH],{verbose:false}),
//启用CommonChunkPlugin
newwebpack.optimize.CommonsChunkPlugin({
names:'vendor',
minChunks:Infinity
})
]
};
2.webpack.dev.config.js
这是用于开发环境的Webpack配置,继承自base:
constwebpack=require('webpack');
//读取同一目录下的baseconfig
constconfig=require('./webpack.base.config');
//添加webpack-dev-server相关的配置项
config.devServer={
contentBase:'./',
publicPath:'/assets/'
};
//有关Webpack的API本地代理,另请参考https://webpack.github.io/docs/webpack-dev-server.html#proxy
config.module.rules.push(
{
test:/\.less$/,
use:[
'style-loader',
'css-loader',
'less-loader'
],
exclude:/node_modules/
}
);
//真实场景中,React、jQuery等优先走全站的CDN,所以要放在externals中
config.externals={
react:'React',
'react-dom':'ReactDOM'
};
//添加Sourcemap支持
config.plugins.push(
newwebpack.SourceMapDevToolPlugin({
filename:'[file].map',
exclude:['vendor.js']//vendor通常不需要sourcemap
})
);
//Hotmodulereplacement
Object.keys(config.entry).forEach((key)=>{
//这里有一个私有的约定,如果entry是一个数组,则证明它需要被hotmodulereplace
if(Array.isArray(config.entry[key])){
config.entry[key].unshift(
'webpack-dev-server/client?http://0.0.0.0:8080',
'webpack/hot/only-dev-server'
);
}
});
config.plugins.push(
newwebpack.HotModuleReplacementPlugin()
);
module.exports=config;
3.webpack.config.js
这是用于生产环境的webpack配置,同样继承自base:
constwebpack=require('webpack');
constExtractTextPlugin=require('extract-text-webpack-plugin');
//读取同一目录下的baseconfig
constconfig=require('./webpack.base.config');
config.module.rules.push(
{
test:/\.less$/,
use:ExtractTextPlugin.extract(
{
use:[
'css-loader',
'less-loader'
],
fallback:'style-loader'
}
),
exclude:/node_modules/
}
);
config.plugins.push(
//官方文档推荐使用下面的插件确保NODE_ENV
newwebpack.DefinePlugin({
'process.env.NODE_ENV':JSON.stringify(process.env.NODE_ENV||'production')
}),
//启动minify
newwebpack.LoaderOptionsPlugin({minimize:true}),
//抽取CSS文件
newExtractTextPlugin({
filename:'[name].css',
allChunks:true,
ignoreOrder:true
})
);
module.exports=config;
现在在你的工程文件夹里应该已经有三个Webpack配置文件,它们分别是:
·webpack.base.config.js
·webpack.dev.config.js
·webpack.config.js
最后,你还需要在package.json里添加相应的配置:
{
...
"scripts":{
"build":"webpack--optimize-minimize",
"dev":"webpack-dev-server--configwebpack.dev.config.js",
"start":"npmrundev"//或添加你自己的start逻辑
},
...
}
和很多项目一样,在开发环境下的时候,你需要使用npmrundev来启动,而在生产环境中,则用npmrunbuild来发布。
题外话,在真实场景中,我们不会直接使用webpack-dev-server,而采用express+webpack/webpack-dev-middleware,配置方法与上面所述的完全相同。
关于专栏
如果你喜欢这篇文章,就请关注我的专栏《前端零栈》,在这里我们一起聊一聊前端技术和前端工程。
关于作者
Henry,10岁开始学习计算机编程,高二暑假获得江苏省青少年信息奥林匹克一等奖。2000年开始自学JavaScript及网页制作,2006年起正式开始从事前端开发工作,从此一干就是10多年。加入阿里巴巴前,曾在SAP中国研究院担任智慧交通大数据产品经理。
Github:MagicCube(HenryLi)
小结
前端从开发到部署前都离不开Webpack的参与,本文结合了我们自己在开发中碰到的种种问题解决方案,同时借鉴了很多开源项目的配置来介绍一种用3个JS文件来配置Webpack的方法。关于本文如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对毛票票网站的支持!
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。