一、什么是模块化?
模块化是指在解决某一个复杂问题或者一系列的杂糅问题时,依照一种分类的思维把问题进行系统性的分解以之处理。模块化是一种处理复杂系统分解为代码结构更合理,可维护性更高的可管理的模块的方式。可以想象一个巨大的系统代码,被整合优化分割成逻辑性很强的模块时,对于软件是一种何等意义的存在。对于软件行业来说:解耦软件系统的复杂性,使得不管多么大的系统,也可以将管理,开发,维护变得“有理可循”。
另一些对于模块化一些专业的定义为:模块化是软件系统的属性,这个系统被分解为一组高内聚,低耦合的模块。那么在理想状态下我们只需要完成自己部分的核心业务逻辑代码,其他方面的依赖可以通过直接加载被人已经写好模块进行使用即可。
既然是模块化设计,那么作为一个模块化系统所必须的能力:
- 定义封装的模块;
- 定义新模块对其他模块的依赖;
- 可对其他模块的引入支持;
本篇,将市面上流行的几种模块化技术进行对比,让大家清楚具体的规范:
- CommonJS(注意:不要与CMD混淆)
- AMD(Asyncronous Module Definition)异步模块定义
- CMD(Common Module Definition)通用模块定义
- UMD(Universal Module Definition)通用模块定义
- ES6 module
二、规范
2.1、CommonJS
- 特点:
- 一个单独的文件就是一个模块;
- 可以多次加载,但只会在第一次加载时运行一次,运行结果被缓存;
- 以后再加载,就直接读取缓存结果;
- 要想让模块再次运行,必须清除缓存;
- 模块加载会阻塞后续代码执行,需要等到模块加载完成才能继续执行(同步加载);
- 环境:服务器环境
- 应用:nodejs的模块规范是参照commonJS实现的
- 语法:
- 导入:require('路径')
- 导出:module.exports和exports
- 注(module.exports和exports的区别):
- exports只是module.exports的一个引用
- 相当于Node为每个模块提供一个exports变量,指向module.exports
- 同在每个模块头部,有一行var exports = module.exports;
// a.js
// 相当于这里还有一行:var exports = module.exports;代码
exports.a = 'Hello world'; // 相当于:module.exports.a = 'Hello world';
// b.js
var moduleA = require('./a.js');
console.log(moduleA.a); // 打印出:hello world
---------------------------------------------------------------------------------
// 稍微复杂点的例子
var $ = require('jquery');
var _ = require('underscore');
// methods
function a(){}; // private because it's omitted from module.exports
function b(){}; // public because it's defined in module.exports
function c(){}; // public because it's defined in module.exports
// exposed public methods
module.exports = {
b: b,
c: c
};
2.2、AMD
- 特点:
- 异步加载
- 管理模块之间的依赖性(多个JS文件依赖时,依次加载最早被依赖的JS文件)
- 环境:浏览器环境
- 应用:requireJS是参照AMD规范实现的
- 语法:
- 导入:require( ['模块名称'], function (模块变量引用){ 代码 } );
- 导出:define(function (){return '值');
// a.js
define(function (){
return {
a:'hello world'
}
});
// b.js
require(['./a.js'], function (moduleA){
console.log(moduleA.a); // 打印出:hello world
});
---------------------------------------------------------------------------------
// 稍微复杂点的例子
define(['jquery', 'underscore'], function ($, _) {
// methods
function a(){}; // private because it's not returned
function b(){}; // public because it's returned
function c(){}; // public because it's returned
// exposed public methods
return {
b: b,
c: c
}
});
2.3、CMD
- 特点:
- CMD是在AMD基础上改进的一种规范;
- 与AMD不同在于对依赖模块的执行时机处理不同:
- CMD是就近依赖
- AMD是前置依赖
- 环境:浏览器环境
- 应用:seaJS是参照CMD规范实现的;requireJS最新几个版本也参照了CMD规范;
- 语法:
- 导入:define(function(require, exports, module) {});
- 导出:define(function (){return '值');
// a.js
define(function (require, exports, module){
exports.a = 'hello world';
});
// b.js
define(function (require, exports, module){
var moduleA = require('./a.js');
console.log(moduleA.a); // 打印出:hello world
});
2.4、UMD
- 特点:
- 兼容AMD和CommonJS规范的同时,还兼容全局引用的方式;
- 环境:浏览器环境
- 应用:无
- 语法:无
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD
define(['jquery'], factory);
} else if (typeof exports === 'object') {
// Node, CommonJS之类的
module.exports = factory(require('jquery'));
} else {
// 浏览器全局变量(root 即 window)
root.returnExports = factory(root.jQuery);
}
}(this, function ($) {
//方法
function myFunc(){};
//暴露公共方法
return myFunc;
}));
---------------------------------------------------------------------------------
// 稍微复杂点的例子
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD
define(['jquery', 'underscore'], factory);
} else if (typeof exports === 'object') {
// Node, CommonJS-like
module.exports = factory(require('jquery'), require('underscore'));
} else {
// Browser globals (root is window)
root.returnExports = factory(root.jQuery, root._);
}
}(this, function ($, _) {
// methods
function a(){}; // private because it's not returned
function b(){}; // public because it's returned
function c(){}; // public because it's returned
// exposed public methods
return {
b: b,
c: c
}
}));
2.5、ES6 module
- 特点:
- 按需加载(编译时加载)
- import和export命令只能在模块的顶层,不能在代码块之中
- 环境:浏览器环境
- 应用:ES6最新语法支持规范;
- 语法:
- 导入:import {模块名A,模块名B...} from '模块路径';
- 导出:export和export default
- import('模块路径').then()方法
- 注意:
- export只支持对象形式导出,不支持值的导出
- export default命令用于指定模块的默认输出,只支持值导出
- export default只能指定一个,本质上它就是输出一个叫做default的变量或方法
/*错误的写法*/
// 写法一
export 1;
// 写法二
var m = 1;
export m;
// 写法三
if (x === 2) {
import MyModual from './myModual';
}
/*正确的三种写法*/
// 写法一
export var m = 1;
// 写法二
var m = 1;
export {m};
// 写法三
var n = 1;
export {n as m};
// 写法四
var n = 1;
export default n;
// 写法五
if (true) {
import('./myModule.js')
.then(({export1, export2}) => {
// ...·
});
}
// 写法六
Promise.all([
import('./module1.js'),
import('./module2.js'),
import('./module3.js'),
])
.then(([module1, module2, module3]) => {
···
});