Node.js深入学习之浅析require函数中怎么添加钩子

Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行时环境。早期的 Node.js 采用的是 CommonJS 模块规范,从 Node v13.2.0 版本开始正式支持 ES Modules 特性。直到 v15.3.0 版本 ES Modules 特性才稳定下来并与 NPM 生态相兼容。


1.png本文将介绍 Node.js 中 require 函数的工作流程、如何让 Node.js 直接执行 ts 文件及如何正确地劫持 Node.js 的 require 函数,从而实现钩子的功能。接下来,我们先来介绍 require 函数。

require 函数
Node.js 应用由模块组成,每个文件就是一个模块。对于 CommonJS 模块规范来说,我们通过 require 函数来导入模块。那么当我们使用 require 函数来导入模块的时候,该函数内部发生了什么?这里我们通过调用堆栈来了解一下 require 的过程:

2.png
由上图可知,在使用 require 导入模块时,会调用 Module 对象的 load 方法来加载模块,该方法的实现如下所示:


image.png
// lib/internal/modules/cjs/loader.js

Module.prototype.load = function(filename) {

  this.filename = filename;

  this.paths = Module._nodeModulePaths(path.dirname(filename));

 

  const extension = findLongestRegisteredExtension(filename);

 

  Module._extensions[extension](this, filename);

  this.loaded = true;

  // 省略部分代码

};

注意:本文所引用 Node.js 源码所对应的版本是 v16.13.1

在以上代码中,重要的两个步骤是:

步骤一:根据文件名找出扩展名;
步骤二:通过解析后的扩展名,在 Module._extensions 对象中查找匹配的加载器。
在 Node.js 中内置了 3 种不同的加载器,用于加载 node、json 和 js 文件。node 文件加载器

image.png
// lib/internal/modules/cjs/loader.js

Module._extensions['.node'] = function(module, filename) {

  return process.dlopen(module, path.toNamespacedPath(filename));

};

json 文件加载器

image.png
// lib/internal/modules/cjs/loader.js

Module._extensions['.json'] = function(module, filename) {

 const content = fs.readFileSync(filename, 'utf8');

 try {

    module.exports = JSONParse(stripBOM(content));

 } catch (err) {

   err.message = filename + ': ' + err.message;

   throw err;

 }

};

js 文件加载器

image.png
// lib/internal/modules/cjs/loader.js

Module._extensions['.js'] = function(module, filename) {

  // If already analyzed the source, then it will be cached.

  const cached = cjsParseCache.get(module);

  let content;

  if (cached?.source) {

    content = cached.source;

    cached.source = undefined;

  } else {

    content = fs.readFileSync(filename, 'utf8');

  }

  // 省略部分代码

  module._compile(content, filename);

};

下面我们来分析比较重要的 js 文件加载器。通过观察以上代码,我们可知 js 加载器的核心处理流程,也可以分为两个步骤:

步骤一:使用 fs.readFileSync 方法加载 js 文件的内容;
步骤二:使用 module._compile 方法编译已加载的 js 代码。
那么了解以上的知识之后,对我们有什么用处呢?其实在了解 require 函数的工作流程之后,我们就可以扩展 Node.js 的加载器。比如让 Node.js 能够运行 ts 文件。

image.png


郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: