模块机制

Node.js中的模块分为核心模块和文件模块:

核心模块:node提供的模块,在node源码编译过程中被编译成二进制文件。node启动时,部分核心模块直接加载进内存,在路径分析中优先判断。

文件模块:用户编写的模块或是node_modules中的第三方模块,运行时动态加载,速度比核心模块慢。

Node.js早期采用了CommonJS模块化机制作为模块加载的标准实现,但是后来随着技术演进ES6的模块化机制(也常被称作ESM,相对于CommonJS的CJS也常被称作EJS)逐渐流行起来。现在版本的Node.js核心模块对于两种模块加载机制都进行了支持,但Node.js也并没有移除CommonJS的打算,两种模块加载机制在某些特殊情况下有区别,前者的兼容性也更好,因此编写Node.js工程还是建议使用CommonJS模块规范。

CommonJS模块规范

CommonJS是一个JavaScript中的模块定义规范,Node.js中使用的就是这套模块加载机制,下面介绍如何引用和定义CommonJS模块。

引用模块

我们使用require可以引用一个模块。

const os = require("os");
const archStr = os.arch();
console.log(archStr);

输出:

x64

上面示例代码中,我们引用了一个叫做os的核心模块,这个模块中的函数arch()能够输出电脑CPU的架构。

定义模块

我们可以自定义一个.js文件作为模块,用exports可以导出当前模块的方法或变量,exportsmodule对象的属性。引用该模块的文件通过require引入即可。下面是一个自定义模块的例子。

myModule.js

const sum = function(a, b) {
    return a + b;
};

module.exports.sum = sum;

app.js

const myModule = require("./myModule");
console.log(myModule.sum(1, 2));

注意:模块名就是模块的文件名,它需要符合小驼峰命名,引用时可以省略文件后缀名.js,引用一个模块不必考虑变量污染,模块中的变量仅在模块内部有效。

模块的路径

上面代码中,我们把myModule.jsapp.js放在了同一个目录下,因此我们使用require引入模块时,路径使用了./myModule,以此表示引用的模块文件和当前文件在同一个目录下,我们同样可以使用绝对路径进行模块路径的指定。如果不明确指定模块的路径,会从当前路径开始,一级一级搜索node_modules文件夹。

例如我们的工程位于/home/ubuntu/workspace/blog文件夹,node会按如下顺序搜索模块:

  1. /home/ubuntu/workspace/blog/node_modules
  2. /home/ubuntu/workspace/node_modules
  3. /home/ubuntu/node_modules
  4. /home/node_modules
  5. /node_modules

此外,node还可能会搜索NODE_PATH,以及全局node_modules等路径,根据使用的操作系统不同这些路径可能不同,实际开发中建议所有的第三方依赖模块都放到工程的node_modules中,这样排查问题时能省去很多麻烦。

模块缓存

node会对引入过的模块进行缓存,加快模块的加载速度,核心模块和文件模块都会缓存。

CommonJS模块加载过程理解

node在加载模块时将模块包装成一个闭包,类似这样的:

(function (exports, require, module, __filename, __dirname) {
    // module code...
});

module即模块本身,exports等价于module.exports。使用require加载模块,调用者得到module.exports

不过这里产生了一个问题,暴露方法时应该用module.exports还是exports呢。这里建议全用module.exports,因为exports在上面代码中只是一个形参,用它暴露属性、方法是没问题的,但是例如exports=obj的写法就错了,这相当于在函数体内更改形参引用,是没有效果的。

ESM模块规范

除了CommonJS,Node.js也在新版本中加入了对于ES6模块化机制(即ESM)的支持。虽然目前实际开发中以CommonJS为主,但这里我们还是简单了解一下如何在Node.js工程中使用ESM模块。Node.js工程中创建ESM模块有两种方式均可以实现:

  1. package.json中加入配置:"type": "module"(该值默认为commonjs),执行该工程使用ESM模块化机制
  2. ESM模块文件以.mjs作为后缀名(而不是.js

下面我们使用ESM模块规范来重写之前的例子:

myModule.js

const sum = function (a, b) {
    return a + b;
};
export default sum;

app.js

import sum from './myModule.js';
console.log(sum(1, 2));
作者:Gacfox
版权声明:本网站为非盈利性质,文章如非特殊说明均为原创,版权遵循知识共享协议CC BY-NC-ND 4.0进行授权,转载必须署名,禁止用于商业目的或演绎修改后转载。
Copyright © 2017-2024 Gacfox All Rights Reserved.
Build with NextJS | Sitemap