# 认识 Node.js
Node.js 是一门服务器编程语言,它也遵循了 ECMAScript 语法规范,在此规范的基础上加入了 Node.js API,包含处理 http 请求、处理文件、socket 编程等。Node.js API 和 ECMAScript 两者结合组成了 Node.js,完成 Server 端的任何操作,为客户端浏览器进行服务。
我们快一起去探索 Node.js 的世界吧~
# 知识点
- Node.js 的介绍
- 下载和安装 Node.js
# Node.js 的介绍
在实验介绍中说了,Node.js 是一门服务器编程语言。它发布于 2009 年 5 月,由 Ryan Dahl 开发,也是一个基于 Chrome V8 引擎的 JavaScript 运行环境,使用了一个事件驱动、非阻塞式 I/O 模型,让 JavaScript 运行在服务端的开发平台,它让 JavaScript 成为与 PHP、Python、Perl、Ruby 等服务端语言平起平坐的脚本语言。
Node.js 有如下特点和优势:
- 它是一个 JavaScript 运行环境
- 依赖于 Chrome V8 引擎进行代码解析
- 事件驱动(event-driven)
- 非阻塞 I/O (non-blocking I/O)
- 轻量、可伸缩,适于实时数据交互应用
- 单进程,单线程
学到这里有的同学可能有些疑惑,这么多特点都是什么意思呢?Node.js 相对于其他服务器语言,例如 Ruby、Java 又有什么区别和优势呢?别着急,老师马上为大家解答。
传统的服务器开发语言(例如 java)是多线程的,例如我们在淘宝商城购物,如果只有一个顾客发送请求,那显然是没问题的。但是当有成百上千万的用户同时访问的时候,我们肯定不希望别人买完以后才能轮到自己购买,这显然要等待很长时间。但是如果 CPU 只有一个核心的时候或者 CPU 只有一个线程的时候,确实要等待别人购买完毕以后才能购买。这就像在饭馆吃饭,如果只有一个厨师的话,同一时间只能给一个顾客做菜,这样必须要等待这一个厨师为别人做完菜以后才能为自己做菜。
为了解决这个问题,我们可以多增加几个厨师来同时为这些顾客做菜,这就是 java 语言的处理方式:使用多线程并发执行,但是这种方式势必会增加聘请厨师的成本和消耗更大的厨房空间,这无疑是一个资源的浪费,而且这种多线程模型,CPU(厨师) 在为客户服务的时候不能做其他的事,例如使用 I/O 读取文件的时候,只能等待文件系统读取文件完毕以后,才能继续做其他事。
为了解决上述问题 Node.js 应运而生。Node.js 是单线程模型、非阻塞 I/O、采用事件循环机制的原理进行处理。如下图:

非阻塞 I/O 的意思就是,文件系统在进行 I/O 操作的时候,Node.js 这个线程还可以做其他的事,当文件系统读取文件完毕时,通过事件的回调函数告诉 Node.js 线程,然后 Node.js 把读取的内容响应给用户。
Node.js 中的 I/O 操作可以理解成生活中的一个幕后工作者,就像在餐馆点餐这个场景中,饭馆可以直接去把点餐的任务派发给外部擅长做菜、煮饭、酿酒的厂家,这些厂家都是非常擅长这些工作的,Node 中也有各种擅长做某件事的模块。这样当这些幕后的厂家完成这些做菜、饭、酒的事件以后,把做完的饭菜酒等食品交给厨师,然后由厨师统一的让服务员把饭菜交给顾客。这样就大大提高了整个餐馆的运营速度。

如果大家还不明白的话,我们再看下一个例子,Node.js 就像国王,国王每天都在写任务清单,然后派发给大臣。大臣把任务清单交给下面的官员去做,而这个时候国王还是可以继续写任务清单的。当官员完成任务这个事件后,统一的任务结果交给国王(以事件回调函数的形式通知 Node.js)。此事件反复进行,这便是事件循环。所以说除了国王(Node.js)线程以外,每件事都是并行发生的,这便是 Node.js 单线程和事件循环能同时处理多个请求的原理。
# 在 Window 和 Mac 中下载和安装 Node.js
# 下载 Node.js
点击 Node 官网 进入 Node 官方网站,效果如下:

网站自动识别系统为 Windows ,默认给出了 Windows 的 Node 下载地址,如果使用 Mac 电脑访问则自动识别系统为 Mac 系统,自动给出 Mac 版下载地址。这里给出了两个版本的下载地址:
一.14.17.3 LTS 版(左侧)
即 Long Time Support 版(长时间支持版),即稳定版。
二.16.4.2 Current (右侧)
即最新版,包含最新的特性,但不稳定。
这里推荐下载 LTS 长时间支持版,点击 14.17.3 LTS 进行下载,下载后的文件如下:

