什么是模块化?
模块
- 现实生活中,我们常见的手机就一个模块化设备(包含屏幕、芯片、内存、电磁、外壳等模块)。
- 在代码程序中,一个前端应用程序由基础组件模块、业务组件模块、网络库模块及各自工具类等模块组成。
模块化开发
本质上就是将应用程序划分成一个个(互相依赖的)模块来开发,然后将小模块组合起来。
- 模块中可以编写属于自己的逻辑代码,有自己的作用域,不会影响到其他的模块;
- 模块可以将自己希望暴露的变量、函数、对象等导出给其模块使用;
好处
- 有效防止命名冲突
- 代码能最大限度复用
- 可维护性高
JavaScript模块化规范
CommonJs规范
- 特点:CommonJs采用同步加载模块方式,磁盘读取速度快。
- 语法:使用require引用和加载模块,exports定义和导出模块,module标识模块。使用require时需要去读取并执行该文件,然后返回exports导出的内容。
- 应用:服务端node,webpack
// index.js
let a = require('./modA.js')
let b = require('./modB.js')
console.log('index.js-1', '执行完毕', a.done, b.done)
// modA.js
exports.done = false
let b = require('./modB.js')
console.log('modA.js-1', b.done)
exports.done = true
console.log('modB.js-2', '执行完毕')
// modB.js
exports.done = false
let a = require('./modA.js')
console.log('modB.js-1', a.done)
exports.done = true
console.log('modB.js-2', '执行完毕')
/*
modB.js-1 false
modB.js-2 执行完毕
modA.js-1 true
modB.js-2 执行完毕
index.js-1 执行完毕 true true
*/
执行顺序:
- node 执行 index.js 文件,发现 require(‘./modA.js’),暂停 index.js 代码执行,进入 modA 模块
- 在 modA 中发现 require(‘./modB.js’),暂停 modA 代码执行,将已执行的部分 modA 代码缓存,随后进入 modB 模块
- 在 modB 中发现require(‘./modA.js’),提取已缓存的部分 modA (因为 modA 代码全部执行完),执行所有的 modB 代码,完毕后,缓存 modB 执行结果,执行栈返回至 modA
- 执行 modA 剩余代码,完毕后,缓存 modA,执行栈返回 index.js
- 在 index.js 发现 require(‘./modBjs’),提取已有的 modB 缓存,执行剩余代码
AMD规范
异步模块定义,所谓异步是指模块和模块的依赖可以被异步加载,他们的加载不会影响它后面语句的运行。有效避免了采用同步加载方式中导致的页面假死现象。AMD代表:RequireJS。
AMD一开始是CommonJS规范中的一个草案,全称是Asynchronous Module Definition,即异步模块加载机制。后来由该草案的作者以require.js实现了AMD规范。
-
特点:
- (1)异步加载:因为面向浏览器端,为了不影响渲染肯定是异步加载。
- (2)依赖前置:所有的依赖必须写在最初的依赖数组中,速度快,但是会浪费资源,预先加载了所有依赖不管你是否用到(存在引入成本,没有考虑按需加载)。
- 语法:通过define方法,将代码定义为模块;通过require方法,实现代码的模块加载。
- 应用:浏览器端,request.js。
//id :可选参数,它指的是模块的名字。
//dependencies:可选参数,定义中模块所依赖模块的数组。
//factory:模块初始化要执行的函数或对象
//define(id?, dependencies?, factory);
define("alpha", ["require", "exports", "beta"], function (require, exports, beta) {
exports.verb = function() {
return beta.verb();
}
});
//module:一个数组,里面的成员就是要加载的模块.
//callback:模块加载成功之后的回调函数。
//require([module], callback);
require(["a","b","c"],function(a,b,c){ //code here });
CMD规范
CMD规范专门用于浏览器端,模块的加载是异步的,模块使用时才会加载执行。CMD规范整合了CommonJS和AMD规范的特点。Sea.js中,所有JavaScript模块都遵循CMD模块定义规范。
-
特点:
- (1)异步加载。
- (2)按需加载/依赖就近:用到了再引用依赖,方便了开发。
- (3)依赖于打包,加载逻辑存在于每个模块中,扩大了模块的体积;加载速度和性能较差。
- 语法:通过define来定义模块,模块还依赖其他模块,在用到的地方引用即可;通过require方法实现代码的模块加载。
- 应用:浏览器端,sea.js。
// module1.js文件
define(function (require, exports, module) {
//内部变量数据
var data = 'atguigu.com'
//内部函数
function show() {
console.log('module1 show() ' + data)
}
//向外暴露
exports.show = show
});
// module2.js文件
define(function (require, exports, module) {
module.exports = {
msg: 'I Will Back'
}
});
// module3.js文件
define(function(require, exports, module) {
const API_KEY = 'abc123'
exports.API_KEY = API_KEY
});
// module4.js文件
define(function (require, exports, module) {
//引入依赖模块(同步)
var module2 = require('./module2')
function show() {
console.log('module4 show() ' + module2.msg)
}
exports.show = show
//引入依赖模块(异步)
require.async('./module3', function (m3) {
console.log('异步引入依赖模块3 ' + m3.API_KEY)
})
});
// main.js文件
define(function (require) {
var m1 = require('./module1')
var m4 = require('./module4')
m1.show()
m4.show()
})
ESM规范
ES6模块的设计思想,是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。所以说ES6是编译时加载,不同于CommonJS的运行时加载(实际加载的是一整个对象),ES6模块不是对象,而是通过export命令显式指定输出的代码,输入时也采用静态命令的形式。
- 特点: 静态编译:在编译的时候就能确定依赖关系,以及输入和输出的变量。
- 语法:模块功能主要由两个命令构成:export和import。export命令用于规定模块的对外接口,import命令用于输入其他模块提供的功能。
- 应用:浏览器端,服务端(nodejs)
// 模块 test.js
var info = {
name: 'name',
age: 18
}
export default info
export var name= '海洋饼干'
export var age = 18
// 引用
import person, {name, age as myAge} from 'test.js'
console.log(person); // { name: 'name', age: 18 }
console.log(name+ '=' + myAge); // 海洋饼干=18
commonJS 和 ES Module 区别
ES Module 静态引入,编译时引入;Commonjs 动态引入,执行时引入。
具体查看:https://zhuanlan.zhihu.com/p/161015809
总结
- CommonJs采用同步加载模块方式,主要用于nodejs在服务器端的编程。
- AMD是require.js在推广过程中对模块定义的规范化产出。运行在浏览器端,采用异步加载模块方式(依赖前置)。
- CMD是SeaJS 在推广过程中对模块定义的规范化产出。运行在浏览器端,采用异步加载模块方式(按需加载/依赖就近)。
- ESM静态编译,完全可以成为浏览器和服务器通用的模块解决方案。
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net