Appearance
开发 npm 包基本知识-js模块化的作用及来由
JS模块化发展历程,CommonJS、AMD、CMD、UMD、ES6模块化
0. 命名空间(“伪”模块化时代)和 IIFE (Immediately Invoked Function Expression) 立即调用函数表达式
js
//命名空间模式
var namespace = {};
namespace.add = function (a, b) {
console.log(a + b);
};
namespace.add(1, 2);
//IIFE (Immediately Invoked Function Expression)
var utils = (function () {
var module = {};
module.multiply = function (a, b) {
console.log(a * b);
};
return module;
})();
utils.mutiply(1, 2);
这种模式需要开发人员知道正确的顺序,比如先执行 vue,才能执行调用 vue 的方法
html
<script src="./vue.js"></script>
<script src="./main.js"></script>
1. 2009 年提出的 - CommonJS 也叫 cjs [ˈkɑːmən]
2009年Mozillag工程师发起CommonJS规范,然后node基于它有了服务端模块概念化
js
//utils.js 文件
function add(a, b) {
console.log(a + b);
}
module.exports.add = add;
//main.js 文件
var add = require("./utils").add;
add(1, 2);
CommonJS 出来后服务端模块化概念形成 CommonJS 读取数据是同步的需要读取服务端数据, 但浏览器也要读取数据,网络延迟,需要很长的时间,由于多个js存在先后依赖关系很难保证加载顺序, 并且造成浏览器假死的状态,然后出现 AMD
2. 2010年 AMD(Asynchronous Module Definition 异步模块定义)
2010年美国程序员James Burk开发了require.js
模块的加载不影响后面定义的运行,等 define 加载模块完成后,再执行 require 回调函数里面的就是依赖函数的输出 AMD 依赖 require.js(一个库)
多个文件可能会有依赖关系,依赖的文件可能要早于被依赖的文件,就可以通过 require.js 解决
js
//utils.js
define ([], function () {
return {
add: function(a, b) {
console. log(a + b)
}
};
});
// main.js 文件
require(['./utils'], function (utils) {
utils.add(1, 2)
}):
但是在使用 require.js 之前我们需要提前加载所需要的依赖
所以 玉伯在开发cjs的时候提出了 CMD规范
3. 2011年 CMD( Common Module Definition)
2011年 支付宝前端玉伯开发了seajs,提出了CMD
规范代表库:sea.js
SeaJS 是一个 JavaScript 模块加载框架,可以实现 JavaScript 的模块化开发及加载机制。 CMD 与 AMD 很类似,不同点在于:AMD 推崇依赖前置、提前执行,CMD 推崇依赖就近、延迟执行。此规范其实是在 sea.js 推广过程中产生的。
js
// AMD
require(["./utils", "a", "b"], function (utils) {
console.log(1);
//还没有用到 utils a,b 等模块,但是AMD已经初始化了所有模块
console.log(2);
utils.add(1, 2);
});
//CMD
define(function (require, exports, module) {
console.log(1);
if (false) {
var utils = require("./utils"); //需要时再require,执行就不会加载
utils.add(1, 2);
}
});
4. UMD (Universal Module Definition) [ˌjuːnɪˈvɜːs(ə)l]
UMD 规范只是一种通用的写法,是在 amd 和 cjs 两个流行而不统一的规范情况下,才催生出 umd 来统一规范的,umd 前后端均可通用。
js
(function (root, factory) {
if (typeof define ==- 'function' && define.amd) {
//AMD
define(['utils'], factory);
] else if (typeof exports ===
'object') {
//CommonJS
var utils = require( 'utils');
module.exports = factory (utils) ;
} else {
// 如果都不是就挂载到全局对象上
root.result = factory(root.utils) ;
}
}(this, function(utils) {
utils.add(1, 2)
})
5. 2015年 ESM(es6)
esm 规范是 es6 原生支持的,很多浏览器开始支持,类似 commonjs 的写法和同、异步加载机制能通过设置 type=module,用于 html 中,而且在 node 中也开始支持啦! export 向外暴露或导出模块 export default xxx; import 引入暴露或导出的模块 import {xx, xx} from ‘./xxx.js’;
js
export const utils = {
add: function (a, b) {
console.log(a + b);
},
};
// main.js 文件
import { utils } from "./utils";
utils.add(1, 2);
PS:注意事项
CommonJS 与 ES6 的区别
CommonJS 模块输出的是一个值的拷贝(浅拷贝),ES6m 模块输出的是值的引用
CommonJS 模块是运行时加载,ES6 模块是编译时输出接口
- 因为 CommonJS 加载的是一个对象(即 module.exports 属性),该对象只有在脚本运行完成才会生成。而 ES6 模块不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成
CommonJS 模块是 Node.js 专用的(其实浏览器也能用),与 ES6 模块不兼容。而ES6模块化在浏览器和node.js中都可以用。
module, exports, require ,global,只需要提供这4个环境变量,然后browserify这个处理了这个4个环境变量,可以在浏览器上运行
在Node中真正用于导出的其实根本不是exports,而是module.exports,module才是导出的真正实现者;
语法上面,两者最明显的差异是,CommonJS 模块使用require()和module.exports,ES6 模块使用import和export。
在node.js使用模块化,需要将 CommonJS 脚本的后缀名都改成.cjs,ES6 模块采用.mjs后缀文件名。或者修改package.son里面的文件,type字段为module或commonjs
模块化历史
requirejs昙花一现,大概在2016年之间的项目,现在几乎不会再使用,seajs也是一样
在nodejs中 可以同时使用commonjs和ES6模块化
在vue-cli构建的项目中,webpack采用commonJS规范,因为webpack基于node运行默认是支持commonJs的
在基于webpack开发的前端项目中,可以直接采用es6模块化规范,比如vue和react等项目,因为webpack集成了babel插件
TS 可以生成各种加载时的代码模块
现在主流都采用ESM
提问
commonjs在浏览器能用吗?
不能直接用,但可以提供 module, exports, require ,global环境变量,支持运行,browserify[ˈbraʊzə(r)ify]/ webapck 就是做这件事
那commonjs对浏览器的意义在哪里?
初期前端模块化的探索产物,为es 发展下一个js语言版本奠定了基石,这就是现在的es6规范
前期没有前端模块化的时候,vue和react是用 node rollup ast编译压缩去除无效代码 打包构建后的产物还是commonjs 但是用Browserify/webapck可以在浏览器运行