另: Mac 下载相应 Mac 版本即可。
# 安装
windows 和 Mac 下都是直接双击安装文件,一路下一步即可。
# 查看是否安装成功
安装完成后,使用 cmd 进入到控制台,输入如下命令测试 Node 是否安装成功。
node - v; // 查看 node 版本 | |
npm - v; // 查看 npm 版本 |
如果看到如下版本信息,则表示 Node 安装成功。

需要说明的是:本课程是基于 node v14+ 的版本,低于该版本,请自行更新至对应版本中。
安装 Node 的时候会同时帮我们安装 npm (Node Package Manager) 即 Node 包管理工具,用于下载依赖的 node 包。
# linux 版 Node 下载和安装
打开我们的实验环境,在右侧控制台中输入如下命令:
node -v |
效果如下:

发现我们并没有安装 Node 就已经出现了 Node 版本,原因是我们的实验环境中已经默认安装了 Node 14.15.1 版。
下面给大家讲解一下 linux 下 Node.js 的下载和安装。
# 下载
进入 Node 官网 点击下图中的 Downloads 。

在弹出的界面中显示的 Node 的全部下载版本,这里我们下载 Linux Binaries(x64 版。见下图:

同学们也可以使用下列命令直接下载我们云服务器上的 Linux Node 安装文件。
wget https://labfile.oss.aliyuncs.com/courses/4380/node-v14.17.3-linux-x64.tar.xz |
使用上述命令下载后效果如下:

然后使用如下命令进行解压缩:
tar -xvf node-v14.17.3-linux-x64.tar.xz |
解压后效果如下

解压完毕后就可以把 node-v14.17.3-linux-x64.tar.xz 文件删除了。
然后使用如下命令对解压后的 node 文件夹进行改名:
mv node-v14.17.3-linux-x64 node |
改名后效果如下:

这时再次输入 node -v 查看版本,发现版本仍然是 14.15.1。

说明我们的 Node 安装并没有完成,如果想使用最新安装的 14.17.3 版,需要使用以下命令。
cd node/bin | |
./node -v |
注意这里是使用./node 来使用当前路径下的 node 命令,效果如下:

发现版本为 14.17.3 ,说明我们已经使用了自己下载的最新版本的 Node,但是每次都需要进入到 Node 安装路径的 bin 路径下才能使用 Node 命令,比较麻烦。如何能让我们在系统的任何地方都能使用最新安装的 Node 呢?这里需要修改我们的环境变量 PATH , 把 node/bin 这个路径添加到 PATH 下。
# 修改环境变量 PATH
为了在全局使用最新安装的 Node ,我们还需要修改一个配置文件 ~/.zshrc 。
PATH 作用:当我们在控制台输入命令的时候(例如 node -v),系统是去 PATH 环境变量下配置的路径中寻找这个命令是否存在,查找的路径顺序为从左到右依次查找(linux 路径分隔符为 :)。如果发现对应的 PATH 路径下有 node 命令就会使用,否则就会报错。
首先使用下列命令查看我们的环境变量:
env | grep node |
效果如下:

发现 PATH 中有 /usr/sbin/nodejs/bin ,这个路径就是我们实验环境默认安装的 14.15.1 的 Node 安装路径,如果想让我们新安装的 Node 优先执行的话,只需要把新版本 Node 的安装路径下的 bin 路径放到 /usr/sbin/nodejs/bin 路径前即可。
使用下列命令修改 ~/.zshrc 。
vim ~/.zshrc |
使用 i 进入插入模式,在文件最后加入如下内容:
export PATH=/home/project/node/bin:$PATH |
然后输入 ESC 回到普通模式,输入 : 进入命令模式,然后 输入 wq 进行保存。修改完成的文件并不会马上生效,需要使用如下命令让刚才配置的环境变量生效。
source ~/.zshrc |
最后输入 node -v 重新查看 node 版本,效果如下:

发现现在全局使用的已经是我们最新安装的 node 了。
# Web 请求
HTTP 协议(Hyper Text Transfer Protocol)是超文本传输协议,它是基于请求 - 响应的一个协议,即客户端浏览器给服务器发送一个 HTTP 请求,然后服务器对客户端的请求作出响应。我们开发的基于浏览器的 B/S 架构的程序都是基于 HTTP 协议(也有 HTTPS)的,例如登录、注册、查询商品等。
当用户在 URL 中输入一个网址到看到页面,大体经过如下几步:
- DNS 解析,建立 TCP 连接,发送 HTTP 请求。
- Server 端接收 HTTP 请求,处理,返回结果。
- 客户端接收到返回数据,进行页面渲染显示内容。
本节实验要给同学介绍的就是发送请求最常用的两种方式,GET 和 POST。
我们快一起去学习吧!
# 知识点
- GET 请求
- POST 请求
# 处理 GET 请求
通过浏览器向服务器发送请求的常用方式分为两种,一种是 GET 方式,另一种是 POST 方式,前者请求的安全性不高,常用于查询的操作,如查询用户信息、查询博客信息等。
GET 请求传递参数是在 URL 后面加入一个 "?" ,然后在 "?" 后面加入想要传递的参数,多个参数之间用 "&" 隔开。格式如下:
http://localhost:8000/index?id=1001&name=abc |
首先在右侧控制台中输入以下命令来初始化 npm 环境,初始化完成后会生成一个 package.json 文件。
npm init -y |
在 project 文件夹下,新建 index.js 文件,并加入下列代码:
const http = require("http"); | |
const server = http.createServer((req, res) => { | |
res.writeHead(200, { "Content-Type": "text/html;charset=utf-8" }); | |
// 通过请求对象获取完整的请求地址并保存在变量 url 中 | |
let url = req.headers["x-scheme"] + "://" + req.headers.host + req.url; | |
// 将变量传入实例化方法中,并实例化一个 URL 对象 | |
const myURL = new URL(url); | |
let params = myURL.searchParams.toString(); | |
res.write(params); | |
res.end(); | |
}); | |
// 服务侦听 8080 端口 | |
server.listen(8080, () => { | |
console.log("服务器运行在 8080 端口..."); | |
}); |
在本示例中代码解析如下:
定义一个变量
url用于保存req对象请求的完整地址,其中req.headers['x-scheme']返回请求协议名称,如http或https,而req.headers.host返回请求的域名和端口,req.url返回请求的详细路径,包含查询字符串。获取并保存完整的请求地址后,实例化一个
URL对象,在实例化对象的方法中,可以传递两个参数,第一个参数是必选项,表示要解析的绝对或相对的网址,第二个参数是可选项,表示要解析的基础网址,如果第一个参数是绝对地址,则第二个可以省略,如本示例中的代码;如果第一个对数是相对地址,则第二个参数必须添加请求地址的协议名称、域名和端口。如果将示例中绝对的请求地址修改成相对地址,那么在实例化
URL对象时,代码修改如下所示:// 通过请求对象获取相对请求路径并保存在变量 url 中let url = req.url;
// 在实例化过程中,通过第二个参数添加请求协议的名称、域名和端口const myURL = new URL(
url,req.headers["x-scheme"] + "://" + req.headers.host
);
这两种代码实现的方式在使用时,结果都是一样的,但在实际运用时,传入完整的请求地址,使用会更多些。
在控制台中输入以下命令运行该程序。
node index.js |
然后点击右侧的 “Web 服务”,并在 simplelab.cn 地址后加入 ?id=1001&name=abc ,刷新页面,效果如下:

