《Webpack原理与实践》课程之Webpack究竟解决了什么问题

663 阅读3分钟

要知道 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 解决的问题就是:在前端项目中高效地管理和维护项目中的每一个资源