Webpack 本质是一个模块化打包工具,通过“万物皆模块”这种设计思想,巧妙地实现了整个前端项目的模块化。
Webpack 解决的问题
如何在前端项目中更高效地管理和维护项目中的每一个资源。
模块化的演进过程
- Stage 1 - 文件划分方式
- Stage 2 - 命名空间方式
- Stage 3 - IIFE
- Stage 4 - IIFE 依赖参数
模块加载的问题
更为理想的方式应该是在页面中引入一个 JS 入口文件,其余用到的模块可以通过代码控制,按需加载进来。
模块化规范的出现
早期制定前端模块化标准时,并没有直接选择 CommonJS 规范(CommonJS 约定的是以同步的方式加载模块),而是专门为浏览器端重新设计了一个规范,叫做 AMD
( Asynchronous Module Definition) 规范,即异步模块定义规范。同期还推出了一个非常出名的库,叫做 Require.js
,它除了实现了 AMD 模块化规范,本身也是一个非常强大的模块加载器。
在 AMD 规范中约定每个模块通过
define()
函数定义,这个函数默认可以接收两个参数,第一个参数是一个数组,用于声明此模块的依赖项;第二个参数是一个函数,参数与前面的依赖项一一对应,每一项分别对应依赖项模块的导出成员,这个函数的作用就是为当前模块提供一个私有空间。如果在当前模块中需要向外部导出成员,可以通过return
的方式实现。// AMD 规范定义一个模块 define(['jquery', './module2.js'], function ($, module2) { return { start: function () { $('body').animate({ margin: '200px' }) module2() } } })
除此之外,Require.js 还提供了一个
require()
函数用于自动加载模块,用法与 define() 函数类似,区别在于 require() 只能用来载入模块,而 define() 还可以定义模块。当 Require.js 需要加载一个模块时,内部就会自动创建script
标签去请求并执行相应模块的代码。// AMD 规范载入一个模块 require(['./modules/module1.js'], function (module1) { module1.start() })
同期出现的规范还有淘宝的 Sea.js
,只不过它实现的是另外一个标准,叫作 CMD
,这个标准类似于 CommonJS,在使用上基本和 Require.js 相同,可以算上是重复的轮子。但随着前端技术的发展,Sea.js 后来也被 Require.js 兼容了。
模块化的标准规范
在 Node.js 环境中,我们遵循 CommonJS
规范来组织模块。
在浏览器环境中,我们遵循 ES Modules
规范。
模块打包工具的出现
模块化可以帮助我们更好地解决复杂应用开发过程中的代码组织问题,但是随着模块化思想的引入,我们的前端应用又会产生了一些新的问题,比如:
- 首先,我们所使用的 ES Modules 模块系统本身就存在环境兼容问题。尽管现如今主流浏览器的最新版本都支持这一特性,但是目前还无法保证用户的浏览器使用情况。所以我们还需要解决兼容问题。
- 其次,模块化的方式划分出来的模块文件过多,而前端应用又运行在浏览器中,每一个文件都需要单独从服务器请求回来。零散的模块文件必然会导致浏览器的频繁发送网络请求,影响应用的工作效率。
- 最后,在实现 JS 模块化的基础上的发散。随着应用日益复杂,在前端应用开发过程中不仅仅只有 JavaScript 代码需要模块化,HTML 和 CSS 这些资源文件也会面临需要被模块化的问题。而且从宏观角度来看,这些文件也都应该看作前端应用中的一个模块,只不过这些模块的种类和用途跟 JavaScript 不同。
对于开发过程而言,模块化肯定是必要的,所以我们需要在前面所说的模块化实现的基础之上引入更好的方案或者工具,去解决上面提出的 3 个问题,让我们的应用在开发阶段继续享受模块化带来的优势,又不必担心模块化对生产环境所产生的影响。
接下来我们先对这个更好的方案或者工具提出一些设想:
-
第一,它需要具备编译代码的能力,也就是将我们开发阶段编写的那些包含新特性的代码转换为能够兼容大多数环境的代码,解决我们所面临的环境兼容问题。
-
第二,能够将散落的模块再打包到一起,这样就解决了浏览器频繁请求模块文件的问题。这里需要注意,只是在开发阶段才需要模块化的文件划分,因为它能够帮我们更好地组织代码,到了实际运行阶段,这种划分就没有必要了。
-
第三,它需要支持不同种类的前端模块类型,也就是说可以将开发过程中涉及的样式、图片、字体等所有资源文件都作为模块使用,这样我们就拥有了一个统一的模块化方案,所有资源文件的加载都可以通过代码控制,与业务代码统一维护,更为合理。
针对上面第一、第二个设想,我们可以借助 Gulp
之类的构建系统配合一些编译工具和插件去实现,但是对于第三个可以对不同种类资源进行模块化的设想,就需要使用前端模块打包工具
。
Webpack:A bundler for javascript and friends(一个 JavaScript 和周边的打包工具)
Webpack 以模块化思想为核心,帮助开发者更好的管理整个前端工程。