前言:什么是Node.js?
简单地说,Node.js就是运行在服务端的JavaScript。
在Node.js出现之前,JavaScript只能运行在浏览器中,负责页面的交互和动态效果。2009年,Ryan Dahl创造性地把Google Chrome的V8引擎从浏览器中剥离出来,让JavaScript得以在服务器端运行——这就是Node.js的起源。
Node.js不是一个编程语言,而是一个运行时平台:它让JavaScript能够读写文件、创建网络服务、操作数据库,做一切传统服务端语言(如PHP、Java、Python)能做的事情。
一句话概括:Node.js = Chrome V8引擎 + 事件驱动架构 + 丰富的标准库。
一、Node.js的核心特点
1.1 事件驱动与非阻塞I/O
这是Node.js最核心的设计哲学。
传统服务端(如PHP、Java Servlet)在处理请求时,如果遇到I/O操作(比如读取文件、查询数据库),线程会被阻塞——必须等I/O完成后才能继续处理下一个请求。这意味着每个请求都需要占用一个线程,并发量高时,线程数量暴增,内存和CPU开销巨大。
Node.js的做法截然不同:当遇到I/O操作时,它不会等待,而是立即去处理下一个请求。等I/O操作完成后,通过"事件回调"的方式来通知处理结果。
用生活中的例子来理解:
传统同步方式:你去食堂打饭,排到窗口后告诉师傅"我要一份红烧肉",然后你就站在窗口前等着,直到师傅把菜做好端给你,你才离开——这期间你什么事都做不了。
Node.js异步方式:你告诉师傅"我要一份红烧肉",师傅给你一个取餐号,你就可以先去找座位坐下、刷刷手机,等师傅叫号时再去取餐——等待的时间里,你完全可以做其他事情。
// 同步方式(阻塞)—— Node.js中一般不这么用 const fs = require('fs'); const data = fs.readFileSync('/path/to/file.txt', 'utf8'); console.log(data); // 必须等文件读完后才会执行后面的代码 console.log('这句话要等文件读完才会打印'); // 异步方式(非阻塞)—— Node.js的推荐方式 const fs = require('fs'); fs.readFile('/path/to/file.txt', 'utf8', (err, data) => { if (err) throw err; console.log(data); // 文件读完才会执行这个回调 }); console.log('这句话会立即打印,不用等文件读完');这种非阻塞I/O + 事件回调的机制,让Node.js可以用单线程处理成千上万的并发连接,非常适合I/O密集型应用。
1.2 单线程与事件循环
Node.js是单线程的——只有一个主线程在执行你的JavaScript代码。
听到"单线程",你可能会担心性能问题。但Node.js的"单线程"指的是JavaScript代码的执行是单线程的,而I/O操作实际上是由底层系统(libuv库)通过多线程来完成的。
事件循环(Event Loop)是Node.js的核心机制:
你的代码按顺序执行,遇到异步操作就交给底层处理,自己继续往下走
底层处理完异步操作后,把回调函数放入事件队列
当主线程空闲时,事件循环不断地从队列中取出回调并执行
这个机制让单线程的Node.js能够高效处理大量并发请求——请求来了就处理,遇到I/O就"挂起"去处理下一个,I/O完成后再回来继续。
1.3 跨平台支持
Node.js基于V8引擎(C++编写),通过libuv库抽象了操作系统底层的I/O操作,因此可以在Windows、Linux、macOS等多个平台上运行,代码几乎不用修改。
你可以用以下命令查看当前Node.js版本:
$ node -v v20.10.0
注意:不同版本之间可能存在API差异,建议使用LTS(长期支持)版本。
二、Node.js能做什么?
Node.js的应用场景非常广泛,以下是几个典型方向:
| 应用场景 | 说明 | 典型工具/框架 |
|---|---|---|
| Web服务器 | 处理HTTP请求,返回HTML/JSON | Express、Koa、NestJS |
| RESTful API / GraphQL | 为前端提供数据接口 | Express、Apollo Server |
| 实时应用 | 聊天室、在线协作、游戏 | Socket.io |
| 命令行工具 | 自动化脚本、构建工具 | CLI工具、Webpack |
| 后端微服务 | 分布式系统中的一个服务 | NestJS、gRPC |
| 全栈开发 | 前后端都用JavaScript | Next.js、Nuxt.js |
三、开发环境搭建
3.1 安装Node.js
访问 Node.js官网,下载LTS版本安装包,按提示安装即可。
安装完成后,在终端验证:
$ node -v v20.10.0 $ npm -v 10.2.3
npm(Node Package Manager)是Node.js的包管理工具,会随Node.js一起安装。
3.2 选择合适的编辑器
| 编辑器 | 特点 | 推荐指数 |
|---|---|---|
| VS Code | 免费、插件丰富、调试方便 | ⭐⭐⭐⭐⭐ |
| WebStorm | 功能强大、智能提示 | ⭐⭐⭐⭐ |
| Vim/Neovim | 轻量、键盘流 | ⭐⭐⭐ |
四、你的第一个Node.js程序
4.1 脚本模式(推荐方式)
创建一个文件hello.js,写入以下代码:
// hello.js console.log("Hello, Node.js!"); console.log("欢迎来到RUNOOB Node.js教程!"); // 打印当前Node.js版本 console.log("Node.js版本:", process.version); // 打印当前工作目录 console.log("当前目录:", process.cwd());在终端运行:
$ node hello.js
输出结果:
Hello, Node.js! 欢迎来到RUNOOB Node.js教程! Node.js版本: v20.10.0 当前目录: /your/project/path
4.2 交互模式(REPL)
打开终端,输入node进入交互模式,可以逐行执行JavaScript代码:
$ node > console.log('Hello World!'); Hello World! > const x = 10; > const y = 20; > x + y 30 > .exit # 退出交互模式交互模式适合快速测试代码片段,但正式开发时还是推荐用脚本文件。
4.3 第一个HTTP服务
恭喜你,你已经写出了第一个Node.js程序。现在让我们更进一步——用Node.js创建一个最简单的HTTP服务器:
// server.js const http = require('http'); // 创建一个HTTP服务器 const server = http.createServer((req, res) => { // 设置响应头,告诉浏览器返回的是HTML res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' }); // 返回响应内容 res.end(` <h1>欢迎来到RUNOOB Node.js教程!</h1> <p>你的第一个Node.js HTTP服务器已成功运行 🎉</p> <p>请求地址: ${req.url}</p> `); }); // 监听3000端口 server.listen(3000, () => { console.log('服务器已启动: http://localhost:3000'); console.log('按 Ctrl+C 停止服务器'); });保存为server.js,然后运行:
$ node server.js
打开浏览器访问http://localhost:3000,你会看到:
![服务器运行效果描述]
这就是一个完整的Web服务器!虽然只有短短十几行代码,但它已经能处理HTTP请求并返回HTML页面了。
五、npm与包管理基础
npm是Node.js的"应用商店",你可以通过它安装别人写好的第三方包,也可以发布自己的包。
5.1 初始化项目
# 创建一个新目录 mkdir my-node-project cd my-node-project # 初始化package.json(一路回车使用默认值) npm init -y
package.json是项目的配置文件,记录了项目名、版本、依赖包等信息。
5.2 安装第三方包
# 安装一个包(如moment,用于日期处理) npm install moment # 安装开发依赖(只在开发时使用,生产环境不需要) npm install nodemon --save-dev
安装后,node_modules目录会存放所有依赖包,package.json中会增加依赖记录。
5.3 使用第三方包
// app.js const moment = require('moment'); const now = moment(); console.log('当前时间:', now.format('YYYY-MM-DD HH:mm:ss')); console.log('今天是星期', now.day()); // 0=周日, 1=周一...运行:
$ node app.js
注意:
node_modules目录很大,不要提交到Git仓库,需要在.gitignore中忽略它。
六、第一个项目实战:欢迎页面
现在让我们把前面的知识串联起来,创建一个带路由的小项目。
// project.js const http = require('http'); const fs = require('fs'); const path = require('path'); const server = http.createServer((req, res) => { const url = req.url; // 根据不同路径返回不同内容 if (url === '/') { res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' }); res.end(` <h1>🏠 首页</h1> <p>欢迎来到RUNOOB Node.js教程!</p> <ul> <li><a href="/about">关于</a></li> <li><a href="/time">当前时间</a></li> </ul> `); } else if (url === '/about') { res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' }); res.end(` <h1>📖 关于</h1> <p>这是RUNOOB Node.js入门教程的第一个项目。</p> <p>我们正在学习用Node.js创建Web服务器!</p> <a href="/">← 返回首页</a> `); } else if (url === '/time') { const now = new Date(); res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' }); res.end(` <h1>🕐 当前时间</h1> <p>服务器时间: ${now.toLocaleString('zh-CN')}</p> <p>时间戳: ${now.getTime()}</p> <a href="/">← 返回首页</a> `); } else { // 404 页面 res.writeHead(404, { 'Content-Type': 'text/html; charset=utf-8' }); res.end(` <h1>404 - 页面未找到</h1> <p>你访问的页面 "${url}" 不存在</p> <a href="/">← 返回首页</a> `); } }); server.listen(3000, () => { console.log('🚀 项目已启动: http://localhost:3000'); console.log('📂 按 Ctrl+C 停止服务器'); });运行并访问不同路径,体验Node.js的HTTP服务能力。
七、常见问题与调试技巧
7.1 端口被占用
# 报错:Error: listen EADDRINUSE: address already in use :::3000 # 解决方案:换一个端口(如3001),或关闭占用端口的进程
7.2 代码修改后需要重启
Node.js默认不会自动监测文件变化,修改代码后需要手动重启:
# 按 Ctrl+C 停止,再重新运行 $ node project.js
开发时可以使用nodemon实现自动重启:
# 全局安装nodemon npm install -g nodemon # 用nodemon运行 nodemon project.js
7.3 调试技巧
// 使用console.log打印调试信息 console.log('变量值:', someVariable); // 使用debugger语句(需要在Chrome开发者工具中调试) debugger; // 使用Node.js内置调试器 // node inspect project.js八、本章小结
| 知识点 | 说明 |
|---|---|
| Node.js是什么 | 服务端JavaScript运行环境 |
| 核心特点 | 事件驱动、非阻塞I/O、单线程事件循环 |
| 适用场景 | Web服务、API、实时应用、CLI工具 |
| 运行方式 | node xxx.js(脚本模式)/node(交互模式) |
| 包管理 | npm(安装、管理、发布第三方包) |