现在对于每个前端工程师来讲,webpack 已经成为了一项基础技能,它基本上包办了本地开发、编译压缩、性能优化的全部工做,从这个角度上来讲,webpack 确实是伟大的,它的诞生意味着一整套工程化体系开始普及,而且慢慢统一了前端自动构建的让前端开发完全告别了以前的刀耕火种时代。如今 webpack 之于前端开发,正如同 gcc/g++ 之于 C/C++,是一个你不管如何都绕不开的工具。css
可是,即便它如此伟大,也有一个巨大的问题,那就是 webpack 实在是太难用了!!!html
我从多年前的 webpack 1.0 时代就一直在用它,如今也不能说彻底掌握了它,不少时候真的让我产生了怀疑,到底是由于个人能力不足,仍是由于 webpack 自身的设计就太难用?随着我接触到愈来愈多的前端项目,听到愈来愈多的吐槽,我也愈加地相信,是 webpack 自身的问题,致使它变得如此复杂又难用。前端
举个简单的例子,一个 vue-cli 生成的最简单的脚手架项目,开发、构建相关的文件就有 14 个之多,代码超过 800 行,而真实的项目只会比这个更多: vue
因此,既然这篇文章的标题是《webpack 为何这么难用?》,那咱们就好好在这里分析一下,webpack 难用的根本缘由。node
是的,这就是第一位的缘由。react
我做为参加过 webpack 中文文档翻译的人,真的想说 webpack 即便通过了这么多年的不断迭代,现在的文档依然仍是是一坨那啥。做为一个开源项目,设计好很差、易用性怎么样、扩展性怎么样这些问题都是仁者见仁智者见智的,但文档写得很烂这一点上,真的没有任何能够开脱的理由。webpack
好比,webpack 的插件体系能够说是 webpack 最核心的一部分功能了,基本上一个项目的构建中,大部分任务都是由各类插件完成的。然而,官方文档上对于插件的介绍只有寥寥几句话:webpack · Plugins,甚至推荐你直接去看 webpack 的源码:git
更糟的是,现有的文档里(包括 webpack 一些插件的文档也是),大部份内容都是在告诉你 “你这样作就能够了”,而没有解释 “你为何须要这么作” 以及 “你这么作了会有哪些后果”。github
好比,在 target 配置上,官方文档里列举了你能够构建到哪些 target,如 node
、node-webkit
、electron-main
,但都只是简单的一句话带过:web
想知道 target 为 electron-main
时,和浏览器环境的打包有什么不一样?对不起,官方文档不想告诉你,看源码或者去 stackoverflow 上搜吧。
官方文档语焉不详的直接后果就是,当你遇到了任何问题,你都没办法在文档里获得直接的回答,而是须要看无数的代码、github issue、stackoverflow、博客文章,而后在本身的项目里反反复复试了好屡次,才能大体解决问题。而这种所谓的“解决问题”,通常都是我的经验性的,意味着其它任何一我的想要解决这个问题,都要重复一遍这个流程,时间成本大量上升。
这就是为何使用 webpack 的时候,常常会出现下面的哲学三问:
咱们要如何开发一个 webpack 的插件?
官方文档里确实写了一些关于如何开发插件的指南。但这份指南也只有 60 分刚及格的水平,它确实向你介绍了 webpack 插件的基础范例、基本概念以及一些 API,但当你读完这份简短的文档后想本身真的去开发一个插件时,你会发现文档里讲的东西真的远远不够。
咱们不妨来看看如今 webpack 生态里那些成熟的插件是怎么写的,以 html-webpack-plugin 为例,这是一个普遍用于生成 html 文件的插件。在它的源码里你会发现,它引用了五个 webpack 内部自带的插件(源码在这里):
var NodeTemplatePlugin = require('webpack/lib/node/NodeTemplatePlugin'); var NodeTargetPlugin = require('webpack/lib/node/NodeTargetPlugin'); var LoaderTargetPlugin = require('webpack/lib/LoaderTargetPlugin'); var LibraryTemplatePlugin = require('webpack/lib/LibraryTemplatePlugin'); var SingleEntryPlugin = require('webpack/lib/SingleEntryPlugin'); 复制代码
嗯哼?这五个插件都是用来干什么的?
官方文档上对内置的插件一个字都没有说起,是的,甚至连 Plugins 这里都没有。官方的 wiki 上却是写了,但真的真的太简略了,并且看起来好久没更新了。
再看另一个一样经常使用的 uglifyjs-webpack-plugin,它却是没依赖 webpack 的内置插件,不过也引用了 webpack 内部的两个文件:
import RequestShortener from 'webpack/lib/RequestShortener'; import ModuleFilenameHelpers from 'webpack/lib/ModuleFilenameHelpers'; 复制代码
文档里一样没有对这两个文件作任何介绍。使人欣慰(?)的是,这两个文件从文件名上看,起码是方法库(实际上也确实是),使用起来不会太复杂。
换句话说,若是你想给 webpack 写一个广为人知的插件,你就必须深刻了解 webpack 的所有,这一点我不反对,毕竟 webpack 开发者和 webpack 使用者在能力的要求上有高低之分。但即便是有经验的开发者,遇到一个文档如此不完善的开源项目,也是很吃力的。不少能帮助开发者的东西本应该正大光明地写在文档和指南里,而不是隐藏在源代码里。
插件体系是 webpack 的核心,事实上,webpack 的大部分功能都是经过内部插件或者第三方插件来完成的。能够说,webpack 的生态就是创建在众多插件之上的。
但插件体系也一样有不少问题。
先问一个问题,一个经过 webpack 构建的项目须要多少插件?
仍是以一个标准的 vue-cli 生成的脚手架项目为例,一共有 7 个第三方插件:
"copy-webpack-plugin": "^4.0.1", "extract-text-webpack-plugin": "^3.0.0", "friendly-errors-webpack-plugin": "^1.6.1", "html-webpack-plugin": "^2.30.1", "webpack-bundle-analyzer": "^2.9.0", "optimize-css-assets-webpack-plugin": "^3.2.0", "uglifyjs-webpack-plugin": "^1.1.1", 复制代码
以及 7 个 webpack 内置插件:
HashedModuleIdsPlugin
ModuleConcatenationPlugin
CommonsChunkPlugin
DefinePlugin
HotModuleReplacementPlugin
NamedModulesPlugin
NoEmitOnErrorsPlugin
总共 14 个插件,咱们按照平均一个插件含有 2-3 个配置项(这已是往低了算了)来计算,14 个插件就有 30 多项配置,这已是一个现代 webpack 开发、构建使用的很基础的配置了,真实的项目只会比这个更多。
要注意到,30 多个配置项带来的复杂程度是远胜于 30 行代码的。 由于配置项已经具备了比较高的抽象性,一项配置包含的反作用是要远大于一行代码的。好比下面是经常用于提取公共模块的 CommonsChunkPlugin
的配置:
new webpack.optimize.CommonsChunkPlugin({ name: 'app', async: 'vendor-async', children: true, minChunks: 3 }) 复制代码
若是你不是一个 webpack 老手的话,看到这 4 项配置确定是一脸懵逼的:
name
该填什么?随便命个名就好吗?async
是什么?异步模块?那为何是个字符串?children
是个啥?为何不是 Array
而是个 boolean
?minChunks
这个数字是什么?chunk
又是什么?而后你就去看了 CommonsChunkPlugin 的文档,十五分钟艰难的阅读以后,你会发现这四项配置都不简单,每一项的更改会给构建带来很大的影响。
然而坏消息是,像这样的配置在项目里整整有 30 多处!
因此我每次改一个项目的构建时,基本都是这样的:
在讨论这个话题以前,先回答两个问题:
webpack 的插件前后顺序会影响构建结果吗?
若是插件顺序不一样,会影响哪些东西?
实际上,这两个问题我找遍了官方文档,也没有提到插件的顺序会影响哪些东西,stackoverflow 上却是找到了一个问题:Webpack: Does the order of plugins matter?
因此回答就是:插件的顺序有影响,但做用不明。
其实问题不止在插件的顺序前后上,就连一个插件到底对构建产生了哪些影响,咱们也很可贵知,除非你极其熟悉这个插件或者就是这个插件的做者。为何会这样?根本缘由就是,webpack 的插件是面向配置的,而不是面向过程的
什么叫面向过程?若是你知道或者使用过 gulp 这个自动化工具的话,应该会记得 gulp 管道的概念,即从源头那里获得源数据(js/css/html 源码、图片、字体等等),而后数据经过一个又一个组合起来的管道,最后输出成为构建的结果。写成伪代码的话,大概是这样:
gulp.src('某些源文件') .pipe(处理一) .pipe(处理二) .pipe(处理三) .dest('构建结果') 复制代码
这种管道化,或者说面向过程的构建,很是容易 debug 或者修改,由于它构建的每一步过程,都整齐的按照顺序展现给你看了。想要修改其中任何一步的心智负担是很低的,由于它的处理机制很是纯函数。
然而若是是 webpack 的话,就相似这样:
{
plugins: [
插件一,
插件二,
插件三
]
}
复制代码
这里,插件一二三是彻底面向配置的,没有告诉你任何执行顺序,它们可能会在 webpack 构建的每一个时间点触发,你只能从它们的功能上大体猜出它是在哪一个时间点工做的。这就是为何修改一些 webpack 的配置,就像要解开一条放在包里好久的耳机线同样,麻烦又闹心。
固然,这种配置化的插件也是有好处的,配置化表明了高集成度,当你只有 1-3 个插件时,维护这些配置的心智负担是能够接受的,而且比维护面向过程的配置更加方便。但当插件数量超过这个值的时候,构建的复杂程度就会呈指数式上升,咱们以前就已经提到了,一个现代的 webpack 项目起码会有 14 个以上的插件以及至少 30 多项配置,这种状况下,面向过程就会好于面向配置,这就是为何我一直以为 gulp + webpack 才是正确解决方案的缘由。
固然仍是要说一句,gulp 和 webpack 并不能直接比较,前者是一个 task runner,然后者是一个 module bundler,它们二者之间都有一些相互不可替代的功能。
在平常业务中,特别是大公司的一些运营性质的业务里,咱们经常会看到 “某某业务已经实现彻底配置化” 这样的字眼,在这个语境里,配置化表明了低维护成本、高灵活性、高封装性。
在技术的世界里,配置化也一样是个好东西,不少工具都会宣称本身是彻底配置化的,只要你的项目里加入一个配置文件,那么这个工具就能够帮你作不少不少的事情,babel、eslint、stylelint,还有本文讨论的 webpack 都是如此。
因此配置化是否是就是全部工具进化的终点了呢?它是否是能解决全部的问题呢?
软件工程上有一句耳熟能详的话:“没有银弹”,指的是复杂的软件工程问题没法靠简单的答案来解决。在前端工程构建这个问题上,也一样不例外。
如何解决前端工程的构建?webpack 给出的答案是:经过 webpack + loader + plugin,让一切资源构建可配置。 这在它诞生的那个时代看来,是很是厉害的,一份简单的配置文件就帮你搞定了全部资源构建的问题。
可是当时间的推移,一个前端项目的构建变得愈来愈复杂,webpack 的配置也愈来愈多,维护起来愈来愈难,这个时候,也就慢慢诞生了诸如 create-react-app、vue-cli 这样的脚手架工具,在 webpack 的基础上进一步封装,来帮你自动生成 webpack 的配置。这个时候,webpack 更多地变成了一个“底层”工具,而这些脚手架才是你实际上的“构建工具”,或者说,这些脚手架提供的配置,才是你真正的构建配置。
为何会这样?
这个问题的根源在于,webpack 如今提供的配置的封装性已经不够了,它面对一个现在复杂得多的大型前端工程,仅有的配置已经没办法像几年前那样为咱们屏蔽掉大部分的构建细节了,因此在它的基础上诞生了如此多的脚手架工具帮咱们进一步封装复杂性。
因此咱们如今能够回答这一段的标题了:配置化是解决复杂度的银弹吗?固然不是,由于配置会随着复杂度的提高,而也逐渐变得复杂,维护愈来愈难,直到超过某个临界值,就会须要在它的基础上进一步封装,产生新的配置化。
正如我在上一章所说的,随着复杂度的上升,须要不断地封装复杂性,以让维护配置的心智成本降到能够接受的程度。而在前端构建工具上,截止到趋势也正是如此:
那么将来的下一代前端构建工具是怎样的呢?
如今普遍使用的这些脚手架工具,终究依赖的是 webpack,咱们实际上须要的是集成度更高、封装性更高(甚至零配置)的构建工具。更详细地说,下一代前端构建工具,必然会有下面的某些特性:
事实上,这也就是部分 webpack 4.0 将会有的新特性,以及前段时间看到的 parcel 也具备其中的某些特色(虽然它如今看起来还很不成熟)。将来这样的构建工具只会愈来愈多。
这篇文章好久以前就在构思了,只是近期在工做上集中遇到了不少 webpack 的坑,让我完全有动力来吐槽一下它的种种不是。
webpack 为何这么难用?本文给出的答案浓缩起来就是两点:
这些问题将来会有改善吗?固然。其实,这篇文章其实有标题党的嫌疑,更准确的标题应该是:
《如今的 webpack 为何这么难用?》
由于这篇文章里提到的问题,都会在 webpack 4.0 中获得改善。
额……至于它的文档嘛……算了不提了不提了 O__O "…