前言
目前流行的js模块化规范有CommonJS、AMD、CMD以及ES6的模块系统
三种常见的js模块化规范
1:CommonJS
伴随着NodeJS
的兴起,能让JS在任何地方运行,特别是服务端,也达到了具备开发大型项目的能力,所以CommonJS
营运而生。Node.js
是commonJS
规范的主要实践者。
commonJS
用同步的方式加载模块。在服务端,模块文件都存在本地磁盘,读取非常快,所以这样做不会有问题。但是在浏览器端,限于网络原因,更合理的方案是使用异步加载。
(1):规范
- 一个文件就是一个模块,拥有单独的作用域
- 普通方式定义的变量、函数、对象都属于该模块内
- 通过
require
来加载模块 - 通过
exports
和module.exports
来暴露块中的内容
(2):注意
- 当
exports
和module.exports
同时存在时,module.exports
会覆盖exports
- 当模块内全是
exports
时,就等同于module.exports
exports
就是module.exports
的子集- 所有代码都运行在模块作用域,不会污染全局作用域
- 模块可以多次加载,但只会在第一次加载时候运行,然后运行结果就被缓存了,以后再加载,就直接读取缓存结果
- 模块加载顺序,按照代码出现的顺序同步加载
(3):使用
暴露模块:
// 1:使用module.exports = value向外暴露一个对象
module.exports = {
foo() {
console.log('moudle1 foo()')
}
}
// 2:使用module.exports = value向外暴露一个函数
exports.foo = function () {
console.log('module3 foo()')
}
引入模块:
var module = require(模块名或模块路径)
// 例如:
//引用模块
let module1 = require('./modules/module1')
let module2 = require('./modules/module2')
//使用模块
module1.foo()
module2()
2:AMD
Asynchronous Module Definition
,异步加载模块。它是一个在浏览器端模块化开发的规范,不是原生js的规范,使用AMD
规范进行页面开发需要用到对应的函数库,RequireJS
。
AMD
规范采用异步方式加载模块,模块的加载不影响它后面语句的运行。所有依赖这个模块的语句,都定义在一个回调函数中,等到加载完成之后,这个回调函数才会运行。
使用require.js
实现AMD
规范的模块化:用require.config()
指定引用路径等,用define()
定义模块,用require()
加载模块。
(1):使用
定义没有依赖的模块(dataService.js
)
define(function () {
let msg = 'atguigu.com'
function getMsg() {
return msg.toUpperCase()
}
return {getMsg}
})
定义有依赖的模块(alerter.js
)
define(['dataService', 'jquery'], function (dataService, $) {
let name = 'Tom2'
function showMsg() {
$('body').css('background', 'gray')
alert(dataService.getMsg() + ', ' + name)
}
return {showMsg}
})
引入模块
(function () {
//配置
require.config({
//基本路径
baseUrl: 'js/',
//映射: 模块标识名: 路径
paths: {
//自定义模块
'alerter': 'modules/alerter',
'dataService': 'modules/dataService',
//库模块
'jquery': 'libs/jquery-1.10.1',
'angular': 'libs/angular'
},
//配置不兼容AMD的模块
shim: {
angular: {
exports: 'angular'
}
}
})
//引入模块使用
require(['alerter', 'angular'], function (alerter, angular) {
alerter.showMsg()
console.log(angular);
})
})()
html引入main.js
<!DOCTYPE html>
<html>
<head>
<title>Modular Demo 2</title>
</head>
<body>
<script type="text/javascript" src="js/libs/require.js" data-main="js/main.js"></script>
</body>
</html>
(2):主要解决的问题
- 文件可能有依赖关系,被依赖的文件需要早于依赖它的文件加载到浏览器
- js加载的时候浏览器会停止页面渲染,加载文件愈多,页面相应事件就越长
- 异步前置加载
3:ES6
ES6
在语言标准的层面上,实现了模块功能,而且实现得相当简单,旨在成为浏览器和服务器通用的模块解决方案。其模块功能主要由两个命令构成:export
和import
。export
命令用于规定模块的对外接口,import
命令用于输入其他模块提供的功能。
其实ES6还提供了export default
命令,为模块指定默认输出,对应的import
语句不需要使用大括号。这也更趋近于AMD的引用写法。
ES6
的模块不是对象,import
命令会被 JavaScript
引擎静态分析,在编译时就引入模块代码,而不是在代码运行时加载,所以无法实现条件加载。也正因为这个,使得静态分析成为可能。
(1):使用
export
可以导出的是一个对象中包含的多个属性、方法。export default
只能导出一个可以不具名的函数。我们可以通过import
进行引用。同时,我们也可以直接使用require
使用,原因是webpack
起了server
相关。
暴露模块:
// 用export一个个暴露
// 暴露函数
export function foo() {
console.log('module1 foo()');
}
// 暴露函数对象
export let bar = function () {
console.log('module1 bar()');
}
// 暴露数组
export const DATA_ARR = [1, 3, 5, 1]
// 用export一起暴露
function fun1() {
console.log('module2 fun1() ' + data);
}
function fun2() {
console.log('module2 fun2() ' + data);
}
export {fun1, fun2}
// export default
export default {
name: 'Tom',
setName: function (name) {
this.name = name
}
}
2.import
import {fn} from ‘./xxx/xxx’ ( export 导出方式的 引用方式 )
import fn from ‘./xxx/xxx1’ ( export default 导出方式的 引用方式 )