Skip to content

走进 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 路径下。

bash
cd /opt

wget https://nodejs.org/dist/v20.6.1/node-v20.6.1-linux-arm64.tar.xz

2.2 解压

解压后的文件我这边将名字改为了 nodejs,这个地方自己随意,只要在建立软连接的时候写正确就可以。

bash
tar -xvf node-v20.6.1-linux-arm64.tar.xz
mv node-v16.13.0-linux-x64 nodejs

3. 建立软连接变为全局

3.1 检查

确认下 nodejs 下 bin 目录里是否有 node 和 npm文件,如果有执行软连接,没有重新执行上面的步骤。

3.2 建立软连接,变为全局

bash
ln -s /opt/nodejs/bin/npm /usr/local/bin/
ln -s /opt/nodejs/bin/node /usr/local/bin/

4. 测试是否安装成功

bash
# 显示 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.js 中,全局对象是 global,global 上有很多的属性与方法。

一、常见的全局对象

(1) process 对象:process 提供了 Node 进程中相关的信息。比如 Node 的运行环境、参数信息等。

  • cwd()
  • exit()
  • argv
  • platform
  • kill(pid)
  • env

(2) console 对象:提供了简单的调试控制台。

javascript
// 最常用的输出内容的方式
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 程序:

javascript
// myscript.js
console.log(process.argv);

然后通过命令行运行这个程序,并传递一些参数:

bash
node myscript.js hello world

将会看到以下的输出:

bash
[ 'C://Program Files//nodejs//node.exe',
  '/path/to/myscript.js',
  'hello',
  'world'
]

四、特殊的全局对象

为什么我称之为特殊的全局对象呢?这些全局对象实际上是模块中的变量,只是每个模块都有,看起来像是全局变量。

包括:__dirname、__filename、module、exports、require()。

注意:在命令行交互中是不可以使用的。

javascript
// 获取当前文件所在的绝对路径
__dirname
// 获取当前文件所在的绝对路径+文件名
__filename

global 和 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 使用模块系统,每个模块都有自己的作用域。
preview
图片加载中
预览

Released under the MIT License.