以前有用 webpack4与babel7改造基于vue-cli2生成的工程模板,介绍文章在此。以后经过一些实践,除去了cli工具相对复杂的配置结构,提供轻量化版本的配置方案。之因此说是轻量化,是相对于Vue、React等框架提供的官方cli工具而言的。并非说这些cli工具很差,它们自己提供了开箱即用的良好特性,又集成了不少提高开发体验的插件,确实能下降框架使用的门槛。但也正是由于工具高度集成,配置高度抽象,致使生成的webpack配置文件结构略显复杂。对于一个技术选型已趋稳定的前端团队来讲,好比通常只会使用一种css预处理方案,以及相对固定的插件集成,因此彻底能够固定某些配置选项,从而输出一个轻量的工程配置方案。这也有利于灵活更改或升级某些依赖,从而进一步提高开发体验及输出性能。javascript
在开始配置以前,仍是要明确自身的需求。对于前端应用来讲,使用webpack做为工程化工具,咱们但愿除了静态文件server,babel编译等基本需求外,还能知足如下这些体验及要求:css
本文是以Vue框架应用为例,应用示例用的就是vue-cli生成的工程模板,能够clone 代码仓库 到本地运行。React技术栈的同窗也请留步,webpack的配置方案基本是一致的,无非就是babel配置有差别。简化后的方案只在build
目录下有3个配置文件:webpack.base.js
, webpack.dev.js
, webpack.prod.js
。其中 dev
与prod
分别是开发和生产构建的webpack config文件,在package.json
中能够配置以下npm scripts用于开发和生产构建:html
"scripts": { "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.js", "build": "cross-env NODE_ENV=production webpack --config build/webpack.prod.js" }
两个文件都继承了webpack.base.js
,此文件包含了主要配置项,下面就方案的关键配置作下注解。前端
devMode
变量用于区分开发与生产构建resolve
方法用于拼接绝对路径,base目录为工程根目录const devMode = process.env.NODE_ENV !== 'production'; function resolve (dir) { return path.join(__dirname, '..', dir) }
src
目录下的 main.js
,此处路径相对于上下文。能够视项目实际状况配置多个入口。context: resolve(''), entry: { app: './src/main.js' }
path
指定输出目录为dist
,全部构建生成的资源都放入此目录filename
命名输出的bundle文件,这里根据devMode区分,生产构建会加上对应bundle的chunkhash
,这里不用 hash
,是须要精确控制每一个bundle的缓存,以知足咱们对于生产环境加载优化的需求。尤为是多入口的状况下,若是只更新其中一个业务bundle,其余bundle的hash不变,从而长效利用缓存,减小没必要要的资源下载。 [name]
会被入口的命名替换,该项目即为app
。chunkFilename
命名输出的chunk文件,上面的filename
是用来控制入口对应的bundle文件,而这个主要是用于分包(splitChunks)以及懒加载等产生的chunk文件,也使用chunkhash
。 [name]
会被分包定义的名称替换。js
,后面的 css、图片等资源也对应加上了目录,这能够根据项目规范来定,没有强制约定。不论是否有前置目录,都是相对于 output.path
目录的。publicPath
也是一个关键配置,会影响到按需加载或外部资源加载,特别是css中引用的图片、字体等资源,若是设置不正确,有可能致使没法加载,受限篇幅与主题,就不展开讲了。output: { path: resolve('dist'), filename: devMode ? 'js/[name].js' : 'js/[name].[chunkhash].js', chunkFilename: devMode ? 'js/[name].js' : 'js/[name].[chunkhash].js', publicPath: '/', }
主要设置模块如何被解析,包括别名配置,能够提高必定的构建效率,具体看配置,不作展开。vue
根据项目规模和资源加载性能要求进行合理的分包配置,其中涉及到的参数与原理都会相对复杂,本文也不作展开。本方案里给到的是能够知足中小型应用的通用策略:分出 common
包与vendor
包。java
common
包是针对于多入口应用的,本示例工程其实不起效。对于多入口应用,提取共同依赖模块到common
包,能够减少每一个业务bundle的size,同时也能利用缓存的优点,优化加载性能。还能够经过minChunks
等参数进一步控制分包粒度。vendor
包是将全部在node_modules
中的第三方模块所有打包成一个chunk。这样作的一个好处是,相对于业务bundle,第三方依赖的变化频度较低,chunkhash
能够稳定较长时间。但若是你的项目持续集成是每次从新全量安装构建,那此策略的效果不会很好,由于npm依赖更新频度过高了(主要是minor和patch版本升级,除非是锁定版本)。若是单个包过大,也能够经过maxSize
等参数进一步拆分。optimization: { splitChunks: { chunks: 'async', name: true, cacheGroups: { common: { name: 'common', chunks: 'initial', minChunks: 2 }, vendor: { name: 'vendor', test: /[\\/]node_modules[\\/]/, chunks: 'all' } } } }
模块处理规则配置,使用恰当的loader处理各类模块。node
vue-loader
处理.vue
文件 。loader的配置基本取自vue-cli
,具体看源码,须要详细了解的能够查看官方文档 babel-loader
进行处理,babel的选项能够在.babelrc
或package.json
中的babel
节点单独配置。注意一下 userBuiltIns
及corejs
的配置,因为babel编译配置不是本文重点,也再也不展开。"presets": [ [ "@babel/preset-env", { "modules": false, "targets": { "browsers": [ "> 1%", "last 2 versions", "not ie <= 8" ] }, "useBuiltIns": "usage", "corejs": 3 } ] ],
test
及预处理 loader,而后是css-loader
,最后是用MiniCssExtractPlugin.loader
将代码提取到css文件中,需配合下面的plugins
配置。生成的css文件命名规则与output
配置相似,注意生产构建用的是该插件提供的contenthash
,也是利用缓存优化。mini-css-extract-plugin
已经支持了内置 HMR,开发阶段启用hmr
option便可,在这以前还须要使用css-hot-loader
来配合实现css的 HMR。其原理也比较简单, 就是经过从新加载生成的css文件进行样式覆盖。另外,若是是纯前端应用(见下节解释),开发阶段也能够考虑使用style-loader
来实现 HMR。{ test: /\.(sa|sc|c)ss$/, use: [ { loader: MiniCssExtractPlugin.loader, options: { hmr: devMode, }, }, 'css-loader', // 'postcss-loader', // 'sass-loader', ], },
url-loader
处理,配置limit
选项可优化请求数,另外命名直接带上hash
。vue-loader/lib/plugin
。MiniCssExtractPlugin({ filename: devMode ? 'css/[name].css' : 'css/[name].[contenthash].css', chunkFilename: devMode ? 'css/[id].css' : 'css/[id].[contenthash].css', }),
HtmlWebpackPlugin
插件来处理入口html文件的资源引用注入修改,咱们无需关心不一样环境构建输出的资源包路径命名差别,插件会自动注入资源引用代码。但若是不是纯前端应用,好比使用了 Node应用框架或其余后端语言框架的模板,则须要考虑如何引入输出后的资源路径了,但具体的实践可能须要另开话题分享了。webpack.dev.js 是开发环境的webpack config 文件,使用webpack-merge
继承 webpack.base.js ,内容主要是开发阶段所需的一些特定配置。webpack
mode
设为development
,会默认使用DefinePlugin
设置process.env.NODE_ENV
值为development
,可在源码中获取区分环境用;另外还会默认启用几个开发阶段所需的插件。devtool
用于指定source map格式,此示例值为cheap-module-eval-source-map
,首次构建与从新构建速度相对较快,以及支持行级别的源码映射品质,是开发阶段的推荐格式。devServer
主要是webpack-dev-server
的配置,包括host,端口,启用hmr、gzip压缩等配置。这里使用了portfinder
解决开发环境可能的端口冲突,能够根据实际状况决定是否使用。另外使用copy-webpack-plugin
插件替代contentBase
,能够用于非 webpack 编译处理的静态资源伺服访问。devServer.stats
能够配置终端中编译输出的显示信息,如下配置的搭配能够看到每次编译后的bundle、chunk包列表,又不会有大串的编译过程信息干扰,具体能够根据实际需求进行调整。devServer: { stats: { colors: true, builtAt: true, cached: true, cachedAssets: true, modules: false, children: false } }
效果是这样的:git
webpack.prodjs 是生产环境的webpack config 文件,也使用webpack-merge
继承了 webpack.base.js 的主要配置。在这里设置mode
值为 production
,一样使用DefinePlugin
设置process.env.NODE_ENV
值为production
,使用TerserPlugin
等插件对生产构建输出包进行压缩优化。另外引入optimize-css-assets-webpack-plugin
插件,对输出的css bundle也作优化处理。
也可使用 babel-minify-webpack-plugin
插件进一步压缩经babel编译的代码,但从实际使用来看,此插件带来的压缩效果并不明显,编译耗时却是增长很多,能够看具体状况来决定是否使用。github
至此,轻量版本的webpack工程配置已经完成。能够拉取仓库代码在本地运行体验,工程效果基本是与 vue-cli 生成版本一致的,不过对比vue-cli 的整个应用工程配置, 去除了单测、e2e测试,以及编译错误桌面提醒等插件的集成,能够根据实际需求再行配置。
对于须要维护多个项目的团队,为了能让webpack工程配置能尽量通用,能够考虑将配置方案封装成一个npm包,抽象部分配置项做为可变参数,好比publicPath
,通常是不一样应用不一样值。在应用里可使用webpack-merge
,对一些配置进行覆盖或增长,会有更好的灵活性。就如文章开头所提,一个团队内的模块处理方案基本是统一的,因此无需抽象过多配置项,不然又走回 cli 工具的老路了。
本文从实战出发,提供了一个相对普适的轻量化webpack工程配置方案。受篇幅所限,没有对一些配置项作过多解释,若是须要了解某个配置细节,能够查询相关文档或文章。有兴趣的同窗也能够阅读webpack源码做深刻了解。