页面中显示的内容就是以字符串形式输出的 GET 请求参数,如果需要使用,也可以通过指定参数名称获取对应的值。
它这里不是实时刷新的,当修改了服务器端的代码,就需要重新运行代码。
res.write() 传入的是字符串,没有自动换行。
# 处理 POST 请求
相对于 GET 方式请求, POST 请求在传输数据时,要安全很多,因此,常用用于数据的添加、删除、修改操作,例如新建博客、删除博客、修改博客等就会使用到 POST 请求。
在 URL 中输入地址直接访问属于 GET 请求,而 POST 请求则常用于表单数据的提交,先在 form 中设置 method=post ,当用户点击提交表单按钮时就会发送一个 POST 请求,并把表单中用户填入的数据传递给服务器。但是手工编写表单的代码显然比较麻烦,有没有一种更加简单的方式发送 POST 请求呢?
答案是有的。可以使用 Postman 这个工具来帮助我们发送 POST 请求。
# Postman 下载安装
首先在右侧桌面环境打开 Firefox 浏览器,然后在 URL 地址栏中输入 http://postman.com/downloads 进入到下载页面。系统自动识别下载 linux 版本的 postman 。

然后单击 Download the App 按钮进行下载,在弹出的界面中选择 Linux 64-bit 下载(如下图)。

单击” 保存文件” 按钮进行保存,如下图。

下载完毕后右上角会提示 “已完成”。

然后点击 “显示全部下载” 会弹出以下界面。

点击 “打开所在文件夹”,然后右键点击 Postman-linux-x86_64-8.11.1.tar.gz 文件重命名为 postman.tar.gz ,然后使用鼠标把该文件拖拽到左侧 shiyanlou 目录下。然后在实验楼路径中点击右键,选择 “在此打开终端”(如下图)。

