Webpack

Webpack

在前端模块化尚未流行的年代里,每个 HTML 文件的尾部都会挂载很多的 script 标签来载入 JavaScript 代码,各个文件之间的依赖异常混乱,项目的可维护性随着代码的增加而迅速降低,整个应用的开发流程中也尚未有专门的编译流程。后来出现了以 Grunt/Gulp 为代表的所谓的 Task Runner。虽然类似于 Gulp 这样的 Task Runner 也能添加很多的预处理器或者转换器,但是本质上它仍然需要指定元输入。而 Webpack 最早的动因即是希望能够让开发工具自己去处理模块依赖问题,开发者不需要再为每一个任务去指定输入输出:

综上所述,Webpack 具有如下优点:

  • 代码拆分:Webpack 有两种组织模块依赖的方式,同步和异步。异步依赖作为分割点,形成一个新的块。在优化了依赖树后,每一个异步区块都作为一个文件被打包。
  • Loader:Webpack 本身只能处理原生的 JavaScript 模块,但是 loader 转换器可以将各种类型的资源转换成 JavaScript 模块。这样,任何资源都可以成为 Webpack 可以处理的模块。
  • 智能解析:Webpack 有一个智能解析器,几乎可以处理任何第三方库,无论它们的模块形式是 CommonJS、AMD 还是普通的 JS 文件。甚至在加载依赖的时候,允许使用动态表达式 require("./templates/" + name + ".jade")
  • 插件系统:Webpack 还有一个功能丰富的插件系统。大多数内容功能都是基于这个插件系统运行的,还可以开发和使用开源的 Webpack 插件,来满足各式各样的需求。
  • 快速运行:Webpack 使用异步 IO 和多级缓存提高运行效率,这使得 Webpack 能够以令人难以置信的速度快速增量编译。Webpack 是笔者见过的最强大的模块管理器与编译工具,他不仅仅同时支持 CMD 与 AMD 模式,也能实时编译 JSX、ES6 等语法,还能将 CSS、图片等资源文件都进行打包。

基础配置

作为著名的打包工具,Webpack 允许我们指定项目的入口地址,然后自动将用到的资源,经由 Loader 与 Plugin 的转换,打包到包体文件中在介绍具体的配置方案之前笔者想先概述下该配置文件的设计的目标,或者说是笔者认为一个前端编译环境应该达成的特性,这样以后即使 Webpack 被淘汰了也可以利用其他的譬如 JSPM 之类的来完成类似的工作。

Webpack 机制

配置级需求:

  • 单一的配置文件:很多项目里面是把开发环境与生产环境写了两个配置文件,可能笔者比较懒吧,不喜欢这么做,因此笔者的第一个特性就是单一的配置文件,然后通过 npm 封装不同的编译命令传入环境变量,然后在配置文件中根据不同的环境变量进行动态响应。另外,要保证一个 Boilerplate 能够在最小修改的情况下应用到其他项目。
  • 多应用入口支持:无论是单页应用还是多页应用,在 Webpack 中往往会把一个 html 文件作为一个入口。笔者在进行项目开发时,往往会需要面对多个入口,即多个 HTML 文件,然后这个 HTML 文件加载不同的 JS 或者 CSS 文件。譬如登录页面与主界面,往往可以视作两个不同的入口。Webpack 原生提倡的配置方案是面向过程的,而笔者在这里是面向应用方式的封装配置。

开发与构建级需求:

  • 调试时热加载:这个特性毋庸多言,不过热加载因为走得是中间服务器,同时只能支持监听一个项目,因此需要在多应用配置的情况下加上一个参数,即指定当前调试的应用。
  • 自动化的 Polyfill:这个是 Webpack 自带的一个特性吧,不过笔者就加以整合,主要是实现了对于 ES6、React、CSS(Flexbox)等等的自动 Polyfill。
  • 资源文件的自动管理:这部分主要指从模板自动生成目标 HTML 文件、自动处理图片/字体等资源文件以及自动提取出 CSS 文件等。
  • 文件分割与异步加载:可以将多个应用中的公共文件,譬如都引用了 React 类库的话,可以将这部分文件提取出来,这样前端可以减少一定的数据传输。另外的话还需要支持组件的异步加载,譬如用了 React Router,那需要支持组件在需要时再加载。