全文主要整理自摘自《Webpack中文指南》(好文,建议直接去看,如下仅对该系列文章中的《历史发展》篇幅进行备份——也整理了点其余内容)css
模块化是老生常谈了,这里不作阐述。html
模块化管理须要具有:前端
1. 定义封装的模块。node
2. 定义新模块对其余模块的依赖。jquery
3. 可对其余模块的引入支持。webpack
要通用,则必需要有规范化,所以一系列的标准应运而生。git
伴随着移动互联的大潮,当今愈来愈多的网站已经从网页模式进化到了 Webapp 模式。它们运行在现代的高级浏览器里,使用 HTML五、 CSS三、 ES6 等更新的技术来开发丰富的功能,网页已经不只仅是完成浏览的基本需求,而且webapp一般是一个单页面应用,每个视图经过异步的方式加载,这致使页面初始化和使用过程当中会加载愈来愈多的 JavaScript 代码,这给前端开发的流程和资源组织带来了巨大的挑战。es6
前端开发和其余开发工做的主要区别,首先是前端是基于多语言、多层次的编码和组织工做,其次前端产品的交付是基于浏览器,这些资源是经过增量加载的方式运行到浏览器端,如何在开发环境组织好这些碎片化的代码和资源,而且保证他们在浏览器端快速、优雅的加载和更新,就须要一个模块化系统,这个理想中的模块化系统是前端工程师多年来一直探索的难题。github
模块系统主要解决模块的定义、依赖和导出,先来看看已经存在的模块系统。web
<script src="module1.js"></script> <script src="module2.js"></script> <script src="libraryA.js"></script> <script src="module3.js"></script>
这是最原始的 JavaScript 文件加载方式,若是把每个文件看作是一个模块,那么他们的接口一般是暴露在全局做用域下,也就是定义在 window
对象中,不一样模块的接口调用都是一个做用域中,一些复杂的框架,会使用命名空间的概念来组织这些模块的接口,典型的例子如 YUI 库。
这种原始的加载方式暴露了一些显而易见的弊端:
<script>
的书写顺序进行加载历史
2009年1月,Mozilla 的工程师 Kevin Dangoor 建立了这个项目,当时的名字是 ServerJS。其是以在浏览器环境以外构建 JavaScript 生态系统为目标而产生的项目,好比在服务器和桌面环境中。
2009年8月,这个项目更名为 CommonJS,以显示其 API 的更普遍实用性。
2013年5月,Node.js 的包管理器 NPM 的做者 Isaac Z. Schlueter 说 CommonJS 已通过时,Node.js 的内核开发者已经废弃了该规范。
定义
服务器端的 Node.js 遵循 CommonJS规范,该规范的核心思想是容许模块经过 require
方法来同步加载所要依赖的其余模块,而后经过 exports
或 module.exports
来导出须要暴露的接口。
require("module"); require("../file.js"); exports.doStuff = function() {}; module.exports = someValue;
优势:
缺点:
实现:
AMD(异步模块定义)是为浏览器环境设计的,由于 CommonJS 模块系统是同步加载的,当前浏览器环境尚未准备好同步加载模块的条件。
AMD 定义了一套 JavaScript 模块依赖异步加载标准,来解决同步加载的问题。
Asynchronous Module Definition 规范其实只有一个主要接口 define(id?, dependencies?, factory)
,它要在声明模块的时候指定全部的依赖 dependencies
,而且还要当作形参传到 factory
中,对于依赖的模块提早执行,依赖前置。
define("module", ["dep1", "dep2"], function(d1, d2) { return someExportedValue; }); require(["module", "../file"], function(module, file) { /* ... */ });
优势:
缺点:
实现:
Common Module Definition 规范和 AMD 很类似,尽可能保持简单,并与 CommonJS 和 Node.js 的 Modules 规范保持了很大的兼容性。
define(function(require, exports, module) { var $ = require('jquery'); var Spinning = require('./spinning'); exports.doSomething = ... module.exports = ... })
优势:
缺点:
实现:
Universal Module Definition 规范相似于兼容 CommonJS 和 AMD 的语法糖,是模块定义的跨平台解决方案。
EcmaScript6 标准增长了 JavaScript 语言层面的模块体系定义。ES6 模块的设计思想,是尽可能的静态化,使得编译时就能肯定模块的依赖关系,以及输入和输出的变量。CommonJS 和 AMD 模块,都只能在运行时肯定这些东西。
import "jquery"; export function doStuff() {} module "localModule" {}
优势:
缺点:
实现:
能够兼容多种模块风格,尽可能能够利用已有的代码,不只仅只是 JavaScript 模块化,还有 CSS、图片、字体等资源也须要模块化。
前端模块要在客户端中执行,因此他们须要增量加载到浏览器中。
模块的加载和传输,咱们首先能想到两种极端的方式,一种是每一个模块文件都单独请求,另外一种是把全部模块打包成一个文件而后只请求一次。显而易见,每一个模块都发起单独的请求形成了请求次数过多,致使应用启动速度慢;一次请求加载全部模块致使流量浪费、初始化过程慢。这两种方式都不是好的解决方案,它们过于简单粗暴。
分块传输,按需进行懒加载,在实际用到某些模块的时候再增量更新,才是较为合理的模块加载方案。
要实现模块的按需加载,就须要一个对整个代码库中的模块进行静态分析、编译打包的过程。
在上面的分析过程当中,咱们提到的模块仅仅是指JavaScript模块文件。然而,在前端开发过程当中还涉及到样式、图片、字体、HTML 模板等等众多的资源。这些资源还会以各类方言的形式存在,好比 coffeescript、 less、 sass、众多的模板库、多语言系统(i18n)等等。
若是他们均可以视做模块,而且均可以经过require
的方式来加载,将带来优雅的开发体验,好比:
require("./style.css"); require("./style.less"); require("./template.jade"); require("./image.png");
那么如何作到让 require
能加载各类资源呢?
在编译的时候,要对整个代码进行静态分析,分析出各个模块的类型和它们依赖关系,而后将不一样类型的模块提交给适配的加载器来处理。好比一个用 LESS 写的样式模块,能够先用 LESS 加载器将它转成一个CSS 模块,在经过 CSS 模块把他插入到页面的 <style>
标签中执行。Webpack 就是在这样的需求中应运而生。
同时,为了能利用已经存在的各类框架、库和已经写好的文件,咱们还须要一个模块加载的兼容策略,来避免重写全部的模块。
那么接下来,让咱们开始 Webpack 的神奇之旅吧。