然后在终端中输入如下命令进行解压。
sudo tar -zxvf postman.tar.gz |
使用如下命令启动 postman
./Postman/Postman |
启动后点击左上角的 "File"---"new" 新建一个窗口,效果如下:

然后申请一个 Postman 账号并点击 Sign in 登录(此过程不再截图,同学们自行完成)。登录成功后如下图,然后点击 Create new 。

在弹出的界面中点击 HTTP Request ,如下图:

如果想发送 HTTP 请求还需要装一个 Postman 客户端才能正常发送请求。使用下列命令下载 Postman 客户端。
cd /home/shiyanlou | |
wget https://labfile.oss.aliyuncs.com/courses/4380/postmanAgent.tar.gz |
注意:如果想复制如上命令到云课桌面环境进行粘贴的话,可以借助右侧 “剪切板” 功能。先复制好想要粘贴的命令,然后点击右侧 “剪切板”,把复制的内容粘贴到剪切板中,然后点击保存即可(如下图),然后就可以在云课桌面环境中进行粘贴了。

下载完毕后截图如下:

然后使用如下命令进行解压安装。
cd /home/shiyanlou | |
sudo tar -zxvf postmanAgent.tar.gz |
# 编写代码处理 POST 请求
然后在 /home/shiyanlou/ 路径下新建 index.js 文件,然后编写代码如下:
const http = require("http"); | |
const server = http.createServer((req, res) => { | |
if (req.method === "POST") { | |
console.log("content-type:", req.headers["content-type"]); // 获取请求类型 application/json | |
// 读 post 数据 | |
let postData = ""; //postData 用来存储传递给服务器的全部数据 | |
// 分段循环传输数据,每次传递数据都会执行后面的回调函数 | |
req.on("data", (chunk) => { | |
postData += chunk.toString(); //chunk 是二进制数据 所以要把它转换成字符串 | |
}); | |
// 当数据传输完毕会执行 end 事件后的回调函数 | |
req.on("end", () => { | |
console.log("postData:", postData); | |
res.end("Hello"); // 在这里返回因为是异步 | |
}); | |
console.log("test"); // 这里先被打印 因为上面的代码是异步的 | |
} | |
}); | |
server.listen(8080, () => { | |
console.log("服务器运行在 8080 端口..."); | |
}); |
在终端中输入以下命令运行该程序。
cd /home/shiyanlou | |
node index.js |
最终效果如下图所示:

res.end() 可以传入字符串作为参数,也可以不传入,所谓结束返回的标志。
# 使用 Postman 发送 POST 请求
然后打开刚才的 Postman 界面,根据下图依次选择 POST 请求,输入请求地址 http://localhost:8080 。然后点击 Body ,选择 raw<span> </span> 和 JSON ,输入发送的内容后点击 Send 按钮。

当看到上图中的返回内容 Hello 时,说明程序已经调试成功了,控制台输出如下:

# 实验总结
本节实验给大家介绍了两种最常用的 Web 请求,分别是 GET 和 POST。这里我们来总结一下:前者的链接中包含请求参数,安全系数低于 POST 请求。但 GET 请求也有自己的用处,比如查询操作时,显然 GET 请求是不错的选择。
# 初始化路由
# 知识点
- 认识路由
- 配置路由
- 访问路由
- 路由开发
# 认识路由
路由,顾名思义,它是指路径的指向或由来,即访问一个地址后它的指向,确定地址指向后,将会根据不同的路由地址,编写相应的代码,如下图所示。

一次地址的指向,实质上是一次数据请求的过程,在这种请求的过程中,还可以携带请求的方式,如 POST 或 GET,同时,也可以携带请求的参数,根据这些请求携带的方式和参数,即使是同一个地址,也可以执行不同的代码,如下图所示。

