Node.js 模块机制 —- 笔记

《深入浅出Node》读书笔记
思考不深,行文不规范,只记录了笔者觉得重要的地方,仅供笔者自己使用。

CommonJS 规范

背景

当时 JavaScript 还处于仅在浏览器中使用,常用场景是DOM 和 BOM 操作的时候。HTML5规范才开始推进,当时JavaScript存在的如下缺陷:

  • 没有模块系统。JavaScript 直接插入HTML标签中,后引入的脚本可能会覆盖前面引入脚本的变量。
  • 没有包管理。
  • 标准库少。
  • 没有标准接口。

此时 Node 就对 CommonJS 规范进行了实现。

内容

模块引用
require 关键字。

const math = require('math');

模块定义

即通过module.exports来暴露方法或对象。
下面是我们一般使用的两种方式定义模块:

方法一:exports

exports.sayHello = () => {}

方法二:module.exports

module.exports.sayHello = () => {}

module.exports才是真正的接口,exports只不过是它的一个辅助工具。最终返回给调用的是module.exports而不是exports。而上面这两种写法得出的结果是一致的。打印出来如图:

{ sayHello: [Function] }
{ sayHello: [Function] }

建议不要在一个文件里面混用,如果已经用了 module.exports,那么再使用 exports 则会被忽略。一张图看懂:

模块分类

分为两类:

  • 核心模块:Node 提供的,如fs,http等
  • 文件模块:用户编写的。

Node 核心模块里又有C/C++模块部分 也叫做内建(build-in)模块

Node会对引入的脚本进行缓存来避免二次加载从而提高性能。

模块查找方式

  • 核心模块:在Node.js源代码编译过程中就已经编译成二进制代码,加载优先级仅次于缓存加载。
  • 路径形式的模块:以路径作为索引进行加载,如require('../../test.js'')
  • 第三方自定义模块:查找最费时,在node_modules进行查找,并逐文件夹往上找,直到根目录下的node_modules

模块编译方式

我们知道一个模块(一个js文件)有requireexportsmodule这些关键字,这些关键字为什么可以使用?

因为在编译阶段进行了引入,同时还引入了__filename__dirname
下面这段代码肯定经常在通过Babel编译后的文件中看到:

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

这就是编译后的代码文件,这里对作用域进行了隔离,即在此处引入的上述关键词(或者叫变量)。

而具体的exports、require等关键词(或者叫方法)的实现应该是在v8内部实现的。

核心模块编译过程

上面说了核心模块中的代码有JavaScript部分和C/C++部分(内建模块),核心模块中的JavaScript代码在Node.js编译时以字符串的形式存在了C++中node命名空间下的数组里。在启动Node进程时引入模块就相当于拿着标识符直接定位到内存中。所以肯定快。

引用

发表评论

电子邮件地址不会被公开。 必填项已用*标注