nodejs
loyalvi Lv7

nodejs

Node.js — 在任何地方运行 JavaScript

版本控制

一、Node. js 运行时版本管理

1. 使用版本管理工具

工具名 特点 命令示例
nvm 最流行的 Node 版本管理工具(仅限 macOS/Linux) nvm install 20
nvm use 18
fnm 更快的替代工具,跨平台支持(Rust 编写) fnm install 20
fnm use 18
Volta 跨平台工具,支持自动版本切换(由 Rust 编写) volta install node@20
n 轻量级工具(但需全局安装 Node. js 作为依赖) n 20
nvm-windows Windows 专用版 nvm nvm install 20

2. 推荐工具:nvm

1
2
3
4
5
6
7
# 安装 nvm
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
# 常用命令
nvm install 20 # 安装 Node.js 20.x
nvm use 20 # 切换到 Node.js 20.x
nvm alias default 20 # 设置默认版本
nvm ls # 查看已安装版本

3. 自动版本切换

在项目中添加 .nvmrc 文件指定版本:

1
2
3
4
# 创建 .nvmrc 文件
echo "20" > .nvmrc
# 自动切换版本(需配合工具)
nvm use # 使用 .nvmrc 中的版本

4.fnm vs nvm 对比

特性 fnm nvm
速度 ⚡️ 极快(Rust 编写) 较慢(Shell 脚本)
跨平台支持 Windows/macOS/Linux Windows/macOS/Linux
自动版本切换 内置支持(--use-on-cd 需插件扩展
依赖管理 无全局 Node 依赖 需要预装 Node

fnm

1
2
3
4
5
6
7
8
9
10
# 安装 fnm (快速 Node 管理器)
winget install Schniz.fnm
# 配置 fnm 环境
fnm env --use-on-cd | Out-String | Invoke-Expression
# 下载并安装 Node.js
fnm use --install-if-missing 22
# 验证环境中是否存在正确的 Node.js 版本
node -v # 应该打印 `v22.12.0`
# 验证环境中是否存在正确的 npm 版本
npm -v # 应该打印 `10.9.0`

配置 fnm 环境

为了在切换目录时自动加载对应的 Node.js 版本,需要在 %USERPROFILE%Documents/WindowsPowerShell 目录下 Microsoft.PowerShell_profile.ps1 文件中添加 fnm env --use-on-cd | Out-String | Invoke-Expression,若没有对应文件夹或文件手动创建即可。
!fnm.png

node 原理

Node.js 是一个开源的、跨平台的 JavaScript 运行时环境,它允许开发者在服务器端运行 JavaScript 代码。以下是 Node.js 的一些核心原理:
一、基于 V8 引擎

  1. V8 引擎简介
    • V8 是由 Google 开发的高性能 JavaScript 引擎,最初是为 Chrome 浏览器设计的。它能够将 JavaScript 代码编译成机器码,从而实现快速执行。Node.js 利用 V8 引擎,使得 JavaScript 代码在服务器端也能像在浏览器端一样高效运行。
    • V8 引擎采用即时编译(JIT)技术。当 JavaScript 代码执行时,V8 会动态地将 JavaScript 代码转换为本地机器码。例如,对于一个简单的数学运算函数,V8 引擎会分析这个函数的执行路径,将其中的运算逻辑编译成高效的机器指令,这样在后续调用该函数时就能快速执行,大大提高了 JavaScript 代码的执行速度。
  2. 与 Node.js 的结合
    • Node.js 将 V8 引擎嵌入到自己的架构中。它为 V8 提供了运行环境,包括内存管理、垃圾回收等功能。同时,Node.js 还扩展了 V8 的功能,使其能够访问操作系统层面的资源,如文件系统、网络等。例如,Node.js 通过自己的模块系统,让 JavaScript 代码可以调用内置的文件系统模块(fs 模块),利用 V8 引擎执行相关的文件操作函数,实现对服务器本地文件的读写。
      二、事件驱动和非阻塞 I/O 模型
  3. 事件驱动机制
    • Node.js 采用事件驱动架构。在 Node.js 应用程序中,存在一个事件循环(Event Loop)。当一个异步操作(如文件读取、网络请求等)被发起时,它会被放入事件队列中。一旦这个异步操作完成,就会触发一个事件,事件循环会检测到这个事件,并调用与之关联的回调函数来处理结果。
    • 例如,当使用 Node.js 的 http 模块创建一个 HTTP 服务器时,服务器监听端口的操作是异步的。当有客户端请求到达服务器端口时,会触发一个“request”事件。事件循环会调用预先注册的处理请求的回调函数,这个回调函数可以获取请求信息并进行相应的响应处理。这种事件驱动的方式使得 Node.js 能够高效地处理大量的并发请求,因为它不需要为每个请求创建新的线程或进程,而是通过事件循环来统一管理和调度。
  4. 非阻塞 I/O
    • 在传统的服务器端编程中,I/O 操作(输入/输出操作,如读取文件、数据库查询等)往往是阻塞的。这意味着当一个 I/O 操作在执行时,程序会暂停执行后续的代码,直到 I/O 操作完成。而 Node.js 采用非阻塞 I/O 模型,当发起一个 I/O 操作时,程序不会等待 I/O 操作完成,而是继续执行后面的代码。
    • 例如,在使用 Node.js 的 fs 模块异步读取文件时,调用 fs.readFile() 方法后,程序不会停留在读文件这一步。它会继续执行后续的代码,如设置其他事件的回调函数等。当文件读取完成时,通过事件驱动机制触发回调函数来处理读取到的数据。这种非阻塞 I/O 模型使得 Node.js 在处理 I/O 密集型任务时非常高效,能够充分利用单个 CPU 核心,避免了多线程编程中可能出现的线程上下文切换等问题。
      三、单线程与多线程协作
  5. 单线程执行 JavaScript 代码
    • Node.js 的 JavaScript 代码是在单个线程中执行的。这个线程就是运行 V8 引擎的线程。所有的 JavaScript 函数调用、逻辑判断等操作都是在这个单线程中顺序执行的。这种单线程模型简化了编程模型,开发者不需要考虑复杂的线程同步、锁等问题。
    • 例如,在一个 Node.js 脚本中,有多个函数调用和变量赋值操作。这些操作都是按照代码的顺序,在单个线程中依次执行。这使得代码的执行顺序非常明确,便于理解和调试。
  6. 多线程协作处理底层任务
    • 尽管 JavaScript 代码是在单线程中执行,但 Node.js 在底层会利用多线程来处理一些任务。对于一些耗时的 I/O 操作,如文件系统操作、网络通信等,Node.js 会将这些操作委托给操作系统提供的线程池来执行。
    • 例如,在进行文件写入操作时,Node.js 会将写文件的任务提交给底层的线程池中的一个线程。这个线程专门负责与操作系统的文件系统接口进行交互,完成文件写入工作。一旦写入完成,会通过事件驱动机制通知单线程的 JavaScript 代码,触发相应的回调函数。这样,Node.js 既利用了单线程的简单性来执行 JavaScript 代码,又通过多线程协作来提高 I/O 操作的效率。
      四、模块化系统
  7. CommonJS 模块规范
    • Node.js 采用 CommonJS 模块规范来组织代码。每个 JavaScript 文件都可以作为一个模块。模块之间通过 require() 函数来相互引用。例如,有一个 user.js 模块,它定义了用户相关的函数和变量。在另一个文件 app.js 中,可以通过 require(‘./user’) 来引入 user.js 模块,从而在 app.js 中使用 user.js 中定义的函数和变量。
    • 每个模块都有自己的作用域,模块内部定义的变量和函数默认不会暴露到全局作用域。只有通过 module.exports 或 exports 对象导出的内容,才能被其他模块引用。这种模块化方式使得代码的组织更加清晰,便于维护和复用。
  8. 内置模块和第三方模块
    • Node.js 提供了许多内置模块,如 fs(文件系统模块)、http(HTTP 服务器模块)、path(路径处理模块)等。这些内置模块为开发者提供了丰富的功能,用于处理常见的服务器端任务。同时,Node.js 社区也非常活跃,有大量的第三方模块可供使用。开发者可以通过 npm(Node Package Manager)这个包管理工具来安装和管理第三方模块。例如,express 是一个非常流行的第三方模块,它是一个基于 Node.js 的 web 开发框架,通过 npm install express 命令就可以安装到项目中,为开发 web 应用提供更便捷的路由处理、中间件支持等功能。

与 js 的区别

Node.js 和浏览器端的 JavaScript 有一些显著的区别,主要体现在运行环境、API、用途等方面,以下是详细对比:
一、运行环境

  1. 浏览器端 JavaScript
    • 浏览器端的 JavaScript 运行在浏览器环境中。它与 HTML 和 CSS 紧密结合,主要用来操作浏览器的 DOM(文档对象模型)和 BOM(浏览器对象模型)。例如,通过 JavaScript 可以获取页面中的元素,修改元素的样式、内容等。当用户在浏览器中打开一个网页时,网页中的 JavaScript 代码会在浏览器的 JavaScript 引擎(如 Chrome 的 V8、Firefox 的 SpiderMonkey 等)中执行。
    • 浏览器端 JavaScript 的执行受到浏览器安全策略的限制。例如,它不能直接访问本地文件系统,以防止恶意脚本窃取用户本地文件。它只能在浏览器提供的沙箱环境中运行,与服务器端的交互通常通过 AJAX(异步 JavaScript 和 XML)等技术实现。
  2. Node.js
    • Node.js 是一个在服务器端运行 JavaScript 的环境。它基于 Chrome 的 V8 引擎构建,但运行在服务器的操作系统上,如 Linux、Windows 等。Node.js 可以直接访问服务器的文件系统、网络接口等资源。例如,使用 Node.js 的 fs 模块可以读写服务器本地的文件,创建、删除文件夹等。
    • Node.js 没有浏览器端的那些安全限制,因为它主要运行在服务器端,由服务器管理员控制。它可以执行各种系统级的操作,如启动网络服务、执行系统命令等,这使得 Node.js 能够在服务器端构建各种应用程序,如 web 服务器、网络服务端程序等。
      二、API
  3. 浏览器端 JavaScript API
    • 浏览器端 JavaScript 提供了大量的 API 来操作网页和浏览器功能。例如,DOM API 可以用来创建、修改和删除 HTML 元素。通过 document.getElementById() 可以获取页面中的元素,通过 element.innerHTML 可以修改元素的内部 HTML 内容。
    • BOM API 提供了对浏览器窗口、历史记录等的控制。例如,window.location 可以获取或设置当前页面的 URL,history.back() 可以实现浏览器的后退功能。还有诸如事件 API,可以用来监听和处理各种用户交互事件,如点击事件(click)、鼠标移动事件(mousemove)等。
  4. Node.js API
    • Node.js 提供了一系列用于服务器端开发的 API。例如,fs 模块提供了文件系统操作的 API,像 fs.readFile() 可以异步读取文件内容,fs.writeFile() 可以写入文件。path 模块提供了路径处理的 API,如 path.join() 可以用来连接路径,path.basename() 可以获取路径的文件名部分。
    • http 模块是 Node.js 中非常重要的一个模块,它提供了创建 HTTP 服务器和客户端的功能。通过 http.createServer() 可以创建一个 HTTP 服务器,用来监听端口并处理客户端的请求。此外,还有像 os 模块(提供操作系统相关的信息)、crypto 模块(提供加密功能)等,这些 API 都是针对服务器端开发场景设计的。
      三、用途
  5. 浏览器端 JavaScript
    • 浏览器端 JavaScript 主要用于增强用户体验和实现页面的动态交互。例如,在一个电商网站中,当用户点击“加入购物车”按钮时,通过 JavaScript 可以在不刷新页面的情况下,将商品信息添加到购物车列表中,并更新购物车的数量显示。它还可以用来实现表单验证,在用户提交表单之前,检查输入的数据是否符合要求,如检查邮箱地址格式是否正确、密码是否符合强度要求等。
    • 它也可以用于创建单页面应用程序(SPA)。在 SPA 中,页面的内容会根据用户的操作动态加载和更新,而不是像传统的多页面应用程序那样频繁地刷新页面。例如,一些现代的前端框架(如 Vue.js、React.js)结合浏览器端 JavaScript,可以构建出非常流畅和响应式的用户界面。
  6. Node.js
    • Node.js 主要用于服务器端开发。它可以用来构建高性能的 web 服务器。例如,使用 Express.js 这个基于 Node.js 的框架,可以快速搭建一个 web 应用程序,处理用户的 HTTP 请求,返回动态生成的 HTML 页面、JSON 数据等。Node.js 也可以用于构建实时通信应用,如聊天服务器。通过 WebSocket 协议,Node.js 能够实现服务器与客户端之间的双向实时通信,当一个用户发送消息时,服务器可以立即将消息推送给其他在线用户。
    • 除了 web 开发,Node.js 还可以用于构建命令行工具。开发者可以利用 Node.js 的文件系统和进程控制等 API,创建一些用于代码格式化、自动化构建等任务的命令行工具。此外,Node.js 也可以用于物联网(IoT)领域,作为设备端的服务器,处理设备之间的通信和数据传输。
      四、性能特点
  7. 浏览器端 JavaScript
    • 浏览器端 JavaScript 的性能主要体现在页面的响应速度和交互的流畅性上。现代浏览器的 JavaScript 引擎已经非常高效,能够快速执行 JavaScript 代码。但是,由于浏览器端 JavaScript 需要操作 DOM,而 DOM 操作相对是比较耗时的,如果在执行 JavaScript 代码过程中频繁地进行大量的 DOM 操作,可能会导致页面出现卡顿现象。例如,在一个列表中动态添加大量元素时,如果没有采用合适的优化方法,如使用文档片段(DocumentFragment)等,可能会使页面渲染变慢。
  8. Node.js
    • Node.js 的性能优势在于其事件驱动和非阻塞 I/O 模型。这种模型使得 Node.js 在处理大量并发 I/O 操作时表现出色。例如,在一个高并发的 web 服务器场景中,当有成千上万个客户端同时发起请求时,Node.js 能够通过事件循环和非阻塞 I/O 机制,高效地处理这些请求,而不需要为每个请求创建新的线程或进程。这大大减少了系统资源的消耗,提高了服务器的吞吐量。然而,Node.js 在处理 CPU 密集型任务时可能会相对较弱,因为它的 JavaScript 代码是在单个线程中执行的。如果一个 CPU 密集型的操作(如复杂的数学计算)耗时过长,会阻塞事件循环,影响其他任务的处理。不过,可以通过一些方法,如将 CPU 密集型任务放到子进程中处理等,来解决这个问题。

知识点

Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行时环境,用于构建高性能的服务器端应用程序。以下是 Node.js 的一些核心知识点,涵盖其基础、核心模块、异步编程、事件驱动机制、生态系统等方面。

1. Node.js 基础

  • 什么是 Node.js?
    • Node.js 是一个开源的、跨平台的 JavaScript 运行时环境,允许开发者在服务器端运行 JavaScript。
    • 它基于 Chrome 的 V8 引擎,专为高性能和高并发设计。
  • 安装 Node.js
  • Node.js 的特点
    • 单线程:Node.js 是单线程的,但通过事件循环和异步编程实现高并发。
    • 非阻塞 I/O:Node.js 使用非阻塞 I/O 模型,避免了传统多线程编程中的复杂性。
    • 高性能:基于 V8 引擎,Node.js 的性能非常出色。
    • 跨平台:支持 Windows、Linux 和 macOS。

2. 核心模块

Node.js 提供了一系列内置模块,用于处理文件系统、网络请求、路径解析等常见任务。

  • fs 模块
    • 用于文件系统操作,如读写文件、创建目录等。
    1
    2
    3
    4
    5
    const fs = require('fs');
    fs.readFile('example.txt', 'utf8', (err, data) => {
    if (err) throw err;
    console.log(data);
    });
  • http 模块
    • 用于创建 HTTP 服务器或客户端。
    1
    2
    3
    4
    5
    6
    7
    8
    const http = require('http');
    const server = http.createServer((req, res) => {
    res.writeHead(200, { 'Content-Type': 'text/plain' });
    res.end('Hello, World!');
    });
    server.listen(3000, () => {
    console.log('Server running at http://localhost:3000');
    });
  • path 模块
    • 用于处理和转换文件路径。
    1
    2
    const path = require('path');
    console.log(path.join('/foo', 'bar', 'baz')); // /foo/bar/baz
  • os 模块
    • 提供操作系统相关的信息。
    1
    2
    const os = require('os');
    console.log(os.platform()); // darwin (macOS)
  • events 模块
    • 提供事件发射器(EventEmitter),用于创建自定义事件。
    1
    2
    3
    4
    5
    6
    7
    const EventEmitter = require('events');
    class MyEmitter extends EventEmitter {}
    const myEmitter = new MyEmitter();
    myEmitter.on('event', () => {
    console.log('Event triggered');
    });
    myEmitter.emit('event');

3. 异步编程

Node.js 的核心是异步编程,通过回调函数、Promise 和 async/await 实现。

  • 回调函数
    • 最传统的异步编程方式。
    1
    2
    3
    4
    fs.readFile('example.txt', 'utf8', (err, data) => {
    if (err) throw err;
    console.log(data);
    });
  • Promise
    • 提供更优雅的异步编程方式。
    1
    2
    3
    4
    5
    6
    const readFilePromise = fs.promises.readFile('example.txt', 'utf8');
    readFilePromise.then(data => {
    console.log(data);
    }).catch(err => {
    console.error(err);
    });
  • async/await
    • 使异步代码更接近同步代码的写法。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    async function readFile() {
    try {
    const data = await fs.promises.readFile('example.txt', 'utf8');
    console.log(data);
    } catch (err) {
    console.error(err);
    }
    }
    readFile();

4. 事件循环和非阻塞 I/O

Node.js 的事件循环是其异步编程的核心机制。

  • 事件循环
    • 事件循环通过监听任务队列(宏任务队列和微任务队列)来执行异步任务。
    • 宏任务(如 setTimeoutsetInterval)和微任务(如 PromisequeueMicrotask)的执行顺序决定了代码的运行逻辑。
  • 非阻塞 I/O
    • Node.js 使用非阻塞 I/O 模型,避免了传统 I/O 操作中的阻塞问题。
    • 通过回调函数或 Promise 处理 I/O 操作的结果。

5. 模块系统

Node.js 使用 CommonJS 模块系统,通过 requiremodule.exports 导入和导出模块。

  • 导出模块
    1
    2
    3
    4
    5
    // myModule.js
    function sayHello(name) {
    return `Hello, ${name}!`;
    }
    module.exports = { sayHello };
  • 导入模块
    1
    2
    const { sayHello } = require('./myModule');
    console.log(sayHello('John')); // Hello, John!

6. 包管理

Node.js 使用 npm(Node Package Manager)作为包管理工具,用于安装和管理依赖。

  • 安装依赖
    1
    npm install express
  • 初始化项目
    1
    npm init -y
  • 查看依赖
    1
    npm list

7. 常用框架和工具

Node.js 拥有丰富的生态系统,提供了许多流行的框架和工具。

  • Express
    • 一个轻量级的 Web 框架,用于构建 HTTP 服务器。
    1
    2
    3
    4
    5
    6
    7
    8
    const express = require('express');
    const app = express();
    app.get('/', (req, res) => {
    res.send('Hello, Express!');
    });
    app.listen(3000, () => {
    console.log('Server running at http://localhost:3000');
    });
  • Koa
    • 由 Express 团队开发的下一代 Web 框架,基于 async/await 设计。
  • Mongoose
    • 一个 MongoDB ORM,用于操作 MongoDB 数据库。
  • Nodemon
    • 一个开发工具,用于自动重启 Node.js 应用程序。

8. 调试和性能优化

Node.js 提供了多种工具和方法用于调试和优化性能。

  • 调试工具
    • 使用 node --inspect 启动 Node.js 应用程序,然后通过 Chrome DevTools 进行调试。
  • 性能分析
    • 使用 --prof--prof-process 生成性能分析报告。
  • 日志和监控
    • 使用 winstonmorgan 等库记录日志。
    • 使用 pm2 管理和监控 Node.js 应用程序。

9. 安全性

Node.js 提供了多种机制用于保障应用程序的安全性。

  • HTTPS
    • 使用 https 模块创建安全的 HTTP 服务器。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    const https = require('https');
    const fs = require('fs');
    const server = https.createServer({
    key: fs.readFileSync('key.pem'),
    cert: fs.readFileSync('cert.pem')
    }, (req, res) => {
    res.writeHead(200);
    res.end('Hello, HTTPS!');
    });
    server.listen(3000, () => {
    console.log('HTTPS server running at https://localhost:3000');
    });
  • 输入验证
    • 使用 express-validator 等库验证用户输入。
  • 安全头
    • 使用 helmet 等库设置安全的 HTTP 头。

10. 部署

Node.js 应用程序可以通过多种方式部署到生产环境。

  • 使用 PM2
    • PM2 是一个流行的 Node.js 进程管理器,用于监控和管理 Node.js 应用程序。
    1
    2
    3
    4
    pm2 start app.js
    pm2 restart app.js
    pm2 stop app.js
    pm2 delete app.js
  • 使用 Docker
    • 将 Node.js 应用程序打包为 Docker 容器,便于部署和管理。

总结

Node.js 是一个功能强大的 JavaScript 运行时环境,适用于构建高性能的服务器端应用程序。通过掌握其核心模块、异步编程机制、事件驱动模型和生态系统,开发者可以高效地开发和部署 Node.js 应用程序。

由 Hexo 驱动 & 主题 Keep
访客数 访问量