借助路由的这些特性,被广泛地应用于项目中各页面的切换,数据接口的请求,因此,路由是项目开发中必须要掌握的内容。在理解了它的重要性之后,如何去配置一个路由呢?带着这个问题,下面来说路由的配置方法。
# 配置路由
在 node 中配置路由十分简单,首先,使用以下命令下载搭建好的开发环境项目包,地址如下所示。
wget https://labfile.oss.aliyuncs.com/courses/4380/router.zip && unzip router.zip |
然后,解压该项目包至 project 文件夹中,并打开项目包 router 文件夹,找到 bin 文件夹,在该文件夹下,创建一个名称为 reqRouters 的 js 文件,加入如下所示的代码:
const reqRouters = (req, res) => { | |
if (req.path === "/aa") { | |
return "首页"; | |
} | |
if (req.path === "/bb") { | |
return "列表页"; | |
} | |
if (req.path === "/cc") { | |
return "详细页"; | |
} | |
}; | |
module.exports = reqRouters; |
在上述代码中,定义了一个名称为 reqRouters 函数,函数中,参数 req 表示请求时携带的对象,用于创建服务请求时的回调函数使用,在这个 req 对象中,通过 path 可以获取到请求时的路由地址,根据获取的不同地址,返回不同的内容,最后,输出这个名称为 reqRouters 的函数。
接下来,再次找到 bin 文件夹,打开名称为 app 的 js 文件,删除原有的内容,加入如下所示的代码。
const reqRouters = require("./reqRouters"); | |
const serverHandle = (req, res) => { | |
res.setHeader("Content-Type", "application/json"); | |
req.path = req.url.split("?")[0]; | |
const result = reqRouters(req, res); | |
if (result) { | |
res.write(result); | |
res.end(); | |
return; | |
} | |
res.writeHead(404, { "Content-Type": "text/plain" }); | |
res.write("404 Not Found\n"); | |
res.end(); | |
}; | |
module.exports = serverHandle; |
在上述代码中,首先,使用 require 输入 reqRouters 模块,用于获取请求输出的内容,其次,定义一个名称为 serverHandle 的函数,用于服务创建时的回调,在该函数中,向 reqRouters 函数传入请求的路由地址,获取输出的内容并保存至变量 result 中,最后,判断变量 result 中是否有内容,如果有,则直接输出在页面中,否则,在页面中输出 "404 Not Found" 的信息。
最后,找到 bin 文件夹,打开名称为 index 的 js 文件,删除原有的内容,加入如下所示的代码。
const http = require("http"); | |
const serverHandle = require("./app"); | |
const port = 8080; | |
const server = http.createServer(serverHandle); | |
server.listen(port, () => { | |
console.log("服务器运行在 8080 端口..."); | |
}); |
在上述代码中,首先,分别导入 http 和 serverHandle 模块,前者用于创建一个新的服务,是 node 自带模块, 后者用于创建服务后的回调函数,当服务创建成功后,使用 listen 方法,在指定的 8080 端口下侦听,当服务启动后,就可以在浏览器的地址栏中,根据启动的地址和端口访问这个服务了,由于配置的路由在服务中,这时,就可以按配置的路由访问页面了 🤪
# 访问路由
要访问路由,需要先启动服务,因此,首先,在项目文件夹 router 下,打开终端界面,并输入如下指令。
npm run dev |
服务启动后的界面效果如下图所示:

当服务成功启动后,就可以在浏览器中,根据指定的地址和端口,访问相应的页面内容,效果如下所示:

当在浏览器地址栏中输入 localhost:8080/aa 时,它的 url.path 值为 '/a' ,传给 reqRouters 函数后,则返回 "首页" 字符,因此,页面中输出 "首页" 内容,其他输入地址依此类推,根据不同的 url.path 值,向页面输出不同的内容,最终实现路由的功能。
# 路由开发
# 获取请求方式
在发送一次请求时,不同的请求方式,将会返回不同的数据,目前常用的请求方式分为 GET 和 POST 两种,前者常用于查询请求,后者用于增加、修改和删除的请求,那么,在 node 中,如何获取路由中的请求方式呢?下面通过一个示例来进行说明。
首先,使用以下命令获取搭建好的路由初始化项目包,地址如下所示。
wget https://labfile.oss.aliyuncs.com/courses/4380/router-param.zip && unzip router-param.zip |
打开项目包 router-param 中的 bin 文件夹,在该文件夹下,找到并打开名称为 reqRouters 的 js 文件,删除原有的代码,加入如下所示的代码:
const reqRouters = (req, res) => { | |
if (req.method === "GET") return "这是一次 GET 方式请求"; | |
if (req.method === "POST") return "这是一次 POST 方式请求"; | |
}; | |
module.exports = reqRouters; |
在上述代码中,首先,通过 req 对象中的 method 属性获取到服务请求的方式,然后根据不同的请求方式,返回不同的文字内容,最后,输出这个名称为 reqRouters 的函数。
修改完成后,在项目文件夹 router-param 下,打开终端界面,并输入如下指令。
npm run dev |
服务启动后的界面效果如下图所示:

当服务成功启动后,就可以在浏览器中,根据指定的地址和端口,访问相应的页面内容,由于是以 GET 和 POST 发送请求,因此,需要借助之前安装好的 Postman 工具,更多与 Postman 相关的内容,请点击 👉参阅资料, 其实现的效果分别如下:
- 如果是
GET方式请求,则返回 "这是一次 GET 方式请求" 内容,如下图所示。

