模块化

许多语言都具有模块支持,例如 python 可通过 import 导入模块,CSS 可通过 @import 导入模块,但是 JavaScript 语言没有模块支持,即不同 JS 文件之间不能直接通过诸如 import 这样的方式进行导入使用,本文主要介绍通过 ES6 module 和 CommonJS 进行 JavaScript 文件模块化,AMD/CMD 等不再介绍,参考资料:

1. ES6 Module

ES6 Module 主要有2个命令构成:exportimportexport命令用于规定模块的对外接口,import命令用于输入其他模块提供的功能。

一个模块就是一个独立的文件。该文件内部的所有变量,外部无法获取。如果你希望外部能够读取模块内部的变量、函数或类(class),就必须使用 export 关键字输出该变量。

使用 export 命令定义了模块的对外接口以后,其他 JS 文件就可以通过 import 命令加载这个模块。

/**
 * export 基本写法
 */
export const age_1 = 18
export function sayHi_1() {
    console.log('hi');
}
// 或者
const age_2 = 18
const sayHi_2 = () => { console.log('hi'); }
export {
    age_2,
    sayHi_2
}
// 重命名
const age_3 = 18
const sayHi_3 = () => { console.log('hi'); }
export {
    age_3 as age,
    sayHi_3 as sayHi
}

/**
 * import 基本写法
 */
import { age_1, sayHi_1 } from './es6.js'
// 重命名
import { age_2 as age, sayHi_2 as sayHi } from './es6.js'
// 通过 星号(*)整体加载,即命名空间
import * as myModule from './es6.js'
myModule.age
myModule.sayHi

使用 import 命令的时候,用户需要知道所要加载的变量名或函数名,以 {} 的形式加载,也可以采用到 export default 命令,为模块指定默认输出。注意,一个模块只能有一个默认输出,因此 export default命令只能使用一次 。本质上,export default就是输出一个叫做default的变量或方法,然后系统允许你为它取任意名字。

另外,exportexport default可以同时使用并且互不影响,但是在引入的时候需要先导入默认导出的,在导入单个导入的值。

如果在一个模块之中,先输入后输出同一个模块,import语句可以与export语句写在一起。注意,如下所示,foo 和 bar 尽管进行 import,但是实际上并没有被导入当前模块,只是相当于对外转发了这两个接口,当前不能使用。

通常,借助上面的 export 与 import 的复合写法,我们会把一些模块都归集到一个 index.js 文件中进行导出。

2. CommonJS

CommonJS 中使用 module.exports 导出变量及函数,也可以导出任意类型的值,也可以直接通过 exports 进行导出(不建议!!!):

CommonJs中使用require语法可以导入,如果想要单个的值,可以通过解构对象来获取。

CommonJS一个需要关注的点就是其能够动态导入,但 ES6 Module 不行,因为其是静态的:

3. ES6 Module 与 CommonJS 的区别

CommonJs可以动态加载语句,代码发生在运行时;ES6 模块是编译时输出接口,是静态的,不可以动态加载语句,只能声明在该文件的最顶部。

CommonJs导出值是拷贝,也就是说,一旦输出一个值,模块内部的变化就影响不到这个值,同时可以修改导出的值,但可能会造成变量污染;ES6 Module导出是引用值,并且值都是可读的但不能修改。举例如下:

总结:

  • CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。

  • CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。

  • CommonJS 模块的require()是同步加载模块,ES6 模块的import命令是异步加载,有一个独立的模块依赖的解析阶段。

如果你对内容有任何疑问,欢迎提交 ❕issues✉️ email

最后更新于

这有帮助吗?