笔者最近在准备给fle-cli
升级到webpack4
版本,以为有必要将探索过程的经验分享给你们,遂决定写这篇文章。(不知道fle-cli
?看这里)css
webpack4是大趋势,升级是必然的,那么为何如今才升级?html
缘由有如下几个方面:node
然而如今,笔者认为以上这些已经成熟,是时候来一波升级了。webpack
本文不会讲解webpack配置的每一个细节点,由于这些官方文档均可以看到。笔者会挑一些难以理解的新概念、可能会碰到的问题,以及笔者总结下来的优化方案来分享,但愿能够给你们带来一些帮助。git
mode是webpack4新增的参数选项,它的值有3个:development、production、none,可以帮助咱们加载一些默认配置,none即不加载默认配置。下面将对应的默认配置列出来供你们参考,以避免重复配置。github
developmentweb
注重提高代码构建速度和开发体验npm
module.exports = { cache: true, devtools: "eval", plugins: [ new webpack.NamedModulesPlugin(), new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify("development") }) ] }
prodution浏览器
提供代码优化,如压缩、做用域提高等缓存
var UglifyJsPlugin = require('uglifyjs-webpack-plugin') module.exports = { plugins: [ new UglifyJsPlugin(/* ... */), new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify("production") }), new webpack.optimize.ModuleConcatenationPlugin(), new webpack.NoEmitOnErrorsPlugin() ] }
这个选项也是webpack4新增的,主要是用来自定义一些优化打包策略。
minimizer
在production模式,该配置会默认为咱们压缩混淆代码,但这显然知足不了咱们对于优化代码的诉求。下面笔者分享一套自身实践总结下来的配置及解释:
var UglifyJsPlugin = require('uglifyjs-webpack-plugin') var OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin') module.exports = { optimization: { minimizer: [ // 自定义js优化配置,将会覆盖默认配置 new UglifyJsPlugin({ exclude: /\.min\.js$/, // 过滤掉以".min.js"结尾的文件,咱们认为这个后缀自己就是已经压缩好的代码,不必进行二次压缩 cache: true, parallel: true, // 开启并行压缩,充分利用cpu sourceMap: false, extractComments: false, // 移除注释 uglifyOptions: { compress: { unused: true, warnings: false, drop_debugger: true }, output: { comments: false } } }), // 用于优化css文件 new OptimizeCssAssetsPlugin({ assetNameRegExp: /\.css$/g, cssProcessorOptions: { safe: true, autoprefixer: { disable: true }, // 这里是个大坑,稍后会提到 mergeLonghand: false, discardComments: { removeAll: true // 移除注释 } }, canPrint: true }) ] } }
UglifyJsPlugin
这款插件相信你们也是常常用到,这里再也不多说,这里的亮点是过滤掉自己已是压缩的js文件,可以提高咱们的编译效率以及避免二次混淆压缩而形成的未知bug。
OptimizeCssAssetsPlugin
这款插件主要用来优化css文件的输出,默认使用cssnano
,其优化策略主要包括:摈弃重复的样式定义、砍掉样式规则中多余的参数、移除不须要的浏览器前缀等,更多优化规则看这里。前文咱们提到这里有个大坑,相信你已经察觉到了,没错,就是这货把咱们经过autoprefixer
加好了前缀给移除了。笔者查阅了许多资料,依旧没有找到满意的答案,没办法,只有硬着头皮去源码中找答案了,因而便有了这段配置autoprefixer: { disable: true }
,禁用掉cssnano对于浏览器前缀的处理。
runtimeChunk
分离出webpack编译运行时的代码,也就是咱们先前称为manifest
的代码块,好处是方便咱们作文件的持久化缓存。它能够设置多种类型的值,具体能够看这里,其中single
即将全部chunk的运行代码打包到一个文件中,multiple
就是给每个chunk的运行代码打包一个文件。
咱们能够配合InlineManifestWebpackPlugin
插件将运行代码直接插入html文件中,由于这段代码很是少,这样作能够避免一次请求的开销,可是新版插件的配置和以前有些不太同样,接下来详细讲解一下如何配置。
var HtmlWebpackPlugin = require('html-webpack-plugin') var InlineManifestWebpackPlugin = require('inline-manifest-webpack-plugin') module.exports = { entry: { app: 'src/index.js' }, optimization: { runtimeChunk: 'single' // 等价于 // runtimeChunk: { // name: 'runtime' // } }, plugins: [ new HtmlWebpackPlugin({ title: 'fle-cli', filename: 'index.html', template: 'xxx', inject: true, chunks: ['runtime', 'app'], // 将runtime插入html中 chunksSortMode: 'dependency', minify: {/* */} }), new InlineManifestWebpackPlugin('runtime') ] }
这段配置会产生一个叫作runtime
的代码块,和老版本不一样的是,咱们并不须要在html模版中添加<%= htmlWebpackPlugin.files.webpackManifest %>
,只需将runtime加入chunks便可。这里有一个点要注意,InlineManifestWebpackPlugin插件的顺序必定要在HtmlWebpackPlugin以后,不然会致使编译失败。
splitChunks
终于要讲到重头戏了,也是笔者我的认为最难以理解的一个配置项。webpack4移除了CommonsChunkPlugin插件,取而代之的是splitChunks。
咱们先来看下默认配置:
splitChunks: { chunks: "async", minSize: 30000, minChunks: 1, maxAsyncRequests: 5, maxInitialRequests: 3, automaticNameDelimiter: '~', name: true, cacheGroups: { vendors: { test: /[\\/]node_modules[\\/]/, priority: -10 }, default: { minChunks: 2, priority: -20, reuseExistingChunk: true } } }
默认配置只会做用于异步加载的代码块,它限制了分离文件的最小体积,即30KB(注意这个体积是压缩以前的),这个是前提条件,而后它有两个分组:属于node_modules模块,或者被至少2个入口文件引用,它才会被打包成独立的文件。
为何要限制最小体积呢?由于webpack认为小于30KB的代码块分离出来,还要额外消耗一次请求去加载它,成本过高,固然这个值也不是随便意淫出来的,而是通过大量的实践总结获得的,笔者我的认为这是一个很是好策略。
而maxAsyncRequests
(最大的异步请求数)和maxInitialRequests
(最大的初始请求数)这两个参数则是为了限制代码块划分的过于细致,致使大量的文件请求。
可是只分离异步代码块显然知足不了咱们的需求,所以接下来笔者分享一套相对来讲比较优雅的分离打包配置:
splitChunks: { cacheGroups: { vendors: { test: /[\\/]node_modules[\\/]/, name: 'vendors', minSize: 30000, minChunks: 1, chunks: 'initial', priority: 1 // 该配置项是设置处理的优先级,数值越大越优先处理 }, commons: { test: /[\\/]src[\\/]common[\\/]/, name: 'commons', minSize: 30000, minChunks: 3, chunks: 'initial', priority: -1, reuseExistingChunk: true // 这个配置容许咱们使用已经存在的代码块 } } }
首先是将node_modules的模块分离出来,这点就再也不累述了。异步加载的模块将会继承默认配置,这里咱们就不须要二次配置了。
第二点是分离出共享模块,笔者认为一个优雅的项目结构,其公共代码(或者称为可复用的代码)应该是放置于同一个根目录下的,基于这点咱们能够将src/common
中的公用代码提取出来。
固然你还能够有另一种选择,将后缀为.js
且使用次数超过3次的文件提取出来,可是笔者不建议这个作,由于这不利于持久化缓存,新增或删除文件都有可能影响到使用次数,从而致使原先的公共文件失效。
原先还想着讲一下css插件部分的配置,限于篇幅,本文就再也不进行讲解说明了,感兴趣的小哥哥小姐姐能够在这里翻看源码:webpack4-test。
顺便在这里推荐一款好用的全局通用脚手架fle-cli:旨在帮助咱们从复杂繁琐的编译配置中解放出来,全身心地投入业务开发中,提升开发效率;同时它也是真正意义上的全局脚手架,区别于市面上其余的全局脚手架,它不会在项目工程中生成各类编译配置文件,也不会给你安装一系列编译的依赖包,这意味着你的项目工程能够很是干净纯粹。