- 如果是
POST方式请求,则返回 "这是一次 POST 方式请求" 内容,如下图所示。

在上述效果示意图中,我们看到,通过 req 对象中的 method 属性可获取每次请求的方式,除获取每次请求的方式之外,还可以获取路由中传递的参数值,根据请求方式的不同,获取的方式也不一样,接下来,我们先来介绍获取 GET 方式传入的参数值。
# 获取 GET 方式传参
在进行 GET 方式请求时,如果需要传递参数值,通常添加在 url 的后面,使用 ? 进行标记,通过 key=value 的形式进行设置,多个参数值之间使用 & 进行连接,如下图所示:

获取 GET 方式传参的过程实质是根据参数名称,获取对应值的过程,在这个过程中,先将传入的地址作为参数,实例化一个 URL 对象,然后通过该对象获取表示网址查询参数的 searchParams 对象,最后,在 searchParams 对象中,根据传入参数名称,获取对应的值,接下面我们通过一个示例来演示参数值获取的过程。
打开项目包 router-param 中的 bin 文件夹,在该文件夹下,找到并打开名称为 reqRouters 的 js 文件,删除原有的代码,加入如下所示的代码:
// 引入 url 模块 | |
var url = require("url"); | |
const reqRouters = (req, res) => { | |
if (req.method === "GET") { | |
const myURL = new URL(req.url, req.headers.host); | |
let params = myURL.searchParams; | |
if (params.get("id") && params.get("name")) { | |
return params.get("id") + "," + params.get("name"); | |
} else { | |
return "没有传入相应参数!"; | |
} | |
} | |
}; | |
module.exports = reqRouters; |
在上述代码中,首先判断请求否是为 GET 方式 ,如果是,则实例化一个 URL 对象,在实例化过程中,需要传入两个参数,第一个参数是请求的 url 地址,通过 req.url 获取,第二个参数是 base 表示要解析的基本网址,包括地址的域名和端口,如果 url 地址是相对的,则需要添加第二个参数,如果是绝对的,则可以省略第二个参数。
在上述代码中,通过这个实例化后的 URL 对象,将保存网址查询参数的 searchParams 对象赋值给变量 params ,然后再判断 params 中是否存在对应参数名称的值,如果存在,则返回该对应值,否则,返回 "没有传入相应参数!" 的错误信息。
修改完成后,在项目文件夹 router-param 下,打开终端界面,并输入如下指令。
npm run dev |
当服务成功启动后,借助之前安装好的 Postman 工具,使用 GET 方式,在当前域名下,首先请求一个指定参数名称和值的地址,然后请求一个不传参数的地址,在点击 "发送" 按钮后,其实现的效果分别如下图所示:

在上述效果示意图中,我们看到,路由中传入的两项参数都获取成功,相比获取 GET 方式传入的参数值而言,获取 POST 方式传入的参数值就要复杂很多,接下来我们再来说下获取 POST 方式传入参数值。
# 获取 POST 方式传参
相对于使用 GET 方式请求而言,使用 POST 方式更加安全,因此,常用于增加、修改和删除数据的操作,使用 POST 方式传参时,携带的请求数据并不在路由中,而是在请求对象中,因此,需要绑定请求过程的两个事件,一个是 data 事件,在该事件中获取并累加每次请求传入的参数值, 另一个是 end 事件,该事件在请求结束时触发,在事件中处理累加后的请求参数,并输出至页面中,接下面我们通过一个示例来演示参数值获取的过程。
打开项目包 router-param 中的 bin 文件夹,在该文件夹下,找到并打开名称为 index 的 js 文件,删除原有的代码,加入如下所示的代码:
const http = require("http"); | |
const serverHandle = (req, res) => { | |
if (req.method === "POST") { | |
var strPOST = ""; | |
// 绑定数据请求事件,每当接受到请求体的数据,就累加到 strPOST 变量中 | |
req.on("data", function (chunk) { | |
strPOST += chunk; | |
}); | |
// 绑定数据请求结束事件,向页面输出指定的参数值 | |
req.on("end", function () { | |
res.setHeader("Content-Type", "application/json"); | |
let objPOST = new URLSearchParams(strPOST); | |
if (objPOST.get("id") && objPOST.get("name")) { | |
const result = objPOST.get("id") + "," + objPOST.get("name"); | |
res.write(result); | |
} else { | |
res.write("没有传入相应参数!"); | |
} | |
res.end(); | |
return; | |
}); | |
} | |
}; | |
const port = 8080; | |
const server = http.createServer(serverHandle); | |
server.listen(port, () => { | |
console.log("服务器运行在 8080 端口..."); | |
}); |
在上述代码中,首先判断请求否是为 POST 方式 ,如果是,则分别绑定请求时的两个事件,一个是 data 事件,在该事件中,将每次发送参数值累加并保存在变量 strPOST 中, 另一个是 end 事件,在该事件中 ,当请求结束后,将传入并累加后的参数解析为查询字符串,并使用它来实例化新的 URLSearchParams 对象,最后,在 URLSearchParams 对象中,先判断传入的参数名称是否存在获取对应的值,如果存在,则并输出到页面中,否则,向页面输出 "没有传入相应参数!" 的错误信息。
修改完成后,在项目文件夹 router-param 下,打开终端界面,并输入如下指令。
npm run dev |
当服务成功启动后,借助之前安装好的 Postman 工具,使用 POST 方式,请求一个当前域名下指定参数名称和值的地址,点击 "发送" 按钮后,其实现的效果如下图所示:

