模块化

149 阅读3分钟

模块化

UMD

通过运行时,或者编译时让同一个代码块在使用 CommonJS, cmd 甚至 AMD 的项目中使用。

CommonJS

node.js 就是采用了 CommonJS 规范。在 node.js 中每个文件都被视为单独的模块。

CommonJS 是在运行时同步加载的

module 对象

每个模块中都存在着一个 module 对象,代表当前模块。

  1. 通过 module.exports 属性提供对外输出的接口。
    1. 导出的内容实际上就是 module.exports 指向的内容。
  2. 提供了名为 exports 的变量,指向 module.exports
    1. 相当于在每个文件的头部添加 var exports = module.exports;

这也就导致了,如果将 exports 指向其他变量,或者是将 module.exports 指向其他变量的时候,都会导致 exportsmodule.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

从执行结果来看,

  1. b.js 文件在首次加载a.js 后,立即执行了a.js 中的代码。
  2. 在第二次加载 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
  1. 仅看 countincrment :模块内的值的确没有变化
  2. 但是 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

静态化处理

  1. 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
  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 赋值。例如

  1. export default a 可以理解为 export default = a
  2. export default const a 就应该被理解为 export default = const a ,这并不是一个正确的语法

那为什么 export function/class 可以?

  1. 函数是作为表达式使用的,并绑定 default
  2. class 的本质就是函数。

import

import 可以引用 ES6 moduleCommonJS

  1. import 只能在 ES6 module 中使用。
  2. CommonJS 支持import() ,提供 module.exports 作为默认导出。例如 import * as com from commonjs

import.meta是一个给JavaScript模块暴露特定上下文的元数据属性的对象。(在vite中实现加载多个文件)