webpack不是内部-【第1147期】为什么webpack这么难用?

前言

最近市面上出现了webpack的书籍,前段时间还出现了一个零配置的包裹打包工具。 今天晨读文章由腾讯@王伟嘉授权分享。

@王伟嘉,腾讯后端工程师,Node.js 贡献者,1995 年出生

正文从这里开始~

如今,webpack已经成为每个后端工程师的基本技能。 它基本上包揽了本地开发、编译压缩、性能优化的所有工作。 从这个角度来说,webpack 确实很棒。 它的诞生意味着一套工程系统开始流行,手动后端搭建也逐渐统一,让后端开发彻底告别了之前刀耕火种的时代。 现在webpack之于后端开发,就像gcc/g++之于C/C++一样,是一个无论如何都绕不开的工具。

但即使它很强大,也有一个巨大的问题,那就是 webpack 太难用了! ! !

我从多年前的1.0时代就开始使用webpack,现在还不能说已经完全掌握了。 很多时候确实让我怀疑到底是自己能力不够还是因为webpack本身的设计太复杂了。 使用起来困难吗? 随着我接触越来越多的后端项目,听到越来越多的抱怨,我越来越相信是webpack自身的问题导致了它如此复杂和难用。

举个简单的例子,vue-cli生成的最简单的脚手架项目就有多达14个开发构建相关文件和800多行代码,而真正的项目只会不止这些:

那么,既然这篇文章的标题是《为什么 webpack 这么难用?》 》,那么我们这里就好好看看webpack使用困难的根本原因吧。

1. 文档非常不完善

是的,这是第一个触发因素。

作为参与过 webpack 中文文档翻译的人,我真的很想说,即使经过了这么多年的不断迭代,目前 webpack 的文档仍然是一团糟。 作为一个开源项目,设计好不好、可用性如何、可扩展性如何,都是见仁见智的事情,但文档写得不好确实没有任何借口。

用户不友好

比如webpack的插件系统,可以说是webpack的核心功能。 基本上,在一个项目的建立中,大部分工作都是通过各种插件来完成的。 不过官方文档中关于插件的只有寥寥几句话:webpack Plugins,甚至推荐大家直接去看webpack的源码:

更糟糕的是,大多数现有文档(包括一些 webpack 插件的文档)都告诉你“你可以这样做”,而没有解释“为什么你需要这样做”以及“你这样做的后果是什么?” ”

例如,在目标配置方面,官方文档列出了您可以构建哪些目标,例如node、node-webkit和electron-main,但都用简单的一句话提到了它们:

想知道当目标是Electron-Main时,在浏览器环境下打包有什么区别? 抱歉,官方文档不想告诉你,看源码或者上stackoverflow搜索。

官方文档不明确的直接后果就是,当你遇到任何问题时,无法在文档中得到直接的答案。 相反,你需要阅读无数的代码、github issues、stackoverflow、博客文章,然后在自己的项目中反思。 经过多次重试、重试,问题大致可以解决。 这些所谓的“问题解决方案”一般都是基于个人经验,这意味着任何其他人想要解决这个问题都必须重复这个过程,时间成本显着增加。

这就是为什么在使用webpack时,经常会出现以下三个哲学问题:

对开发商不友好

我们如何开发一个webpack插件?

官方文档确实写了一些关于如何开发插件的手册。 但这本手册只达到了60分的及格水平。 它确实向您介绍了 webpack 插件的基本示例、基本概念和一些 API。 但读完这个简单的文档后,您将想要自己实际开发一个插件。 有时候,你会发现文档中所说的确实远远不够。

我们来看看现在的webpack生态中这些成熟的插件是如何编写的。 以html-webpack-plugin为例,它是一个广泛用于生成html文件的插件。 在它的源码中,你会发现它引用了webpack自带的五个插件(源码在这里):

varNodeTemplatePlugin=require('webpack/lib/node/NodeTemplatePlugin');varNodeTargetPlugin=require('webpack/lib/node/NodeTargetPlugin');varLoaderTargetPlugin=require('webpack/lib/LoaderTargetPlugin');varLibraryTemplatePlugin=require('webpack/lib/LibraryTemplatePlugin');varSingleEntryPlugin=require('webpack/lib/SingleEntryPlugin');

嗯? 这五个插件是做什么用的?

官方文档中没有提到外部插件webpack不是内部,是的,这里甚至没有提到Plugins。 官方wiki上写的,不过确实太详细了,而且好像已经很久没有更新了。

再看另一个常用的uglifyjs-webpack-plugin,它不依赖webpack的外部插件,但也引用了webpack内部的两个文件:

importRequestShortenerfrom'webpack/lib/RequestShortener';importModuleFilenameHelpersfrom'webpack/lib/ModuleFilenameHelpers';

文档中也没有对这两个文件进行介绍。 令人惊讶(?)从文件名来看,这两个文件至少是方法库(事实上是),使用起来也不会太复杂。

也就是说,如果你想为webpack写一个知名的插件,你必须对webpack的一切有深入的了解。 我对此没有异议。 毕竟webpack开发者和webpack用户的能力要求有高有低。 但即使你是一位经验丰富的开发人员,遇到文档如此不健全的开源项目也是非常费力的。 许多可以帮助开发人员的东西应该写在上面的文档和指南中,而不是隐藏在源代码中。

2.超重插件系统

插件系统是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 内置插件:

总共有 14 个插件。 我们估计平均一个插件包含 2-3 个配置项(这已经很低了)。 14个插件,30多种配置。 这已经是一个现代的 webpack 开发和构建了。 使用了非常基本的配置,真正的项目只会比这个更多。

需要注意的是,30多个配置项带来的复杂度远远胜过30行代码。 由于配置项已经具有比较高的抽象性,因此一个配置所包含的副作用比一行代码要小得多。 例如下面是CommonsChunkPlugin的配置,常用于提取公共模块:

newwebpack.optimize.CommonsChunkPlugin({name:'app',async:'vendor-async',children:true,minChunks:3})

如果你不是 webpack 老手,看到这 4 个配置你一定会感到困惑:

然后您阅读 CommonsChunkPlugin 文档。 经过15分钟的苦读,你会发现这四个配置并不简单,每一个修改都会对构建产生很大的影响。

但坏消息是,项目中有超过30种这样的配置!

所以我每次改变项目的建立,基本上都是这样的:

面向配置的插件

在讨论这个话题之前,先回答两个问题:

其实这两个问题我搜过官方文档,并没有提到插件的顺序会影响什么。 相反,我在 stackoverflow 上发现了一个问题:Webpack:插件的顺序重要吗?

所以答案是:插件的顺序有影响,但效果未知。

其实问题不仅仅在于插件的顺序,我们也很难知道一个插件对构建有什么影响,除非你对这个插件非常熟悉或者是该插件的作者这个插件。 为什么?根本原因是webpack插件是面向配置的,而不是面向流程的。

什么是面向过程? 如果你了解或者使用过gulp这个自动化工具,你应该记得gulp pipeline的概念,就是从源头获取源数据(js/css/html源代码、图片、字体等),然后数据依次组合起来,最后向上的管道输出就成为创建的结果。 用伪代码编写,它看起来像这样:

gulp.src('某些源文件').pipe(处理一).pipe(处理二).pipe(处理三).dest('构建结果')

这种管道,或者说面向流程的建立,非常容易调试或更改,因为它建立的流程的每一步都按顺序整齐地显示给你。 更改任何这些步骤的心理开销都很低,因为处理机制纯粹是功能性的。

不过,如果是webpack的话,就类似这样:

{plugins:[插件一,插件二,插件三]}

这里,插件 1、2、3 完全是面向配置的,不会告诉你任何执行顺序。 它们可能会在 webpack 构建的每个时间点被触发。 你只能从它们的功能中粗略地猜测它在那个时间点起作用。 的。 这就是为什么更改某些 webpack 配置就像解开包里已经放了很长时间的麦克风电缆一样。