# Node.js 文件操作
我们知道,在前端在开发时,JavaScript 是一种依赖浏览器解析的语言,在访问文件时,需要依赖浏览器的兼容性,操作文件的能力很弱 ,而 Node 则这方面有很强的能力,那么,它是如何操作一个文件的呢?带着这个问题,我们快速地进入到今天的学习中来吧!
# 知识点
- 文件读取操作
- 文件写入操作
- 删除文件操作
# 文件读取操作
在 node 中,如果要读取一个文件,就必须引入一个名称为 fs 的模块,而 fs 是 file-system 的简写,表示文件系统,在该模块中提供了全部操作文件的方法,因此,如果想去读取一个文件,必须先导入该模块,代码如下所示。
const fs = require("fs"); |
完成模块导入之后,就可以使用该模块中读取文件的方法 fs.readFile() ,该方法是一个异步读取文件的方法,它的调用格式如下所示:
fs.readFile(path[, options], callback) |
在上述格式中,第一个参数 path 表示读取文件的路径,通常是一个相对路径,如 "./public/msg.txt",第二个参数 options 是一个可选项,在该项中可以设置读取文件时的编码格式 encoding ,文件打开的行为 flag ,允许中止正在进行的读取文件 signal ,其中 flag 包含多种方式,具体如下表所示:
(诶?这都已经是 readFile 了,那么为什么要写这些 flag 呢?不就是 r 了吗)
| flag 名称 | 描述 |
|---|---|
| a | 打开文件进行追加。 如果文件不存在,则创建该文件。 |
| ax | 类似于 a 但如果路径存在则失败。 |
| a+ | 打开文件进行读取和追加。 如果文件不存在,则创建该文件。 |
| ax+ | 类似于 a+ 但如果路径存在则失败。 |
| as | 以同步模式打开文件进行追加。 如果文件不存在,则创建该文件。 |
| as+ | 以同步模式打开文件进行读取和追加。 如果文件不存在,则创建该文件。 |
| r | 打开文件进行读取。 如果文件不存在,则会发生异常。 |
| r+ | 打开文件进行读写。 如果文件不存在,则会发生异常。 |
| rs+ | 以同步模式打开文件进行读写。 指示操作系统绕过本地文件系统缓存。 |
| w | 打开文件进行写入。 如果它不存在则创建,如果它存在则截断该文件。 |
| wx | 类似于 w 但如果路径存在则失败。 |
| w+ | 打开文件进行读写。 如果它不存在则创建,如果它存在则截断该文件。 |
| wx+ | 类似于 w+ 但如果路径存在则失败。 |
第三个参数 callback 是一个回调函数, 在回调函数中,传入了两个参数,一个参数的名称为 error ,表示文件未读取成功时的错误信息,另一个参数的名称为 data ,表示文件读取成功时的内容信息,接下来通过一个示例来演示文件读取的过程。
找到并打开 project 文件夹,在该文件夹下,创建一个名称为 public 的文件夹,在 public 文件夹中,新建一个名称为 msg 的记事本文件,打开该文件,添加下列文字内容并保存。
蓝桥,一个学习知识的好地方! | |
--程序员寄语-- |
再次找到并打开 project 文件夹,在该文件夹下,新建一个名称为 readfile 的 js 文件,打开该文件,并添加下列代码:
// 导入 fs 模块 | |
const fs = require("fs"); | |
// 调用模块中异步读取文件的方法 | |
fs.readFile("./public/msg.txt", "utf-8", (error, data) => { | |
if (error) throw error; | |
console.log(data); | |
}); |
在上述代码中,先导入 fs 模块,再调用模块中的异步读取文件的方法 readFile ,以 "utf-8" 的编码格式,读取记事本 msg 文件中的内容,在方法的回调函数中,如果出现错误,则返回相关的错误信息,如果没有出现错误,在控制台输出读取到的文件内容。最后,在 project 文件夹下,打开终端,并使用 node 执行名称为 readfile 的 js 文件,最终的效果如下图所示:

