要知道 Webpack
解决了什么问题, 先要对他解决的问题进行了解。
先回答我:为什么模块很重要?
答:因为有了模块,我们就可以更方便地使用别人的代码,想要什么功能,就加载什么模块。 但是,这样做有一个前提,那就是大家必须以同样的方式编写模块,否则你有你的写法,我有我的写法,岂不是乱了套!
一、模块化演进过程
阶段1:文件划分方式
每一个功能及其数据状态存放到单独的 js
文件中,约定一个文件表示一个模块,在 html
中用 script
标签引入这些模块,就可以直接在全局使用这些模块中的成员了。
缺点:
- 模块直接在全局工作,大量模块成员污染全局作用域
- 没有私有空间,所有模块内的成员都可以在模块外部被访问或者修改
- 一旦模块增多,容易产生命名冲突
- 无法管理模块与模块之间的依赖关系
- 在维护的过程中也很难分辨每个成员所属的模块
命名空间方式
规定每个模块只能暴露一个全局对象,比如 window.moduleA = { ... }
,所有模块的成员都必须挂载到这个对象上。
只是解决了命名冲突的问题,其他问题依然存在。
阶段2:IIFE(立即执行函数方式)
;(function(){
method1 = function(){ ... }
window.moduleA = {
m1: method1
}
})()
立即执行函数实现了每个模块有自己的私有空间,暴露给外部的成员挂载到全局对象上,解决了全局作用域污染和命名冲突的问题。 立即执行函数带来了私有成员的概念,私有成员在模块内部只能通过闭包的方式去访问。
阶段3:IIFE 依赖参数
;(function($){ // 通过参数,明显表明这个模块的依赖,以后维护过程中不用看模块内部代码就知道模块之间的依赖关系
method1 = function(){ ... }
window.moduleA = {
m1: method1
}
})(jQuery)
以上方式解决了模块组织的问题,但是没有解决模块加载的问题。js
文件是通过 script
标签引入,不受代码控制,如果 script
依然引入被删除的 js
文件,又或者 script
没有引入你的 js
,都有可能导致不必要的问题。
理想的方式是在页面中有一个 JS
入口文件,其余用到的模块通过代码控制,按需加载 。
二、模块化规范的出现
两点需求:
- 一个统一的模块化标准规范
- 一个可以自动加载模块的基础库
1、CommonJS 规范
是 Node.js
中所遵循的模块规范,
约定一个文件就是一个模块,每个模块都有单独的作用域,
通过 module.exports
导出成员,require
函数载入模块,
启动时去加载模块,代码执行过程中使用模块,这种同步的方式在服务端没有什么问题,因为所有的模块都存放在本地硬盘,可以同步加载完成,等待时间就是硬盘的读取时间,但在浏览器端使用这种方式就会造成大量同步加载请求,导致页面加载慢,因此出现了 AMD
规范。
2、AMD(异步模块定义) 规范
Require.js
库,除了实现 AMD
模块化规范,本身也是一个非常强大的模块加载器。预执行,定义当前模块的时候就把依赖模块也执行了。
// AMD 规范定义一个模块(也能用来导入一个模块)
// module1当前模块的模块名,['jquery', './module2.js']定义当前模块的两个依赖的路径,第3个参数是一个函数,像立即执行函数一样为当前模块建立一个私有空间
// return 向外暴露一些成员
define('module1', ['jquery', './module2.js'], function($, module2){
return {
start: function() {
$('body').animate({ margin: '200px' });
module2();
}
}
})
// AMD 规范载入一个模块
// 自动创建一个script标签,请求和执行所对应的模块的代码
require(['./modules/module1.js'], function(module1) {
module1.start();
})
如果项目的模块划分比较细,就会出现在同一个页面中加载比较多的 js
文件,从而导致应用运行效率低。
3、CMD(通用模块定义) 规范
阿里的玉伯提出的,他写了一个叫 seaJS
的库,懒执行,需要使用 a
模块的时候才会执行 a
模块。
// 所有模块通过 define 来定义
define(function (require, exports, module){
var $ = require('jquery');
// 通过 exports 或者 module.exports 对外暴露成员
module.exports = function( {
$('body').append('<p>module2</p>');
})
});
前端模块化规范的最佳实践方式基本实现统一:
- 在
Node.js
环境中,遵循CommonJS
规范来组织模块 - 在浏览器环境中,遵循
ES Modules
规范
三、模块打包工具的出现
模块化开发会划分出很多的模块文件,前端应用运行在浏览器中,所有文件都需要从服务器请求回来,因此必然会导致浏览器频繁发送网络请求,影响应用的工作效率。
前端项目中的 HTML
CSS
这些资源文件也需要被模块化处理。
工具需要具备的能力:
- 把
ES6
代码编译成浏览器能解析的ES5
代码 - 把各种文件
HTML
CSS
JS
图片等分别打包到一起,这样就不用频繁发送网络请求
模块化只在开发阶段需要,能更好地组织我们的代码
Webpack
解决的问题就是:在前端项目中高效地管理和维护项目中的每一个资源