应用:服务端模块、客户端模块。
同步加载 synchronous
异步加载 asynchronous
CommonJS
nodejs
同步加载
var math = require('math');
math.add(2, 3);
在服务端,模块文件都存在本地磁盘,读取非常快,所以这样做不会有问题。但是在浏览器端,限于网络原因,更合理的方案是使用异步加载。 这种写法适合服务端,因为在服务器读取模块都是在本地磁盘,加载速度很快。
这对服务器端不是一个问题,因为所有的模块都存放在本地硬盘,可以同步加载完成,等待时间就是硬盘的读取时间。但是,对于浏览器,这却是一个大问题,因为模块都放在服务器端,等待时间取决于网速的快慢,可能要等很长时间,浏览器处于"假死"状态。
AMD
Asynchronous Module Definition
异步加载
requireJS
推崇依赖前置、提前执行
先定义所有依赖,然后在加载完成后的回调函数中执行:
require([module], callback);
require(['clock'],function(clock){
clock.start();
});
CMD
Common Module Definition
异步加载
seaJS
依赖就近,用的时候再require。
推崇依赖就近、延迟执行
define(function(require, exports, module) {
var clock = require('clock');
clock.start();
});
ES6
浏览器和服务器通用
ES6 模块是动态引用,并且不会缓存值,模块里面的变量绑定其所在的模块。
各模块化规范区别
AMD和CMD最大的区别是对依赖模块的执行时机处理不同,而不是加载的时机或者方式不同,二者皆为异步加载模块。 AMD依赖前置,js可以方便知道依赖模块是谁,立即加载;而CMD就近依赖,需要使用把模块变为字符串解析一遍才知道依赖了那些模块,这也是很多人诟病CMD的一点,牺牲性能来带来开发的便利性,实际上解析模块用的时间短到可以忽略。
AMD和CMD最大的区别是对依赖模块的执行时机处理不同,而不是加载的时机或者方式不同,二者皆为异步加载模块。 AMD依赖前置,js可以方便知道依赖模块是谁,立即加载;而CMD就近依赖,需要使用把模块变为字符串解析一遍才知道依赖了那些模块,这也是很多人诟病CMD的一点,牺牲性能来带来开发的便利性,实际上解析模块用的时间短到可以忽略。
本次项目中,由 seajs 转向 requirejs。很大一个原因是因为 seajs社区的凋零,长时间没有人更新的文档给人的不自信感所带来的恐慌。虽然seajs 和 Commonjs 更像,但是 事实证明 AMD 更容易理解 与 使用。
- CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。
CommonJS 模块输出的是值的拷贝,也就是说,一旦输出一个值,模块内部的变化就影响不到这个值。 ES6 模块的运行机制与 CommonJS 不一样。JS 引擎对脚本静态分析的时候,遇到模块加载命令import,就会生成一个只读引用。等到脚本真正执行时,再根据这个只读引用,到被加载的那个模块里面去取值。换句话说,ES6 的import有点像 Unix 系统的“符号连接”,原始值变了,import加载的值也会跟着变。因此,ES6 模块是动态引用,并且不会缓存值,模块里面的变量绑定其所在的模块。
- CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。
运行时加载: CommonJS 模块就是对象;即在输入时是先加载整个模块,生成一个对象,然后再从这个对象上面读取方法,这种加载称为“运行时加载”。
编译时加载: ES6 模块不是对象,而是通过 export 命令显式指定输出的代码,import时采用静态命令的形式。即在import时可以指定加载某个输出值,而不是加载整个模块,这种加载称为“编译时加载”。
CommonJS 加载的是一个对象(即module.exports属性),该对象只有在脚本运行完才会生成。而 ES6 模块不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成。
import、require
import { formatDateToDash, formatDateToSlash } from 'jscom/utils/formatter';
require('./ActivityPopOverForm.scss');
require 是赋值过程并且是运行时才执行, import 是解构过程并且是编译时执行。 require可以理解为一个全局方法,就意味着可以在任何地方执行。 而import必须写在文件的顶部。 require的性能相对于import稍低,因为require是在运行时才引入模块并且还赋值给某个变量, 而import只需要依据import中的接口在编译时引入指定模块所以性能稍高 es6 import 尽量静态化,编译就能确定模块的依赖关系,输入及输出。 因为require是运行时加载,所以import命令没有办法代替require的动态加载功能。 所以引入了import()函数。完成动态加载。
require,exports,module.exports属于CommonJS规范,import,export,export default属于ES6规范 require支持动态导入,动态匹配路径,import对这两者都不支持 require是运行时调用,import是编译时调用 require是赋值过程,import是解构过程 对于export和export default 不同的使用方式,import就要采取不同的引用方式,主要区别在于是否存在{},export导出的,import导入需要{},导入和导出一一对应,export default默认导出的,import导入不需要{} exports是module.exports一种简写形式,不能直接给exports赋值 当直接给module.exports赋值时,exports会失效.
| 区别 | require | import |
|---|---|---|
| 规范 | CommonJS规范 | es6标准 |
| 调用 | 运行时 | 编译时 |
| 实质 | 赋值过程 | 解构过程 |
| 引用 | 任何位置 | 文件头部 |
关于调用
require的引用可以在代码的任何地方。 import语法规范上是放在文件开头。
关于本质
require的结果就是对象、数字、字符串、函数等,再把require的结果赋值给某个变量 目前所有的引擎都还没有实现import,我们在node中使用babel支持ES6,也仅仅是将ES6转码为ES5再执行,import语法会被转码为require
module,export 和 module.export区别
module 对象
console.log(module)
modules = {
children,
exports,
hot,
id,
loaded,
parents,
paths
}
module.id 模块的识别符,通常是带有绝对路径的模块文件名。
module.filename 模块的文件名,带有绝对路径。
module.loaded 返回一个布尔值,表示模块是否已经完成加载。
module.parent 返回一个对象,表示调用该模块的模块。
module.children 返回一个数组,表示该模块要用到的其他模块。
module.exports 表示模块对外输出的值。
require 方法用于加载模块,即加载的是 module.exports 对象
console.log(exports === module.exports); //true
exports 变量
exports 是 module 里的属性,也就是 module.export的引用
为了方便,Node为每个模块提供一个 exports 变量,指向 module.exports。这等同在每个模块头部,有一行这样的命令: var exports = module.exports;,所以上面的example.js 可以改成:
// example.js
let baseNum = 1;
let addOne = function (num) {
return baseNum + num;
}
exports.baseNum = baseNum;
exports.addOne = addOne;
console.log('example', module)
需要注意的是下面两种方式是错误的
exports = addOne; // exports 被重新赋值了,不在指向module.exports
exports.addOne = addOne;
module.exports = 'hello'; // exports 指向 module.exports, module.exports 指向了一个字符串,所以addOne不能被外部访问了