从最终读取出的内容效果看,使用 fs 模块中的 readFile 方法,可以很方便地读取到文件中的内容,
# 文件写入操作
在 fs 模块中,与文件读取方法 readFile 相对应, writeFile 方法则是用于文件的写入,该方法也是一个异步写入文件的方法,它的调用格式如下所示:
fs.writeFile(file, data[, options], callback) |
在上述格式中,方法中第一个参数 file 表示要写入的文件名,第二个参数 data 表示需要写入的内容,第三个参数 options 是一个可选项,它与文件读取方法中的功能一样,在此不再赘述,第四个参数 callback 是一个回调函数, 在回调函数中,传入了一个名称为 error 的参数,表示文件没有写入成功时的错误信息,接下来通过一个示例来演示文件写入的过程。
找到并打开 project 文件夹,在该文件夹下,新建一个名称为 writefile 的 js 文件,打开该文件,并添加下列代码:
// 导入 fs 模块 | |
const fs = require("fs"); | |
// 调用模块中异步写入文件的方法 | |
fs.writeFile("./public/msg.txt", "今天是一个好日子", (error) => { | |
if (error) throw error; | |
console.log("写入成功!"); | |
}); |
在上述代码中,先导入 fs 模块,再调用模块中的异步写入文件的方法 writeFile ,将字符串 "今天是一个好日子" 写入 msg 文件中,由于该文件已存在,因此,写入的内容将会覆盖文件中原有的内容,同时,在方法的回调函数中,如果出现错误,则返回相关的错误信息,如果写入没有出错,则在控制台输出 "写入成功!" 的字样。最后,在 project 文件夹下,打开终端,并使用 node 执行名称为 writefile 的 js 文件,效果如下图所示:

当调用 fs 模块中的 writeFile 方法在指定的文件中写入内容后,文件中的原有内容默认将会被覆盖,只保留最新写入的内容,也可以借助 options 对象中的 flag 属性值,改变写入文件时的方式,由覆盖变为追加内容,接下来通过一个示例来演示它实现的方式。(或者先读取保存,然后字符串拼接再写)
找到并打开 project 文件夹,在该文件夹下,新建一个名称为 writefile2 的 js 文件,打开该文件,并添加下列代码:
// 导入 fs 模块 | |
const fs = require("fs"); | |
// 调用模块中异步写入文件的方法 | |
fs.writeFile( | |
"./public/msg.txt", | |
",明天又是一个大晴天。", | |
{ flag: "a", encoding: "utf-8" }, | |
(error) => { | |
if (error) throw error; | |
console.log("写入成功!"); | |
} | |
); |
打开终端,并使用 node 执行名称为 writefile2 的 js 文件,效果如下图所示:

在上述实现的代码中, flag 值为 "a" 时,表示以追加的方式向文件中写入内容,其他方式大家可以根据这种方式进行变更。
# 删除文件操作
在 fs 模块中,可以使用 writeFile 方法增加一个指定名称的文件,如果不希望使用某个文件,也可以调用 unlink 方法,删除这个文件, unlink 方法也是一个异步的方法,它的调用格式如下所示:
fs.unlink(path, callback); |
在上述格式中,第一个参数 path 表示删除文件的路径,第二个参数 callback 是一个回调函数, 在回调函数中,传入了一个名称为 error 的参数,表示文件没有删除成功时的错误信息,接下来通过一个示例来演示文件删除的过程。
首先找到并打开 project 文件夹,在该文件夹下,再打开 public 文件夹,新建一个文件为 temp 的记事本文件,结构如下图所示:

最后,在 project 文件夹下,新建一个名称为 delefile 的 js 文件,打开该文件,并添加下列代码:
// 导入 fs 模块 | |
const fs = require("fs"); | |
// 调用模块中异步删除文件的方法 | |
fs.unlink("./public/temp.txt", (error) => { | |
if (error) throw error; | |
console.log("删除成功!"); | |
}); |
在上述代码中,先导入 fs 模块,再调用模块中的异步删除文件的方法 unlink ,在方法的回调函数中,如果出现错误,则返回相关的错误信息,如果删除没有出错,则在控制台输出 "删除成功!" 的字样。最后,在 project 文件夹下,打开终端,并使用 node 执行名称为 delefile 的 js 文件,效果如下图所示:

注意:在 fs 模块中的 unlink 方法,只能删除文件,不适用于删除目录,即使是一个空目录,如果要删除目录,需要使用另外一个名称为 rmdir 的方法,关于 rmdir 更的资料,请查看 👉 rmdir 使用方法