走进 Node.js 世界
第一章:认识 Node.js
官方网站地址:nodejs.org
中文网站地址:nodejs.cn
一、是什么
Node.js 也称 Node,是一个基于 Chrome V8 引擎的开源、跨平台的 JavaScript 运行时环境(宿主)。
注意:
Node.js 不是一种独立的语言。与 PHP、Python、Perl、Ruby 的”既是语言也是平台” 不同。

- Node.js 也不是一个 JavaScript 框架或库。不同于 Vue.js、React.js、Angular、jQuery 等。
- 运行在 Node.js 上的 JavaScript 不能使用 DOM、BOM,但可以使用 Node.js 提供的各种 API(文件系统读写、网络 IO、加密、压缩解压文件等操作)。

二、为什么学习
前端可以实现工程化开发。
前端自动化工具、模块化打包工具 gulp、webpack 以及 vue、react 的脚手架工具都是基于 Node 运行的。
可以使用 JavaScript 进行后端开发,前端工程师秒变全栈工程师。
使用 JS 开发很多小工具,如自动化脚本,爬虫程序等。
三、特点
1. 单线程
在 Java、PHP 或者 .net 等服务器端语言中,会为每一个客户端连接创建一个新的线程。而每个线程需要耗费大约 2MB 内存。也就是说,理论上,一个 8GB 内存的服务器可以同时连接的最大用户数为 4000 个左右。要让 Web 应用程序支持更多的用户,就需要增加服务器的数量,而 Web 应用程序的硬件成本当然就上升了。
Node.js 不为每个客户连接创建一个新的线程,而仅仅使用一个线程。当有用户连接了,就触发一个内部事件,通过非阻塞 I/O、事件驱动机制,让 Node.js 程序宏观上也是并行的。使用 Node.js,一个 8GB 内存的服务器,可以同时处理超过 4 万用户的连接。
另外,单线程带来的好处,还有操作系统完全不再有线程创建、销毁的时间开销。
坏处,就是一个用户造成了线程的崩溃,整个服务都崩溃了,其他人也崩溃了。
2. 非阻塞 I/O (non-blocking I/O)
例如,当在访问数据库取得数据的时候,需要一段时间。在传统的单线程处理机制中,在执行了访问数据库代码之后,整个线程都将暂停下来,等待数据库返回结果,才能执行后面的代码。也就是说,I/O 阻塞了代码的执行,极大地降低了程序的执行效率。
由于 Node.js 中采用了非阻塞型 I/O 机制,因此在执行了访问数据库的代码之后,将立即转而执行其后面的代码,把数据库返回结果的处理代码放在回调函数中,从而提高了程序的执行效率。
当某个 I/O 执行完毕时,将以事件的形式通知执行 I/O 操作的线程,线程执行这个事件的回调函数。为了处理异步 I/O,线程必须有事件循环,不断的检查有没有未处理的事件,依次予以处理。
阻塞模式下,一个线程只能处理一项任务,要想提高吞吐量必须通过多线程。而非阻塞模式下,一个线程永远在执行计算操作,这个线程的 CPU 核心利用率永远是 100%。所以,这是一种特别有哲理的解决方案:与其人多,但是好多人闲着;还不如一个人玩命,往死里干活儿。
3. 事件驱动 (event-driven)
Node.js 的事件循环机制允许你在执行一个回调函数时,转而执行其他事件,但不是在同一个回调函数执行过程中。而是通过将回调函数放入事件队列,并由事件循环来调度执行。
Node.js 底层是 C++(V8 也是 C++ 写的)。底层代码中,近半数都用于事件队列、回调函数队列的构建。用事件驱动来完成服务器的任务调度,这是鬼才才能想到的。针尖上的舞蹈,用一个线程,担负起了处理非常多的任务的使命。
四、架构

