为什么我们要做三份 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(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。