文章目录

  • 1.CommonJS模块
  • 2.异步模块定义
  • 3.通用模块定义
  • 4.ES6模块
  • 4.1模块导出
  • 4.2模块导入


1.CommonJS模块

CommonJS规范概述了同步声明依赖的模块定义。这个模块主要用于在服务器端实现模块化代码组织,但也可用于定义在浏览器中使用的模块依赖。CommonJS模块模块语法不能在浏览器中直接运行。
一般认为,Node.js模块系统使用了CommonJS规范,实际上并不完全正确。Node.js使用了轻微修改版的CommonJS,因为Node.js主要在服务器环境下使用,所以不需要考虑网络延迟问题。
CommonJS模块定义需要使用require()指定依赖,而使用exports对象定义自己的公共API。
在CommonJS中,模块加载是系统执行的同步操作。
将utils.js文件夹中的add模块暴露出去

//utils.js 文件
function add(a,b){
	console.log(a + b)
}
module.exports.add = add

在main.js的文件中引入add模块

const add = require('./utils')
console.log(add(1,2))

CommonJS模块出来后,服务器端的模块概念基本已经形成,但是CommonJS是同步的,服务器端读取本地硬盘,可以很快的完成同步加载。但是浏览器要同步读取服务器端的模就需要很长的时间,并且造成浏览器处于假死的状态,所以出现了异步模块加载AMD。

2.异步模块定义

CommonJS以服务器端为目标环境,能够一次性把所有模块都加载到内存,而异步定义模块(AMD,Asynchronous Module Definition)的模块定义系统则以浏览器为目标执行环境,这需要考虑网络延迟的问题。AMD的一般策略是让模块声明自己的依赖,而运行在浏览器中的模块系统会按需获取依赖,并在依赖加载完成后立即执行依赖它们的模块。

3.通用模块定义

为了统一CommonJS和AMD生态系统,通用模块定义(UMD,Universal Module Definition)规范应运而生。UMD可用于创建这两个系统都可以使用的模块代码。本质上,UMD定义的模块会在启动时检测要使用哪个模块系统,然后进行适当的配置,并把所有逻辑包装在一个立即调用的函数表达式(IIFE)中。虽然这种组合并不完美,但是在很多场景下足以实现两个生态的共存。

4.ES6模块

ES6最大的一个改进就是引入了模块服务。这个规范全方位简化了之前出现的模块加载器,原生浏览器支持意味着加载器及其他预处理都不再必要。从很多方面看,ES6模块系统是集AMD和CommonJS之大成者。

4.1模块导出

ES6模块支持两种导出:命名导出默认导出

命名导出(named export):就好像模块是被导出值的容器
1.行内命名导

export const foo = 'foo';

2.变量声明跟导出不再一行

const foo = 'foo';
export {foo}

3.导出时提供别名,别名必须在export子句的大括号语法中指定,因此声明值,导出值和为导出值提供别名不能在一行完成。

const foo = 'foo';
export { foo as myFoo};

4.因为ES6命名导出可以将模块作为容器,所以可以在一个模块中声明多个命名导出。导出的值可以在导出语句中声明,也可以在导出之前声明。考虑到导出多个值是常见操作,ES6模块也支持对导出声明分组,可以同时为部分或者全部导出值指定别名。

const foo = 'foo';   
const bar = 'bar';   
const baz = 'baz';    
export {foo, bar as myBar, baz}

默认导出(default export):就好像模块与被导出的值是一回事。默认导出使用default关键字将一个值声明为默认导出,每个模块只能有一个默认导出。重复的默认导出会导致SyntaxError。

1.默认导出

const foo = 'foo';
export default foo;

2.ES6模块系统会识别作为别名提供的default关键字。此时,虽然对应的值是使用命名语法导出的,但实际上会成为默认导出:

const foo = 'foo';
export {foo as default}
//上面的代码等价于下面的代码
const foo = 'foo';
export default foo

3.命名导出和默认导出不会冲突,所以ES6支持在一个模块中同时定义这两种导出:

const foo = 'foo';
const bar = 'bar';
export {bar};
export default foo;
//上面的export语句可以组合成一行
const foo = 'foo';
const bar = 'bar';
export {foo as default, bar};

4.注意会导致错误的不同形式

//行内默认导出中不能出现变量声明
export default const foo = 'bar';
//只有标识符可以出现在export子句中
export {123 as foo}
//别名只能在export子句中出现
export const foo = 'foo' as  myFoo;

4.2模块导入

1.模块可以通过使用import关键字使用其他模块导出的值。
2.模块标识符可以是相对于当前模块的相对路径,也可以是指向模块文件的绝对路径。它必须是纯字符串,不能是动态计算的结果。
3.不是必须通过导出的成员才能导入模块。如果不需要模块的特定导出,但仍想加载和执行模块以利用其副作用,也可以只通过路径加载它

import './index.less'

4.直接修改导出的值是不可能的,但是可以修改导出值的属性

//*表示批量导入
import foo, * as Foo './foo.js';
foo = 'foo';	 //错误
Foo.foo = 'foo'; //错误
foo.bar = 'bar'; //允许

5.命名导出可以使用*批量获取并赋值给保存导出集合的别名,而无须列出每个标识符

const foo = 'foo', bar = 'bar', baz = 'baz';
export {foo, bar, baz}
import * as Foo from './foo.js';
console.log(Foo.foo); //foo
console.log(Foo.bar); //bar
console.log(Foo.baz); //baz
//要指名导入,需要把标识符放在import子句中。使用import子句可以为导入的值指定别名
import {foo, bar, baz as myBaz} from './foo.js'
console.log(foo); //foo
console.log(bar); //bar
console.log(myBaz); //baz

6.默认导出就好像整个模块就是导出的值一样。可以使用default关键字并提供别名来导入。也可以不使用大括号,此时指定的标识符就是默认导出的别名:

//下面的代码是等效的
import {default as foo} from './foo.js';
import foo from './foo.js'

7.如果模块同时导出了命名导出和默认导出,则可以在import语句中同时取得它们。可以依次列出特定导出标识符来取得,也可以使用*号来取得:

import foo, {bar,baz} from './foo.js'; //常用
import {default as foo, bar, baz} from './foo.js';
import foo, * as Foo from './foo.js';