模块化
本文主要关注点:AMD/CMD
差异;CommonJS/ES6模块化
差异;4种模块化规范的进步,解决的问题
模块化的优点:避免命名冲突;更好的分离,按需加载;更高复用性;高可维护性;
总结:
1、CommonJS
规范主要用于② 服务端编程;①加载模块是同步的,不适合在浏览器环境,存在 阻塞加载;③输出一个值的拷贝;④运行时加载;
浏览器资源是异步加载的,因此有了AMD、CMD解决方案。
2、AMD/RequireJS
规范在浏览器环境中 ① 异步加载模块;而且可以②并行加载多个模块;③依赖前置,④加载模块后直接执行,无法保证执行顺序
3、CMD/SeaJS
规范与AMD规范很相似,都用于浏览器编程,①异步;②依赖就近,③加载后直到调用才按需执行
4、ES6
在语言标准的层面上,实现了模块功能,而且实现得相当简单,完全可以取代 CommonJS 和 AMD 规范,成为浏览器和服务器 通用的模块解决方案 - ①异步,②输出的是值的引用,③编译时输出(静态解析时生成)
一、前景提要 - 模块化
- 模块化的优点
- 避免命名冲突(减少命名空间污染)
- 更好的分离, 按需加载
- 更高复用性
- 高可维护性
二、AMD/CMD 的区别
1、AMD推崇 依赖前置,在定义模块的时候就要声明其依赖的模块
2、CMD推崇 就近依赖,只有在用到某个模块的时候再去 require
最大的区别:对依赖模块的 执行时机处理不同,注意不是加载的时机或者加载方式不同(都是异步加载)
// AMD - 依赖必须一开始就写好
define(['./a', './b'], function(a, b) {
a.doSth();
b.doSth();
})
// CMD - 依赖可以就近书写
define(function(require, exports, module) {
let a = require('./a');
a.doSth();
// ...
let b = require('./b');
b.doSth();
})
AMD异步模块定义 | CMD通用模块定义 | |
---|---|---|
工具库 | RequireJS | SeaJS |
依赖 | 依赖前置,js直接找到依赖模块,立即加载 | 就近依赖,需要使用把模块变为字符串解析一遍才知道依赖了那些模块,以性能换便利 |
执行时机 | 加载模块完成后直接执行,所有模块都加载执行完后会进入 require 的回调函数,执行主逻辑,依赖模块的 执行顺序和书写顺序不一定一致 |
加载完某个依赖模块后并不执行,在所有依赖模块加载完成后进入主逻辑,遇到 require 语句的时候才执行对应的模块,模块的 执行顺序和书写顺序是完全一致的 |
三、CommonJS 和 ES6 区别 - 代码部分在详解
CommonJS 模块 | ES6 模块 import | |
---|---|---|
require exports |
import,export,default,as,from |
|
require 需要代码同步执行 - 不适合浏览器端 |
异步 | |
输出 | 输出的是一个值的拷贝 (一旦输出一个值,模块内部的变化就影响不到这个值) |
输出的是值的引用 (动态引用,脚本执行时,再根据引用,到模块里面取值,若原始值变了, import 加载的值也会跟着变) |
时机 | 运行时加载,加载的是 整个模块 - 所有接口,只有在 脚本运行 完才会生成 | 编译时输出 接口,可以单独加载某个接口,在代码 静态解析 阶段就会生成 |
加载原理 | 一个模块就是一个脚本,require 命令第一次加载该脚本,就会执行整个脚本,然后在内存生成一个对象以后需要用到这个模块的时候,就会到 exports 属性上面取值。也就是说,不会再次执行该模块,而是到 缓存 之中取值,只会在第一次加载时运行一次 |
静态化 |
四、4个模块化规范的详解
1/4、CommonJS
2009年 Nodejs
发布,采用 CommonJS
模块规范。
-
特点 1、每个文件都是一个模块实例,代码运行在 模块作用域,不会污染全局作用域。
2、文件内通过
require
对象引入指定模块,通过exports
对象来向外暴漏API,文件内定义的变量、函数,都是 私有 的,对其他文件不可见3、每个模块 加载一次之后就会被缓存
4、所有文件加载均是 同步 完成,加载的顺序,按照其在代码中出现的顺序
5、模块输出的是一个 值的拷贝,模块内部的变化不会影响该值
-
缺点
1、发送多个请求,模块 同步加载,资源消耗和等待时间 ,适用于 服务器编程
2、引入的 js 文件顺序不能搞错,否则会报错
-
基本语法
2/4、AMD异步模块定义/RequireJS
AMD - Asynchronous Module Definition
,异步模块定义
-
定义
采用 异步 方式加载模块,模块的加载不影响它后面语句的运行。所有依赖这个模块的语句,都定义在一个 回调函数 中,等到所有依赖加载完成之后(依赖前置),这个回调函数才会运行(书写顺序和执行顺序不一定一致)。
-
工具
RequireJS
- 一个工具库,主要用于客户端的 模块管理。它的模块管理遵守AMD规范;RequireJS
的基本思想是,通过define
方法 - 将代码定义为模块,通过require
方法 - 实现代码的模块加载。 -
案例
// 定义没有依赖的模块: define(function(){ return 模块 }) // 定义有依赖的模块: define(['module1', 'module2'], function(m1, m2){ return 模块 })
例如:
引入使用模块:
3/4、CMD/SeaJS
CMD - 规范专门用于浏览器端,阿里借鉴整合了 Commonjs
的规范与 AMD
规范并进行了改进,模块的加载是异步的,模块使用时才会加载执行。(依赖就近)
-
模块输出
只有一个出口:
module.exports = value/exports.xxx=value
-
加载模块:
require(xxx)
SeaJs - CMD规范的实现,跟 RequireJs
类似,CMD是 SeaJs
推广过程中诞生的规范
-
案例
// 定义没有依赖的模块 define(function(require, exports, module){ exports.xxx = value module.exports = value }) // 定义有依赖的模块 define(function(require, exports, module){ //引入依赖模块(同步) var module2 = require('./module2') //引入依赖模块(异步) require.async('./module3', function (m3) { }) //暴露模块 exports.xxx = value })
引入使用模块:
define(function (require) {
var m1 = require('./module1')
var m4 = require('./module4')
m1.show()
m4.show()
})
例如:
4/4、ES6
2015年,ES6规范中,终于将 模块化 纳入 JavaScript 标准
-
设计思想
是尽量的静态化,使得 编译时 就能确定模块的依赖关系,以及输入和输出的变量
关键字有
import,export,default,as,from
export
- 命令用于规定模块的对外接口import
- 命令用于输入其他模块提供的功能export default
命令,为模块指定默认输出