一、背景
为什么使用模块化
侧重的功能的封装,主要是针对Javascript代码,隔离、组织复制的javascript代码,将它封装成一个个具有特 定功能的的模块。
模块可以通过传递参数的不同修改这个功能的的相关配置,每个模块都是一个单独的作用域,根据需要调用。 一个模块的实现可以依赖其它模块。
不引入的缺点--为什么引入
index.html中完成某一功能,以下代码
<script src="util.js"></script>
<script src="a-util.js"></script>
<script src="a.js"></script>
- 需引用的js文件中的代码中的函数必须是全局变量,才能暴露给适用方,造成全局变量污染(被其他覆盖)
- 引用顺序不能变,按script标签书写顺序加载
- 文件依赖关系靠开发者主观解决
- 代码耦合度过高:代码重复率过高,则会导致本地资源包过大。
优点
- 解决命名冲突问题()
- 避免过多的文件依赖
- 模块版本管理
- 提高代码的可维护性,代码方便重用,别人开发的模块直接拿过来就可以使用,不需要重复开发法类似的功能。
- 前端性能优化(可实现异步加载模块)
- 跨环境共享模块(可实现模块的跨服务器和浏览器共享)
发展史:
- 普通的函数封装
- 封装成对象
- 使用自执行函数,避免变量污染(function())()
- 通过多级命名空间,但是代码变得繁琐。如:
app.util.modA = xxx; // 定义
app.tools.modA = xxx;
app.tools.modA.format = xxx;
app.tools.modA.format(); // 使用
二、JS的模块规范
Common JS
- 特点:
同步加载- 服务器端的模块化规范
- Node JS 基于其实现
- 使用
- 定义(导出):module.exports属性表示当前模块对外暴露的接口,其他文件加载该模块,实际上就是读取module.exports变量;
// 导出
module.exports = {
add:function(x,y){}
};
- 加载:一个文件就是一个模块,require方法用来加载模块,该方法读取一个文件并执行。(同步加载)
// 导入
var math=require("module");
math.add(2,3)
- 优点
- 简单容易使用
- 服务器端模块便于复用
- 缺点
- 不适应浏览器--所以有了AMD
- 因为require同步加载,会造成浏览器假死。
为什么浏览器不能使用同步加载,服务端可以?
对于服务器端而言,所有模块放在本地硬盘,可以同步加载完成,等待时间就是硬盘读取的时间,但是,对于浏> 览器端,模块放在服务器端,等待时间取决于网速的快慢,可能要等待很长时间,浏览器处于“假死”状态
AMD
- 特点:
异步加载- 浏览器端的模块化规范
- require.js基于其实现
- 使用: 定义模块:
define([module], function(module){})
module为本模块依赖的模块
回调函数通过return将想暴露的暴露出去
加载模块:
require([module], function(module){});
-
优点:
- 适合在浏览器环境中异步加载模块
- 可以并行加载多个模块
-
缺点:
- 提高了开发成本,代码的阅读和书写比较困难,模块定义方式的语义不顺畅
- 不符合通用的模块化思维方式,是一种妥协的实现
-
require JS执行流程
- require函数检查依赖的模块,根据配置文件,获取js文件的实际路径
- 根据js文件实际路径,在dom中插入script节点,并绑定onload事件来获取该模块加载完成的通知。
- 依赖script全部加载完成后,调用回调函数
CMD(同用模块定义)
- 特点:
异步加载- 浏览器端的的模块化规范
- sea.js基于其实现
- 使用:
- 定义模块:
- 加载模块:
define(function(require, exports, module){
var a = require('a');
a.foo();
};
AMD和CMD对比
AMD和CMD最大的区别是对依赖模块的
执行时机处理不同,而不是加载的时机或者方式不同,二者皆为异步加载模块;
AMD 特点:
- 依赖前置---js可以方便知道依赖模块是谁,立即加载;
- 提前执行依赖的模块---执行顺序不一定是先1后2;
- API 一个当多个使用。
CMD 特点:
- 依赖就近---需要使用把模块变为字符串解析一遍才知道依赖了那些模块;
- 延迟执行依赖的模块,----只有在用到某个模块的时候再去require;
- API 严格区分,职责单一,一个模块只做一件事。
// AMD
define(['./a','./b'], function(a,b){ // 依赖前置-依赖必须一开始就写好 ,提前执行
a.doSomething();
...
b.doSomething();
})
// CMD
define(function(require, exports, module){
var a = require('a'); // 依赖就近
a.doSomething(); // 延迟执行
...
var b= require('b');
b.doSomething();
...
};
三、ES6的模块化
思想
- 尽量的静态化、使得编译时就能确定模块的依赖关系,以及输入和输出的变量(CommonJS和AMD模块,都只能在运行时确定这些东西)。
- 浏览器和服务器的模块规范
- 使用:
导出:
var a=()=>{};
var b=23;
export {
a as A,
b
}
导入:
import {a,b} from 'react'
- 优点:
- 容易进行静态分析
- 面向未来的 EcmaScript 标准
- 缺点:
- 原生浏览器端还没有实现该标准
- 全新的命令字,新版的 Node.js才支持