JavaScript 的模块规范说明
Js协议和规范
例如下边的协议
- CJS(Commonjs)
- AMD(Asynchronous Module Definition)
- ESM
- UMD
CommonJS
最先大规模使用在nodejs上,是同步加载模块,主要用于服务端,也有在客户端使用cjs同步导入(使用),在网络环境差的情况下容易导致程序假死
, ADM
规范便应用而生
- 导出
// module.js
module.exports = {
name: 'lindaidai',
sex: 'boy'
}
// module.js
exports.name = 'lindaidai';
exports.sex = 'boy'
我理解为导出的是modeule.exports 其中module
意味着文件本身,exports
是要导出的对象
- 导入
// 默认导入的就是js文件后缀名字
const module = require('./module');
AMD
AMD(异步模块定义)是为浏览器环境设计的,因为 CommonJS 模块系统是同步加载的,当前浏览器环境还没有准备好同步加载模块的条件。
define([module-name?],[array-of-dependencies?],[module-factory-or-object]);
- module-name:模块标识,可以省略
- array-of-dependencies:所依赖的模块数组,可以省略
- module-factory-or-object:模块的实现或者一个 JavaScript 对象
- 导出
define(['package/lib'], function(lib){
function foo(){
lib.log('hello world!');
}
// 导出的回调
return {
foo: foo
};
});
上方的模块意味这要导出一个含有依赖package/lib
模块方法的一个模块,换句话说就是一个匿名模块依赖package/lib
模块
//在模块定义内部引用依赖:
define(function(require) {
var lib = require('package/lib');
function foo(){
lib.log('hello world!');
}
return {
foo
}
});
- 导入
require([module], callback);
第一个参数[module],是一个数组,里面的成员就是要加载的模块;第二个参数callback,则是加载成功之后的回调函数。类似于Promise.all
回调的参数个数是根据前边的请求模块个数决定的
AMD和Commonjs导出规范略有差异,但是可以共存,这也为UMD的模块所能实现的基础,最下方会有demo代码
(function(golbal, factory){
// AMD
if(typeof define === "function" && define.amd)
define(factory);
// CommonJS
else if(typeof require === "function" && typeof module === "object" && module && module.exports)
module.exports = factory();
})(this, function(){
// 定义类
function Utils(name) {
this._name = name;
}
// 定义类方法
Utils.prototype.sayHi = function() {
console.log("Hi, I am " + this._name);
};
// 定义静态方法
Utils.add = function(a, b) {
return a + b;
}
// 将类 Utils 作为当前文件的模块返回
return Utils;
});
ESM(ES6 Modules)
ECMA Script Modules,是在 ES6 语言层面提出的一种模块化标准。这个DDDD // 常见的导入导出:
// 导入
import Vue from 'vue'
import VueRouter from 'vue-router'
// 导出
export const name = 'gsy'
export default {
name: 'gsy'
}
UMD(Universal Module Definition)
Universal Module Definition,同时兼容 CJS 和 AMD,并且支持直接在前端用 <script src="lib.umd.js"></script>
的方式加载。现在还在广泛使用,不过可以想象 ESM 和 IIFE 逐渐代替它。
(function(root, factory) {
if (typeof module === 'object' && typeof module.exports === 'object') {
console.log('是commonjs模块规范,nodejs环境')
var depModule = require('./umd-module-depended')
module.exports = factory(depModule);
} else if (typeof define === 'function' && define.amd) {
console.log('是AMD模块规范,如require.js')
define(['depModule'], factory)
} else if (typeof define === 'function' && define.cmd) {
console.log('是CMD模块规范,如sea.js')
define(function(require, exports, module) {
var depModule = require('depModule')
module.exports = factory(depModule)
})
} else {
// iife
console.log('没有模块环境,直接挂载在全局对象上')
root.umdModule = factory(root.depModule);
}
}(this, function(depModule) {
console.log('我调用了依赖模块', depModule)
// ...省略了一些代码,去代码仓库看吧
return {
name: '我自己是一个umd模块'
}
}))
IIFE(Immediately Invoked Function Expression)
Immediately Invoked Function Expression(立即执行函数),只是一种写法,可以隐藏一些局部变量,前端人要是不懂这个可能学的是假前端。可以用来代替 UMD 作为纯粹给前端使用的写法。
npm使用模块的优先级
由于我们使用的模块规范有 ESM 和 commonJS 两种,为了能在 node 环境下原生执行 ESM 规范的脚本文件,.mjs 文件就应运而生。
当存在 index.mjs
和 index.js
这种同名不同后缀的文件时,import './index' 或者 require('./index') 是会优先加载 index.mjs
文件的。
也就是说,优先级 mjs
> js
mjs 是 EcmaScript 模块的扩展 package.json文件的字段区别
- main : 定义了 npm 包的入口文件,browser 环境和 node 环境均可使用
- module : 定义 npm 包的 ESM 规范的入口文件,browser 环境和 node 环境均可使用
- browser : 定义 npm 包在 browser 环境下的入口文件