这是我参与「第五届青训营 」伴学笔记创作活动的第 2 天
CommonJS
概述
Node 应用由模块组成,采用 CommonJS 模块规范。每个文件就是一个模块,有自己的作用域。在一个文件中定义的变量、函数、类,都是私有的,对其他文件不可见。在服务器端,模块的加载是运行时同步加载的;在浏览器端,模块需要提前编译打包处理。
特点
- 所有代码都运行在模块作用域,不会污染全局作用域。
- 模块可以多次加载,但是只会在第一次加载时运行一次,然后运行结果就被缓存了,以后再加载,就直接读取缓存结果。要想让模块再次运行,必须清除缓存。
- 模块加载的顺序,按照其在代码中出现的顺序。
基本语法
- 暴露模块:
module.exports = value或exports.xxx = value - 引入模块:
require(xxx),如果是第三方模块,xxx 为模块名;如果是自定义模块,xxx 为模块文件路径。
模块的加载机制
CommonJS 模块的加载机制是,输出的是被输出的值的拷贝。也就是说,一旦输出一个值,模块内部的变化就影响不到这个值。这点与ES6 模块化有重大差异,请看下面这个例子:
// lib.js
var counter = 3;
function incCounter() {
counter++;
}
module.exports = {
counter: counter,
incCounter: incCounter,
};
上面代码输出内部变量 counter 和改写这个变量的内部方法 incCounter。
// main.js
var counter = require('./lib').counter;
var incCounter = require('./lib').incCounter;
console.log(counter); // 3
incCounter();
console.log(counter); // 3
上面代码说明,counter 输出以后,lib.js 模块内部的变化就影响不到 counter 了。这是因为 counter 是一个原始类型的值,会被缓存。除非写成一个函数,才能得到内部变动后的值。
ES6 模块与 CommonJS 模块的差异
它们有两个重大差异:
- CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用
- CommonJS 模块是运行时加载,ES6 模块是编译时输出接口
下面重点解释第一个差异,我们还是举上面那个 CommonJS 模块的加载机制例子:
// lib.js
export let counter = 3;
export function incCounter() {
counter++;
}
// main.js
import { counter, incCounter } from './lib';
console.log(counter); // 3
incCounter();
console.log(counter); // 4
ES6 模块的运行机制与 CommonJS 不一样。ES6 模块是动态引用,并且不会缓存值,模块里面的变量绑定其所在的模块。