这幅图展示的是 Node.js 的运行机制。Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境,它使用了非阻塞的事件驱动的 I/O 模型。这个图解说明了 Node.js 应用程序如何与底层的系统交互,包括以下几个主要部分:
- 应用程序(Application):这是开发者使用 JavaScript 编写的代码部分。
- V8(JavaScript Engine):这是 Google 开发的开源 JavaScript 引擎,用于解释和执行用户的 JavaScript 代码。
- Node.js 绑定(Node.js Bindings):这是 Node.js 的核心 API,它提供了一些方法,允许 JavaScript 代码与操作系统进行交互。
- 事件队列(Event Queue):这是一个队列,用于存放事件和对应的回调函数。
- 事件循环(Event Loop):这是 Node.js 的核心,负责不断地从事件队列中取出事件和回调函数执行。事件循环是非阻塞的,确保 Node.js 可以处理大量的并发而不会停滞。
- LIBUV:这是一个专门处理异步 I/O 的库,提供了跨平台的 I/O 功能。
- 工作线程(Worker Threads):对于一些可能会阻塞事件循环的操作(如密集型文件操作、网络请求等),Node.js 会使用工作线程来处理,这样可以确保事件循环不会因为这些耗时操作而被阻塞。
整个流程大致是:应用程序通过 Node.js API 发起非阻塞 I/O 操作,这些操作被放入事件队列中,事件循环不断地处理这些事件,并在操作完成后调用相应的回调函数。如果操作是计算密集型或阻塞型的,则通过工作线程来处理,以免阻塞事件循环。这种模型使得 Node.js 能够高效地处理大量的并发连接,特别适合 I/O 密集型的应用(计算密集型对 Node.js 来说会比 Java 慢)。
Node.js 的"单线程"指的是什么?
✅ JavaScript 执行是单线程的。
❌ Node.js 系统整体不是单线程的。
第二章:安装
一、Linux
1. 去官网下载
英文网址:Download Node.js for nodejs.org
中文网址:
可以从淘宝 npm 镜像站的二进制文件镜像站来下载历史版本:https://npmmirror.com/mirrors/node/
备注:通过 uname -a 命令可查看 Linux 系统位数(x86_64 表示 64 位系统, i686、i386 表示 32 位系统)。
建议:推荐 Visual Studio Code 安装一个 Code Runner 插件。
2. 上传 & 解压
2.1 上传到服务器
目录可以是任意路径,目前我放置在 /opt 路径下。
cd /opt
wget https://nodejs.org/dist/v20.6.1/node-v20.6.1-linux-arm64.tar.xz2.2 解压
解压后的文件我这边将名字改为了 nodejs,这个地方自己随意,只要在建立软连接的时候写正确就可以。
tar -xvf node-v20.6.1-linux-arm64.tar.xz
mv node-v16.13.0-linux-x64 nodejs3. 建立软连接变为全局
3.1 检查
确认下 nodejs 下 bin 目录里是否有 node 和 npm文件,如果有执行软连接,没有重新执行上面的步骤。
3.2 建立软连接,变为全局
ln -s /opt/nodejs/bin/npm /usr/local/bin/
ln -s /opt/nodejs/bin/node /usr/local/bin/4. 测试是否安装成功
# 显示 node.js 版本
node -v二、NVM
1. 介绍
nvm 全称 Node Version Manager。顾名思义它是用来管理 node 版本的工具,方便切换不同版本的 Node.js。
2. 使用
nvm 的使用非常的简单,跟 npm 的使用方法类似。
下载安装
nvm 本身不支持 Windows 操作系统,可以选择使用 NVM for Windows。
选择 nvm-setup.exe 下载即可。
常用命令
| 命令 | 说明 |
|---|---|
nvm install <version> [arch] | 安装 version 版本的 Node.js。若想安装最新版,可以使用 latest 来替代具体版本,但有时安装的是测试版。若想安装最新的稳定版,可以使用 lts 关键字。 |
nvm uninstall <version> | 删除某个版本的 Node.js |
| nvm list [available] 别名:ls | 添加 available 表示显示所有可以下载的 Node.js 版本。否则显示已安装的版本。 |
nvm use <version> | 切换到 <version> 版本的 Node.js |
| nvm current | 当前使用的 Node.js 版本 |
| nvm node_mirror [url] | 设置下载 Node.js 时使用的地址。国内使用 https://npmmirror.com/mirrors/node/ |
| nvm npm_mirror [url] | 设置下载 npm CLI 程序的地址。国内使用 https://npmmirror.com/mirrors/npm/ |
| nvm root [path] | 用来指定 NVM 把下载的不同版本的 Node.js 存放在电脑的哪个文件夹里。不加 path 是查看安装位置,加了 path 是设置。 |
| nvm [--]version 别名:v | 查看 NVM 的版本 |
为了加快下载速度,建议设置为国内镜像。
node 镜像
npm 镜像站
npm CLI 镜像
- CNPM Binaries Mirror:https://npmmirror.com/mirrors/npm/
第三章:全局对象
在 Node.js 中,全局对象是 global,global 上有很多的属性与方法。
一、常见的全局对象
(1) process 对象:process 提供了 Node 进程中相关的信息。比如 Node 的运行环境、参数信息等。
cwd()exit()- argv
- platform
kill(pid)- env
(2) console 对象:提供了简单的调试控制台。
// 最常用的输出内容的方式
console.log()
// 清空控制台
console.clear()
// 打印函数的调用栈
console.trace()(3) 定时器函数:在 Node 中使用定时器有好几种方式。
setTimeout(callback, delay [, ...args]):callback 在 delay 毫秒后执行一次。setInterval(callback, delay [, ...args]):callback 每 delay 毫秒执行一次。setImmediate(callback [, ...args]):callback I/O 事件后的回调的“立即”执行。这里先不展开讨论它和 setTimeout(callback, 0) 之间的区别;因为它涉及到事件循环的阶段问题,会在后续详细讲解事件循环相关的知识。
process.nextTick(callback [, ...args]):添加到下一次 tick 队列中。
(4) Buffer:
(5) URL 处理
new URL('https://example.com');new URLSearchParams('key=value');
二、Node 程序传递参数
可以通过命令行向程序传递参数。这些参数可以在程序中通过 process.argv 访问。process.argv 是一个包含命令行参数的数组。第一个元素是 'node 程序安装位置',第二个元素是 JavaScript 文件的路径。接下来的元素将是任何额外的命令行参数。
例如,如果有以下的 Node.js 程序:
// myscript.js
console.log(process.argv);然后通过命令行运行这个程序,并传递一些参数:
node myscript.js hello world将会看到以下的输出:
[ 'C://Program Files//nodejs//node.exe',
'/path/to/myscript.js',
'hello',
'world'
]四、特殊的全局对象
为什么我称之为特殊的全局对象呢?这些全局对象实际上是模块中的变量,只是每个模块都有,看起来像是全局变量。
包括:__dirname、__filename、module、exports、require()。
注意:在命令行交互中是不可以使用的。
// 获取当前文件所在的绝对路径
__dirname
// 获取当前文件所在的绝对路径+文件名
__filenameglobal 和 window 的区别?
环境
- window 存在于浏览器环境。
- global 存在于 Node.js 环境。
API
- window 提供了浏览器相关的 API,如 document, location, history, localStorage, sessionStorage, requestAnimationFrame 等。
- global 提供了 Node.js 相关的 API,如 Buffer, process, global.gc (如果启用了垃圾回收器的手动控制), setImmediate, clearImmediate 等。
默认对象
- 在浏览器环境中,顶层声明的变量和函数默认是 window 对象的属性。
- 在 Node.js 中,顶层声明的变量和函数不会成为 global 对象的属性。因为 Node.js 使用模块系统,每个模块都有自己的作用域。