CommonJS

Node 模块化遵循的是commonjs规范,CommonJs定义的模块分为: 模块标识(module)、模块导出(exports) 、模块引用(require)。
在node中,一个文件即一个模块,使用exports和require来进行处理。
导出形式有2种,module.exports 和 exports 导出:

  1. // 第一种
  2. module.exports = { // 需要导出的对象
  3. ....
  4. }
  5. // 第二种
  6. exports.a = 'hello world';
  7. // common.js文件里的this指向 module.exports
  8. console.log(this === module.exports) // true
  • 一个模块真正导出的是 module.exports 的值,exports只是 module.exports 的一个引用。通过exports.XXX来修改module.exports.XXX
  • 尽量使用只module.exports

导入形式:

  1. const index = require('./index');

require命令用于加载模块文件。require命令的基本功能是,读入并执行一个JavaScript文件,然后返回该模块的exports对象。如果没有发现指定模块,会报错

module

module为该模块运行时生成的模块标识对象,标识该模块的一些信息。
module参数说明:

  • id 为当前文件
  • exports 为当前node文件模块儿导出的值
  • parent 为父级调用的module对象,如果为null则该文件没有被调用
  • filename 为当前文件名
  • loaded 是否被加载
  • children 引入模块数组,数组项格式同module
  • paths 为node模块儿 node_modules 模块儿查找路径,一直查到根目录

最佳实践

  • 导出对象时用module.exports
  • 导出变量或方法时用exports
  • exports与module.exports最好不要同时使用,exports不要重新赋值到新对象,否则引用无效。

ES6模块化

https://zh.javascript.info/import-export
常见的导出形式:

  1. export default obj;
  2. export const name = "hello world";
  3. export { name1, name2, …, };

导入形式:

  1. import obj, { name } from "./exp/exports";

export default

  • 通过export方式导出,在导入时要加{ },export default则不需要
  • export default表示一个模块默认的对外接口,一个模块只能有一个export default。

export default 和 export 导出的都是可以修改的。区别在于 export default 如果导出的是对象,则只能修改该对象下的属性,如果导出的是基本类型,则不能修改。export 不论导出的是对象还是基本类型,都是可以修改的。

as别名

在import和export中可以这样用:
export

  1. export { name as hello };

import

  1. import { hello as name } from './index';

直接导入并运行js import './index.js'

最佳实践

1.当导出一个变量时,推荐使用export default。
2.当导出多个变量时,推荐使用export。
3.当需要导出多个变量且需要默认导出时,可以同时使用export和export default。

对比

  • CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。
  • CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。
  • CommonJS 模块的require()是同步加载模块,ES6 模块的import命令是异步加载,有一个独立的模块依赖的解析阶段。
  • ES6 模块之中,顶层的this指向undefined;CommonJS 模块的顶层this指向当前模块的输出module.exports,这是两者的一个重大差异。
  • 循环加载:CommonJS 遇到循环依赖的时候,只会输出已经执行的部分,后续的输出或者变化,是不会影响已经输出的变量。而ES6模块相反,使用import加载一个变量,变量不会被缓存,真正取值的时候就能取到最终的值;

参考:

https://juejin.im/post/6844903945383444494 https://juejin.im/post/6844904067651600391 https://es6.ruanyifeng.com/#docs/module-loader

AMD

AMD是”Asynchronous Module Definition”的缩写,意思就是”异步模块定义”。它采用异步方式加载模块,模块的加载不影响它后面语句的运行。所有依赖这个模块的语句,都定义在一个回调函数中,等到加载完成之后,这个回调函数才会运行。其中 RequireJS 是最佳实践者。

使用define来定义模块,return来输出接口, require来加载模块

  1. define("alpha", ["require", "exports", "beta"], function (require, exports, beta) {
  2. exports.verb = function() {
  3. return beta.verb();
  4. //Or:
  5. return require("beta").verb();
  6. }
  7. });

CMD

CMD(Common Module Definition - 通用模块定义)规范主要是Sea.js推广中形成的,一个文件就是一个模块,可以像Node.js一般书写模块代码。主要在浏览器中运行,当然也可以在Node.js中运行。
它与AMD很类似,不同点在于:AMD 推崇依赖前置、提前执行,CMD推崇依赖就近、延迟执行。

  1. // a.js
  2. define(function(require, exports, module) {
  3. console.log('a.js执行');
  4. })
  5. // b.js
  6. define(function(require, module, exports) {
  7. console.log('b.js执行');
  8. })
  9. // index.js
  10. define(function(require) {
  11. var a = require('a');
  12. var b = require('b');
  13. console.log(a);
  14. console.log(b);
  15. })

UMD

UMD(Universal Module Definition - 通用模块定义)模式,该模式主要用来解决CommonJS模式和AMD模式代码不能通用的问题,并同时还支持老式的全局变量规范。

UMD 先判断是否支持 Node.js 的模块(exports)是否存在,存在则使用 Node.js 模块模式。再判断是否支持 AMD(define 是否存在),存在则使用 AMD 方式加载模块。

rollup的 umd 打包结果:

  1. (function (global, factory) {
  2. typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
  3. typeof define === 'function' && define.amd ? define(factory) :
  4. (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Vue = factory());
  5. })(this, (function () {
  6. 'use strict';
  7. //...
  8. }));