模块化
UMD
通过运行时,或者编译时让同一个代码块在使用 CommonJS, cmd 甚至 AMD 的项目中使用。
CommonJS
node.js 就是采用了 CommonJS 规范。在 node.js 中每个文件都被视为单独的模块。
CommonJS 是在运行时同步加载的
module 对象
每个模块中都存在着一个 module 对象,代表当前模块。
- 通过
module.exports属性提供对外输出的接口。- 导出的内容实际上就是
module.exports指向的内容。
- 导出的内容实际上就是
- 提供了名为
exports的变量,指向module.exports。- 相当于在每个文件的头部添加
var exports = module.exports;
- 相当于在每个文件的头部添加
这也就导致了,如果将 exports 指向其他变量,或者是将 module.exports 指向其他变量的时候,都会导致 exports 和module.exports 的指向不同的变量。从而使 exports 的导出无效
require 命令
::读入并执行一个 js 文件,在首次加载后缓存返回的信息。::
// a.js
console.log('init start a');
function a() {
return {
name: 'a',
};
}
console.log('init end a');
module.exports = a;
// b.js
console.log('init start b');
const a = require('./a');
console.log('require end a');
a.age = 3;
const b = require('./a');
console.log(b.age);
// result
// init start b
// init start a
// init end a
// require end a
// 3
从执行结果来看,
b.js文件在首次加载a.js后,立即执行了a.js中的代码。- 在第二次加载
a.js时并未执行a.js中的代码,但是也取到了age这个属性值。说明两次加载的是同一个对象,意味着对require(‘./a’)首次执行后的返回值进行了缓存。
值一旦被输出,模块内的变化就不会影响到这个值。
// a.js
let count = 2;
function increment() {
count++;
console.log('a.js', count);
}
let a = {
count: 2,
increment() {
this.count++;
console.log('a.js执行 a.incremrnt', this.count);
},
};
module.exports = {
count,
increment,
a,
};
// b.js
let { count, increment, a } = require('./a');
console.log('increment 执行前', count);
increment();
console.log('increment 执行后', count);
console.log('a.increment 执行前', a.count);
a.increment();
console.log('a.increment 执行后', a.count);
// result
increment 执行前 2
a.js 3
increment 执行后 2
a.increment 执行前 2
a.js执行 a.incremrnt 3
a.increment 执行后 3
- 仅看
count和incrment:模块内的值的确没有变化 - 但是
a呢,还是有影响。这说明是对导出值的浅拷贝。
ES6 Module
ES6 的模块化规范出来的要比 CommonJS 晚,在这之前已经有了很多 npm 包的诞生。
::ES6 Module 是在预编译阶段加载模块的,核心思想是静态化处理。::
由于 js 中的模块依赖关系是确定的,和运行状态无关
导出: epoxrt , export default
导入: import * from ‘’ 。
使用 node 执行 ES6 Module 时,需要添加指令 --es-module-specifier-resolution=node
例如: node --es-module-specifier-resolution=node init.js
静态化处理
import命令在编译阶段会优先处理,产生声明提升的效果,
// a.js
console.log('I am a.js');
let foo = 1;
export default foo;
// b.js
console.log('I am b.js');
import foo from './a';
console.log(foo);
// 执行b.js results
I am a.js
I am b.js
1
- 导出的是一个引用类型的值,并不会进行缓存
// a.js
let count = 2;
function increment() {
count++;
console.log('a.js', count);
}
export { count, increment };
// b.js
import { count, increment } from './a';
console.log(count);
increment();
console.log(count);
// result
2
a.js 3
3
为什么 export default const 报错
export default 应该为一个赋值语句,为变量 default 赋值。例如
export default a可以理解为export default = aexport default const a就应该被理解为export default = const a,这并不是一个正确的语法
那为什么 export function/class 可以?
- 函数是作为表达式使用的,并绑定
default。 class的本质就是函数。
import
import 可以引用 ES6 module 和 CommonJS
import只能在ES6 module中使用。CommonJS支持import(),提供module.exports作为默认导出。例如import * as com from commonjs
import.meta是一个给JavaScript模块暴露特定上下文的元数据属性的对象。(在vite中实现加载多个文件)