本文由体验技术团队曹杨毅原创。
Node.js 是一个开源的、跨平台的JavaScript运行时环境,它允许开发者在服务器端运行JavaScript代码。Node.js 是基于Chrome V8引擎构建的,专为高性能、高并发的网络应用而设计,广泛应用于构建服务器端应用程序、网络应用、命令行工具等。
本系列将分为9篇文章为大家介绍 Node.js 技术原理:从调试能力分析到内置模块新增,从性能分析工具 perf_hooks 的用法到 Chrome DevTools 的性能问题剖析,再到 ABI 稳定的理解、基于 V8 封装 JavaScript 运行时、模块加载方式探究、内置模块外置以及 Node.js addon 的全面解读等主题,每一篇都干货满满。
本文内容为本系列第2篇,以下为正文内容。
前言
作为 Node.js 的使用者,想必同学们对“内置模块”这个概念并不陌生:Node.js 内置模块也叫核心模块,预置在 Node.js 运行时中,这些内置模块不需要额外下载安装,可以在 js 代码中通过 require 引入。
常用的内置模块包括 fs、http 等
const fs = require("fs");
const http = require("http");
const url = require("url");
对于这些天天使用的功能,大家有没有好奇过它们是怎么开发出来的呢?
为了更好地了解 Node.js 的底层实现,我把这套流程自己走了一遍。这里可以跟大家分享一下:如何基于 Node.js 开源代码定制开发,添加一组新的内置模块。
所有复杂的功能都是建立在简单的基础之上的,因此我们用作演示的这组内置模块不需要太复杂。我们就实现一个往标准输出流输出信息的功能吧,类似于 console.log() 功能。
我们要实现的效果是这样的:我们的内置模块开发完成之后,我们可以通过这样的一段 js 代码对它进行调用
const my_console = require("my_console");
class a {
constructor() {
this.a = 1;
}
}
let b = new a();
arr = [1, 2, 3, 4, 5];
my_console.log("Hello World!", 111111, b, arr);
调用的结果是这样的
实现步骤
1、编写核心源码
我们选用 Node.js 22.7.0 源码进行改造,在 Node.js 的 src 文件夹下新增一个源码文件 my_console.cc,在这个文件中编写我们的 C++ 业务代码,实现我们上面提到的功能。
首先引入我们所需要使用的头文件。
为了让我们的程序具备一定的跨平台能力,我们在这里还写了一些条件编译宏,根据平台的不同而使用不同的头文件。
#include "env-inl.h"
#include "node_external_reference.h"
#include "string_bytes.h"
#ifdef __MINGW32__
# include <io.h>
#endif // __MINGW32__
#ifdef __POSIX__
# include <climits> // PATH_MAX on Solaris.
#endif // __POSIX__
#include <array>
#include <cerrno>
#include <cstring>
在编写业务代码之前,我们需要先定义一个命名空间(嵌套在 node 命名空间下,Node.js 里面现有的内置模块普遍遵循这个习惯)
namespace node {
namespace my_console {
由于我们要使用一些来自其他命名空间的数据类型,所以我们将它们引入进来
using v8::Array;
usingv8::ArrayBuffer;
usingv8::Boolean;
usingv8::Context;
usingv8::Float64Array;
usingv8::FunctionCallbackInfo;