当然,这种配置插件也是有用的。 配置代表了高度的集成度。 当你只有1-3个插件时,维护这个配置的精神负担是可以接受的,而且比维护面向流程的配置要好。 更方便。 但当插件数量超过这个值时,构建的复杂度就会呈指数级增长。 正如我们之前提到的,一个现代的 webpack 项目将至少有 14 个插件和至少 30 个配置。 在所有情况下,面向流程都比面向配置更好,这就是为什么我仍然认为 gulp + webpack 是正确的解决方案。

当然,我还是要说gulp和webpack没有直接可比性。 前者是任务运行器,而后者是模块捆绑器。 两者都有一些不可替代的功能。

3、配置是手炮吗?

在日常业务中,尤其是大公司的一些运营业务中,我们经常会听到“某某业务已经实现全配置”这样的说法。 在这种背景下,配置代表着低维护成本、高灵活性和高封装性。

在科技的世界里,配置也是一件好事。 许多工具声称已完全配置。 只要你给你的项目添加一个配置文件,那么这个工具就可以帮你做很多事情,本文讨论的 babel 、 eslint 、 stylelint 、 webpack 都是这样的。

那么配置是所有工具进化的终点吗? 它能解决所有问题吗?

软件工程中有一句耳熟能详的说法:“没有手炮”,指的是复杂的软件工程问题无法用简单的答案来解决。 在构建后端项目时也不例外。

前端工程的建立如何解决? webpack给出的答案是:通过webpack+loader+plugin让所有资源都可配置。 这在它诞生的时代是非常强大的。 一个简单的配置文件就可以帮助您解决所有资源创建问题。

但随着时间的推移,后端项目的建立变得越来越复杂,webpack配置也越来越多,维护起来也越来越困难。 此时诸如create-react-app、vue-cli等脚手架工具在webpack的基础上进一步封装,帮助你手动生成webpack配置。 这时,webpack更多地成为了一个“底层”工具,而这种脚手架才是你真正的“构建工具”,或者说,这些脚手架提供的配置才是你真正的构建配置。

为什么?

问题的关键是webpack提供的配置封装已经不够了。 它面临着一个更加复杂的小型后端项目,唯一的配置无法像几年前那样使我们免受其中大多数的影响。 建立的细节被减少了,所以基于它诞生了很多脚手架工具来帮助我们进一步封装复杂性。

那么我们现在可以回答本段的标题了:配置是解决复杂性的手炮吗? 当然不是webpack不是内部,因为配置会随着复杂度的增加而越来越复杂,维护也会越来越困难。 当超过某个临界值时,就需要在其基础上进一步封装,生成新的配置。

4.前端工程建设的未来

正如我在上一章中所说,随着复杂性的增加,需要不断地封装复杂性,以将维护配置的心理成本降低到可接受的水平。 至于后端创建工具,目前的趋势是完全相同的:

那么未来的下一代后端构建工具会是什么样子呢?

现在广泛使用的这种脚手架工具毕竟依赖于webpack。 我们真正需要的是一个更集成、更封装(甚至零配置)的创建工具。 更具体地说,下一代后端创建工具必然具有以下特定特征:

其实这是webpack 4.0将会有的新功能的一部分,前段时间听说的parcel也有个别的功能(虽然看起来还很不成熟)。 未来这样的构建工具只会越来越多。

总结

这篇文章构思很久了,但是最近在工作中遇到了很多webpack的陷阱,这让我有动力抱怨它的各种缺点。

为什么 webpack 这么难用? 这篇文章给出的答案浓缩为两点:

这些问题未来会得到改善吗? 当然。 事实上,这篇文章虽然有标题的嫌疑,但更准确的标题应该是:

“为什么现在webpack这么难用了?” 》

因为本文提到的问题将在webpack 4.0中得到改善。

嗯...至于它的文档...算了,不提了。 O__O“……

最后给大家推荐一下

关于本文