问诊webpack:从何而来,去往何处

avatar
公众号:转转技术

前言

webpack是一个很重的话题,网上关于如何写配置、如何写loader、写plugin的文章一搜一堆。甚至业内为了调侃webpack配置的复杂性,发明了一个工种“webpack配置工程师”。 如何写配置、loader、plugin,在我看来是一种“查阅性知识”。用的时候,能快速查到,不用的时候,真记不住。

但是如果对webpack的认识仅仅停留在这个层面,总感觉和webpack还是很陌生,和webpack之间依然搁着一层看不见、摸不着的面纱。

这层“面纱”背后是什么?小编觉得应该是宏观层面对webpack设计理念的认知。如webpack从何而来、要解决什么问题、如何解决这些问题,未来如何演变等等。 但是,如果使用百度或者谷歌搜索“webpack 设计理念”,你会发现,得不到太多内容,坦白讲,几乎没有。

没有资料,我们就自己试试提炼吧,不妥或不对之处,欢迎留言交流~

① 为什么会有webpack

感观上来讲,webpack和node.js八竿子打不着。 但是,将新生事物的诞生,放在它所属的历史环境下去看,就会发现中间还有些奇妙的因果关系。

通过上图的颜色可以区分为三大生态,一是以粉红色为代表的JS模块化发展路线。另一个是橙色为代表的node.js生态扩张路线。最后一个是以橘红色为代表的webapck

webpack的第一次提交的README.md中,我们可以看到作者非常清晰而纯朴的愿望:

As developer you want to reuse existing code. As with node.js and web all file are already in the same language, but it is extra work to use your code with the node.js module system and the browser. The goal of webpack is to bundle CommonJs modules into javascript files which can be loaded by <script>-tags.

意译:

作为开发者,大家都想复用已经存在的代码。node.js和web应用使用的是相同的语言。但是,如果想将node.js模块复用到浏览器,需要做很多额外的工作。webpack的目标是将CommonJs模块打包成javascript文件,然后可以被<script>标签加载。

由上述可见,webpack作者看到node.js生态中存在着大量的js代码,却不能够被浏览器端复用。所以就萌生了想把基于CommonJS规范的模块化代码,打包成能够被<script>加载的模块。 所以,说一句webpack缘起于node.js应该是不过分的。

② webpack到底要解决什么问题

如果我们从webpack的官方文档去了解它,会发现它具备的功能非常多。ts加载、图片加载、sourcemap、代码压缩、代码分割、热加载、treeshaking等等....

到底哪一个才是webpack的核心功能?到底哪里才是webpack的扩张边界?难道随着版本的迭代,webpack的功能会一直无限制地膨胀下去吗?指引作者背后的发展思路,到底是什么?

其实上述种种疑问,早在作者写的第一个commit中的package.json文件中作过解释:

Bundle CommonJS modules into single script or multiple scripts for web browser.

意译:

将遵循CommonJS规范的JS模块,打包成一个或多个脚本供web浏览器使用。

同时,在webpack官网对webpack的也有明确的定义

At its core, webpack is a static module bundler for modern JavaScript applications. When webpack processes your application, it internally builds a dependency graph which maps every module your project needs and generates one or more bundles.

意译:

webpack是现代JS应用程序的一个静态模块打包器。当webpack处理你的程序时,会为项目中的所有模块创建一个依赖图,然后打成一个或者多个包。

最后,webpack官网上这张万年不变的示意图中其实也已经说明了一切。

webpack从诞生之初,到现在的5.x版本,初心一直未变:bundle(打包/构建)

只是随着前端技术生态的不断演化和繁荣,出现了很多新的花样:ts、vue、electron、es6等等。为了应对这些新的趋势,webpack的生态也不断往前发展、扩张。 但是,还是那句话,无论webpack如何发展、扩张,初心一直未改,一直都是专注于“打包”。 当然,为了让“打包”这个事情更简单、舒适。webpack顺带解决了很多“打包”需要的周边小功能。比如代码分割、按需加载、CDN加速、treeshaking等等。

③ webpack怎样解决“打包”这个问题

可以想象一下,如果让我们自己为浏览器设计一个“打包工具”,我们需要考虑哪些问题?

首先,得想办法把浏览器无法直接识别的vue、ts等文件,转换成可以被认识的js文件。 另外,如果一个vue、ts模块中引用了其它模块,得想办法通过分析得到他们的依赖关系,然后把他们打包在一起。

最后,还要考虑,将所有依赖模块全部打包在一个文件中,网络请求时可能会比较慢,得想办法解决。

其实,想到以上三点,就已经可以触及到webpack的设计灵魂了。webpack作者在最初设计的时候,需要解决的核心问题,其实也基本是上述三点。但是,普通人和大神之间的区别是,普通人也就是想一想。而大神却可以给出一个堪称完美且能够经受时间考验的架构方案,解决上述问题。

为了解决上述三个问题。首先webpack作者提出一个构想“万物皆module”。无论是js代码文件、还是各种图片资源,最终都用“module”的概念将之归拢。 作者也通过“module”的概念,将各种复杂混乱的文件类型,拉齐到了一个平等的关系轴上

其次,webpack作者通过设计了以下4个概念:Entry、Output、Loaders、Plugins,为webpack实现了一个结构清晰、功能开放、可以持续迭代的的架构设计。

这几个概念,我们在配置webpack时经常遇到。但往往只是盲人摸象,得其局部,而不得其整体。为了加深对这4个概念理解的整体性,我们再画一张图,体现webpack作者的设计思路。

从上图我们可以看出:

  • webpack使用Entry概念解决分析的“入口”问题;
  • 用Loaders概念解决“依赖图谱构建”问题,翻译各种奇奇怪怪的文件类型;
  • 用Plugins概念对外开放webpack运行时的各种时机,以便第三方开发者可以扩展webpack的功能;
  • 用Output概念囊括了所有的“输出”问题。

同时,因为Plugins和Loaders是对外开放的设计,所以保证了webpack拥有持续的灵活性。如果用一句话点评webpack的架构设计的话,那就是:简洁而富有生命力

④ 除了“打包”,webpack还有哪些能力

如果说“打包”是webpack第一核心的能力,那么“拆包(代码分割)”就是webpack第二核心的能力。其实这个道理很好理解。“打包”和“拆包(代码分割)”本来就是一对共生体,如果webpack仅仅只关注于“打包”,那么最终出来的“包”也可能会因为体积太大而不实用。

除了“代码拆分”之外,webpack生态中,常见的周边能力,总结如下:

⑤ 最后,聊聊webpack那些所谓的竞品

如果我们深刻理解了为什么会有webpack以及webpack到底解决什么问题时,就会发现在知名软件中,其实webpack并没有所谓的“竞品”。

  • webpack

产品定位是打通node.js与浏览器端JS代码资源的模块打包器。虽然,现在webpack的loader和plugin功能让它看起来无所不能,但是webpack对自己是个模块打包器的定位始终没有变化。看起来强大,只是webpack开放性设计带来的额外好处。

  • Grunt

是一个任务执行者,有大量现成的插件封装了常见的任务,也能管理任务之间的依赖关系,自动化执行依赖的任务,

  • Gulp

是一个基于流的任务管理器

小结

webapck本质是模块打包器,因为其开放性设计,恰好可以做一些构建工具的工作;Grunt是纯粹的任务执行者;Gulp的定位才是构建工具。

怎么说呢,webpack误打误撞,侵入了其它产品的目标领域。但webapck的初衷始终没变。 最后,再来回味一下webpack官网的这句话:

At its core, webpack is a static module bundler for modern JavaScript applications.