NodeJs 速通

前言:参考 Node.js 中文网,可以通过 Node.Js V19 API 文档查看常用 API 。

番外

番外知识点,多学点总没坏处 😀😀😀。

1. 计算机的基本组成

计算机由 CUP、内存、硬盘、显卡、主板、散热器 等组成。

1.1 CUP

中央处理器,是整个计算机运算和控制的中心

1.2 内存 

是存储数据的介质,读写速度快,但是断电丢失数据。我们程序在运行时,就会载入内存当中,让 CUP 高速运行程序。

1.3 硬盘

和内存很像,用于存放数据,读写速度慢,断电不丢失数据。我们安装的应用程序就装在硬盘中,比如 QQ、微信等

1.4 显卡

处理视频信号的,当有信息需要在显示器呈现时,就要传递给显卡,显卡处理完毕后,再传输给显示器。

1.5 主板

集成电路板,上面有很多插槽,可以插入 CUP 处理器,内存条、硬盘、显卡等。

2. 程序运行基本流程

安装的程序在硬盘,运行程序在内存,CUP 从内存中拿程序执行,执行时有视频信号传给显卡,有声音信号传给声卡。

3. 进程和线程

3.1 进程

运行中的程序,每一个运行中的程序都有一个属于自己的进程,这些进程占用内存空间。

3.2 线程

进程中的任务,一个进程至少有一个任务,就是一个线程,不然你开了一个程序,但是这个程序什么都不做,那它就没有存在的意义。也可以有多个任务,就是我们常说的多线程。

4. HTTP 协议

超文本传输协议,是互联网运用最广泛的协议之一。协议是什么?协议就是双方必须遵从的一组约定。HTTP 协议是对浏览器和服务器双方的约定。

4.1 请求报文

请求报文由请求行、请求头、请求体构成。以访问百度为例。

4.1.1 请求行

由三部分组成:请求方法、URL、HTTP 协议版本号。

请求方法:GET 获取数据、POST 新增数据、PUT/PATCH 修改数据、DELETE 删除数据

URL:用来定位服务器资源,由协议、域名、端口、路径、查询字符串组成

HTTP 版本号:有 1.0、1.1、2、3 四个版本号

GET https://www.baidu/com/ HTTP/1.1
4.1.2 请求头

由一系列的键值对组成,用来记录浏览器的相对信息和交互行为等。

HTTP/1.1 200 OK
Bdpagetype: 1
Bdqid: 0x9a760cca0000e1ee
Connection: keep-alive
Content-Encoding: gzip
Content-Type: text/html; charset=utf-8
Date: Fri, 21 Jun 2024 03:44:22 GMT
Server: BWS/1.1
Set-Cookie: H_PS_PSSID=60297_60338_60352_60346_60364_60360; path=/; expires=Sat, 21-Jun-25 03:44:22 GMT; domain=.baidu.com
Set-Cookie: BDSVRTM=8; path=/
Set-Cookie: BD_HOME=1; path=/
Strict-Transport-Security: max-age=172800
Traceid: 1718941462054085735411130097590815744494
X-Ua-Compatible: IE=Edge,chrome=1
X-Xss-Protection: 1;mode=block
Transfer-Encoding: chunked
4.1.3 请求体

请求体的构成就很灵活,我们用的最多的 JSON 格式。

4.2 响应报文

响应报文由响应行、响应头、响应体构成。以访问百度为例。

4.2.1 响应行

由三部分组成:HTTP 版本号、响应状态码、响应状态描述

HTTP 版本号:有 1.0、1.1、2、3 四个版本号

响应状态码:200 请求成功、403 禁止请求、404 找不到资源、500 服务端错误。

响应状态码五大类:1xx 信息响应、2xx 成功响应、3xx 重定向信息、4xx 客户端错误、5xx 服务端错误

响应状态描述:200 OK、403 Forbidden、404 Not Found、500 Internal Server Error

HTTP/1.1 200 OK
4.2.2 响应头

记录服务器相关的内容。

4.2.3 响应体

响应体内容格式很灵活:HTML、CSS、JS、图片、视频、JSON

5. 网络基础概念

5.1 什么是 IP

就是你上网设备在互联网上的地址。比如:192.168.1.1

5.2 IP 有什么用

IP 用来标识网络中的设备,实现设备通信。只要设备接入互联网,就都会有一个 IP 地址。当你给你小伙伴发消息时,你发的消息就带有你的 IP 地址和你小伙伴的 IP 地址,这样才能实现通信。

5.3 IP 分类

每一个接入互联网的设备都有一个自己的 IP 地址,而互联网上的 IP 总共也才 42 亿,全球人口 80 亿+,每个人的联网设备还不止一个。有手机,电脑,手表,电视,智能家居等,这么多设备根本不够分。所以就有了共享 IP,分为区域共享 IP 或者家庭共享 IP。比如你家里有手机、笔记本、电视,然后这些设备通过 wifi 或者网线都链接到了路由器上,路由器会为每个设备分配 IP 地址,路由器本身也有 IP 地址比如 192.168.1.1,给你手机分个 IP 192.168.1.2,电脑 192.168.1.3,电视 192.168.1.4。我们的设备通过路由器链接到了一起,形成了个网络,这我们称之为局域网。路由器分配给我们的 IP 地址,称为局域网 IP。在这个网络里,我们的设备是可以通信的。但是如果你想给你远方的女友发个消息 “吃了吗”,目前的网络是不行的。需要将路由器接入互联网。如何接入互联网呢,去找电信、联通、移动办理业务。办理完业务后路由器就有了另外一个 IP,比如 180.91.213.151,这就是公网 IP。家里设备共享的 IP 就是这个公网 IP,有了这个 IP,你就可以和远方的女友通信了。

局域网 IP:192.168.0.0 ~ 192.168.255.255、172.16.0.0 ~ 172.31.255.255、10.0.0.0 ~ 10.255.255.255

公网 IP:除了局域网 IP 和本地回环 IP

本地回环 IP:127.0.0.1 ~ 127.255.255.254 这类 IP 地址是指向本机的。称为本地回环地址。

5.4 什么是端口

应用程序的数字标识,一台计算机有 65536 个端口(0 ~ 65535),一个应用程序可以使用一个或多个端口。什么是应用程序?就是你安装到计算机上的软件,比如微信、QQ、游戏等。

5.5 端口有什么用

实现不同主机应用程序之间的通信的。比如两台计算机的微信相互通信,如果只有 IP 没有端口,那计算机不知道接受到的报文,用什么程序去处理。

一:入门指南

1. Node.js 简介

1.1 什么是 Node.js

Node.js 是一个运行环境,这个运行环境是基于谷歌 v8 引擎的,用于在服务端运行 JS 代码。

Node.js 采用事件驱动、非阻塞式 I/O 的设计理念,使 JS 能高效地处理大量并发请求。

1.2 Node.js 可以做什么

开发服务器应用、工具类应用、桌面端应用。

1.2.1 服务器应用

可以对用户请求做出处理,返回资源。

1.2.2 工具类应用

Webpack、Vite、Babel

1.2.3 桌面端应用

VsCode、PostMan、Figma 都是使用 electron 开发的,electron 是基于 node 开发的

1.3 Node.js 的优、缺点

优点:因为 Node.js 采用事件驱动、非阻塞式 I/O 的设计理念,所以使 JS 能高效地处理大量并发请求。

缺点:大前端,木有缺点!

1.4 示例

使用 Node.js 启动一个 Web 服务器,首先创建一个 server.js,并在 server.js 所在文件夹点右键打开终端,运行 node server.js 命令。

// server.js
const http = require("http");const hostname = "127.0.0.1";
const port = 3000;const server = http.createServer((req, res) => {res.statusCode = 200;res.setHeader("Content-Type", "text/plain");res.end("Hello World\n");
});server.listen(port, hostname, () => {console.log(`Server running at http://${hostname}:${port}/`);
});

2. 如何安装 Node.js

点击安装 nodejs,推荐使用 nvm,因为 nvm 是 nodejs 的版本管理工具,便于切换 node 版本,教程 nvm 用法

3. Node.js 和浏览器的区别

可操作的 API 不同:浏览器可以操作 DOM、BOM。Node.js 不行,因为不存在这些,Node.js 有 fs、path、http 等模块可以操作,浏览器没有。

模块系统不同:浏览器只支持 ES 模块系统,Node.js 同时支持 CommonJs 和 ES 模块系统

二、npm 包管理

可以参考 npm 包管理 

1. npm 简介

npm 是 Node.js 的标准包管理器。我们习惯使用 yarn 或 pnpm 来代替 npm。因为可以更快安装依赖。

2. 包

就是通过 npm 下载到项目中的依赖统称为包,npm 管理项目依赖的下载。

3. 安装依赖

如果项目有一个 package.json 文件,通过运行 npm install 来安装依赖。它会将所需依赖安装到 node_modules 文件夹,如果尚不存在,则创建它。

npm install
# or
yarn install
# or
pnpm install

4. 安装单个包

可以通过包名来安装特定的包。

npm install <package-name>
// or
yarn add <package-name>
# or
pnpm add <package-name>

5. 更新软件包

npm 将检查所有软件包是否有满足版本控制约束的较新版本。

npm update
# or
yarn update
# or
pnpm update

6. 更新单个包

npm update <package-name>
# or
yarn update <package-name>
# or
pnpm update <package-name>

7. 指定安装固定版本包

指定版本有助于让每个人都使用相同的软件包版本,以便整个团队运行相同的版本。

npm install <package-name>@<version>
# or
yarn add <package-name>@<version>
# or
pnpm add <package-name>@<version>

8. 运行任务

package.json 文件支持一种格式,用于指定可以使用以下方式运行的命令行任务

{"scripts": {"watch": "webpack --watch --progress --colors --config webpack.conf.js","dev": "webpack --progress --colors --config webpack.conf.js","prod": "NODE_ENV=production webpack -p --config webpack.conf.js"}
}

可以通过 npm run <task-name> 来代替那么长串命令。

npm run dev
# or
yarn dev
# or
pnpm dev

9. 安装全局依赖

通过 npm root -g 查看全局安装的位置。nodemon 的作用是动态更新服务文件。

npm install -g nodemon

三、NodeJs 常用模块

1. Buffer 模块

Buffer 是一个固定长度的内存空间,用来处理二进制数据。

Buffer 的特点:1. 大小固定无法调整。2. 性能较好,可以直接对计算机内存进行操作。3. 每个元素的大小为 1 字节。

1.1 Buffer 的创建

Buffer 为全局属性可以直接使用,alloc 代表分配,如下给 buf 变量分配 10 个字节。

let buf = Buffer.alloc(10); // <Buffer 00 00 00 00 00 00 00 00 00 00>

allocUnsafe 代表不安全分配,和上面运行结果一样,有什么区别勒?使用 allocUnsafe 创建的数据可能会包含旧的内存数据。

let buf = Buffer.allocUnsafe(10); // <Buffer 00 00 00 00 00 00 00 00 00 00>

from 可以将字符串、数组转换为 buffer,每个字符都会转换为 unicode 码表码表的数字,数字转换成二进制保存在码表中。

let buf = Buffer.from("yqcoder"); // <Buffer 79 71 63 6f 64 65 72>

1.2 Buffer 与字符串转换

使用 toString() 方法,将 buffer 转换为字符串。

let buf = Buffer.from("yqcoder");
let name = buf.toString(); // yqcoder

1.3 Buffer 和中文转换

中文转 Buffer,一个汉字占 3 个字节

let buf = Buffer.from("你好"); // <Buffer e4 bd a0 e5 a5 bd>

1.4 通过 [] 操作单个字符

和数组类似,通过 [] 下标的方式,实现读取和修改

let buf = Buffer.from("yqcoder");
buf[0]; // 121

2. fs 模块

fs 可以和我们硬盘进行交互,比如文件的创建、删除、重命名、移动。文件内容的写入、读取。和文件夹相关操作。

注:fs 中的相对路径相对的是打开命令行工具的目录,而不是执行文件的目录,这就容易出问题。使用__dirname 拼接绝对路径解决这个问题。__dirname 表示执行文件所在文件夹的绝对路径。__filename 表示执行文件的所在路径

2.1 写入文件

使用 fs.writeFile(file, data[, options], callback) 创建和写入文件,file 文件名、data 写入数据、options 可选配置、callback 回调函数。

writeFile 是一个异步操作,js 的主线程开始执行这段代码,当执行到 writeFile 时会进行磁盘的写入,并将磁盘写入操作交给 I/O 线程去完成。I/O 线程会在写入完毕后,将回调函数放入任务队列中,在主线程代码执行完毕后,再执行任务队列的函数。这就是传说中的事件循环机制。

const fs = require("fs");
fs.writeFile("./demo.txt", "hello yqcoder", (err) => {if (err) {console.log("写入失败");return;}console.log("写入成功!");
});

writeFileSync 同步操作。使用和 writeFile 差不多,只是没有回调函数了,主线程执行到 writeFileSync 时,后停止执行后续代码,等 I/O 线程执行完毕后,再继续执行后续代码。如下使用 try catch 是为了防止读写失败后,阻塞程序继续运行。

const fs = require("fs");
try {fs.writeFileSync("./demo.txt", "yqcoder");
} catch (err) {}

2.2 追加内容

使用 fs.appendFile(file, data[, options], callback),参数含义同 writeFile 的参数。在文件末尾添加内容,如果文件不存在,先创建文件再添加内容。使用 \r\n 实现添加文本换行。同步操作 fs.appendFileSync。

const fs = require("fs");
fs.appendFile("./demo.txt", "\r\nyqcoder", (err) => {if (err) {console.log("写入失败");return;}console.log("写入成功!");
});

也可以使用 fs.writeFile,添加配置。

const fs = require("fs");
fs.writeFile("./demo.txt", "hello yqcoder", { flag: "a" }, (err) => {if (err) {console.log("写入失败");return;}console.log("写入成功!");
});

2.3 流式写入

通过 fs.createWriteStream(path[, options]) 创建一个实例,path 文件路径。options 可选配置。使用实例方法 ws.write(data) 往目标文件写入内容。和 writeFile 的区别是 writeFile 是一次性写入,createWriteStream 是打开一个通道,在通道没有关闭之前,可以断断续续的给文件写入内容。适合写入频率高的场景。

const fs = require("fs");
const ws = fs.createWriteStream("./demo.txt");
ws.write("yqcoder,");
ws.write("hello");
ws.close();

2.4 文件读取

使用 fs.readFile(path[, options], callback) 读取文件,path 文件路径,options 可选配置,callback 回调函数。readFileSync(path[, options])同步读取。

const fs = require("fs");
fs.readFile("./demo.txt", (err, data) => {if (err) {console.log("读取失败");return;}console.log(data); // <Buffer 79 71 63 6f 64 65 72 2c 68 65 6c 6c 6f>console.log(data.toString()); // yqcoder,hello
});

2.5 流式读取

通过 fs.createReadStream(path[, options]) 创建一个实例,path 文件路径。options 可选配置。监听 data 事件 ,ws.on('data', (chunk) => {}) 每次获取 64kb 的文件内容 chunk。读取完成后,触发 end 事件。和 readFile 的区别是 readFile 是一次性读取,createReadStream 是流失读取,能提升读取大文件效率。

const fs = require("fs");
const rs = fs.createReadStream("./demo.txt");
rs.on("data", (chunk) => {console.log(chunk.length);
});
rs.on("end", () => {console.log("读取完成");
});

2.6 重命名和移动

使用 fs.rename(oldPath, newPath, callback) 重命名文件,oldpath 就文件地址,newPath 新文件地址,callback 回调函数。renameSync(oldPath, newPath)同步命名。

使用重命名 API 可以实现文件移动的效果。

const fs = require("fs");
fs.rename("./demo.jpg", "./newDemo.jpg", (err) => {if (err) {console.log("重命名失败");return;}console.log("重命名成功");
});

2.7 文件删除

使用 fs.unlink(path, callbck) 删除文件,path 文件路径,callback 回调函数。unlinkSync(path) 同步删除。

也可以使用 fs.rm(path, callbck) 删除文件,path 文件路径,callback 回调函数。rmSync(path) 同步删除。

const fs = require("fs");fs.unlink("./newDemo.jpg", (err) => {if (err) {console.log("删除失败");return;}console.log("删除成功");
});
// or
fs.rm("./newDemo.jpg", (err) => {if (err) {console.log("删除失败");return;}console.log("删除成功");
});

2.8 文件夹操作

2.8.1 创建

使用 fs.mkdir(path[, options], callback) 创建文件夹,path 文件夹路径,options 可选配置,callback 回调函数。mkdirSync(path[, options]) 同步创建。

const fs = require("fs");
fs.mkdir("./demo", (err) => {if (err) {console.log("创建文件夹失败");return;}console.log("创建文件夹成功");
});

递归创建文件夹,需要添加配置 { recursive: true }

const fs = require("fs");
fs.mkdir("./demo/assets/img", { recursive: true }, (err) => {if (err) {console.log("创建文件夹失败");return;}console.log("创建文件夹成功");
});
2.8.2 读取

使用 readdir(path[, options], callback) 读取文件夹有哪些文件,path 文件夹路径,options 可选配置,callback 回调函数。readdirSync(path[, options]) 同步读取。

const fs = require("fs");
fs.readdir("./demo", (err, data) => {if (err) {console.log("读取文件夹失败");return;}console.log("读取文件夹成功", data); // 读取文件夹成功 [ 'assets' ]
});

读取文件夹下所有文件,需要添加配置 { recursive: true }

const fs = require("fs");
fs.readdir("./demo", { recursive: true }, (err, data) => {if (err) {console.log("读取文件夹失败");return;}console.log("读取文件夹成功", data); // 我没有读取成功,可能 node 版本需要22以上
});
2.8.3 删除

使用 fs.rmdir(path[, options], callbck) 删除文件,path 文件路径,options 可选配置,callback 回调函数。rmdirSync(path[, options]) 同步删除。

注意:删除的文件夹下不能有文件。

const fs = require("fs");fs.rmdir("./demo", (err) => {if (err) {console.log("删除失败");return;}console.log("删除成功");
});

递归删除,删除文件夹包括文件夹所有文件。需要配置 { recursive: true }

const fs = require("fs");fs.rmdir("./demo", { recursive: true }, (err) => {if (err) {console.log("删除失败");return;}console.log("删除成功");
});

2.9 查看资源状态

使用 fs.stat(path[, options], callbck) 查看资源详细信息,path 文件路径,options 可选配置,callback 回调函数。查看成功后回调函数返回的 data 有两个方法,可以判断查看文件的类型,data.isFile(),data.isDirectory()。 rmdirSync(path[, options]) 同步查看。

const fs = require("fs");fs.stat("./demo.mp4", (err, data) => {if (err) {console.log("查看失败");return;}console.log("查看成功", data);//  查看成功 Stats {//   dev: 3603322110,//   mode: 33206,//   nlink: 1,//   uid: 0,//   gid: 0,//   rdev: 0,//   blksize: 4096,//   ino: 844424930382849,//   size: 101030174,//   blocks: 197328,//   atimeMs: 1718876089782.0842,//   mtimeMs: 1718876089782.0842,//   ctimeMs: 1718876089782.0842,//   birthtimeMs: 1718876088481.4133,//   atime: 2024-06-20T09:34:49.782Z,//   mtime: 2024-06-20T09:34:49.782Z,//   ctime: 2024-06-20T09:34:49.782Z,//   birthtime: 2024-06-20T09:34:48.481Z// }console.log(data.isFile()); // trueconsole.log(data.isDirectory()); // false
});

3. path 模块

path 模块是用来操作路径的。

3.1 拼接绝对路径

使用 path.resolve(path[, path]...) 拼接路径,path 文件路径,使用 \_\_dirname 拿到当前文件所在的目录,然后拼接第二个参数,第二个参数需要是相对路径,输出结果为\拼接的路径。

const path = require("path");
path.resolve(__dirname, "./demo.mp4");
// 等于
path.resolve(__dirname, "demo.mp4");

3.2 获取操作系统分隔符

使用 path.sep 获取操作符,不同操作系统分隔符不一样,windows \,Linux /

const path = require("path");
path.sep; // \

3.3 解析路径

使用 path.parse(path) 解析路径,path 文件路径。可以解析文件所在盘符 root、所在文件夹 dir、文件全名 base、拓展符 ext、文件名 name

const path = require("path");
path.parse(__filename);
// {
//   root: 'D:\\',
//   dir: 'D:\\xxx\\xxx\\xxx\\xxx\\dist',
//   base: 'index.js',
//   ext: '.js',
//   name: 'index'
// }

3.4 获取路径基础名称

使用 path.basename(path) 获取文件名,path 文件路径。

const path = require("path");
console.log(__filename); // D:\xxx\xxx\xxx\xxx\dist\index.js
console.log(path.basename(__filename)); // index.js

3.5 获取路径目录名

使用 path.dirname(path) 获取文件名,path 文件路径。

const path = require("path");
console.log(__filename); // D:\git项目\notes\docs\每日博客\dist\index.js
console.log(path.dirname(__filename)); // D:\git项目\notes\docs\每日博客\dist

3.6 获取路径拓展名

使用 path.extname(path) 获取文件名,path 文件路径。

const path = require("path");
console.log(__filename); // D:\git项目\notes\docs\每日博客\dist\index.js
console.log(path.extname(__filename)); // .js

4. http 模块

4.1 创建服务对象

使用 http.createServer(callbck) 创建服务对象 server,callback 回调函数。

callback(res, req) 接受两个参数,res 请求报文、req 响应报文。

res.end(content):设置响应内容。

res.setHeader(key, value):设置响应头

使用 server.listen(port, callback) 监听端口,启动服务。当服务启动成功执行 listen 的回调。启动成功后 8080 端口就被我们的服务给占了,以后有程序访问我们电脑 8080 端口,我们创建服务的回调函数就会执行。

当我们的服务接受到 http 请求时,执行回调函数。浏览器可以向我们的服务发送 http 请求。

HTTP 协议默认端口 80,HTTPS 协议默认端口 443。

const http = require("http");
const server = http.createServer((req, res) => {res.end("hello man");
});
server.listen(80, () => {console.log("服务启动成功:", "http://127.0.0.1:80");
});

4.2 获取请求报文

获取请求报文数据。

req.method:请求方法

req.url:请求路径,只包含路径和查询条件

req.headers:请求头

req.httpVersion:请求版本

req.on('data', (chunk) => {}):流式获取请求体

req.on('end', () => {}):请求体获取完成

为什么需要获取到请求报文勒?因为我们需要正确返回请求的数据。

const http = require("http");const server = http.createServer((req, res) => {console.log(req);console.log(req.method); // GETconsole.log(req.httpVersion); // 1.1console.log(req.url); // /console.log(req.headers);req.on("data", (chunk) => {console.log(chunk);});req.on("end", () => {console.log("获取完毕");});res.end("ok");
});server.listen(80, () => {console.log("服务启动成功:", "http://127.0.0.1:80");
});

4.3 获取请求体

使用 new URL(req.url, 'http://127.0.0.1') 拿到请求报文路径,pathname 输出路径,searchParams.get(key) 查询字符串

// 访问 127.0.0.1/login?username='yqcoder'&password=111
const http = require("http");const server = http.createServer((req, res) => {const url = new URL(req.url, "http://127.0.0.1");console.log(url.pathname); // /loginconsole.log(url.searchParams.get("username")); // yqcoderres.end("hello");
});server.listen(80, () => {console.log("服务启动成功:", "http://127.0.0.1:80");
});

4.4 设置响应报文

状态码 statusCode、状态描述 statusMessage、响应头 setHeader、响应体 write()、end(),一般我们在 write 里设置了响应体,就不会在 end 里传值了。

const http = require("http");const server = http.createServer((req, res) => {const url = new URL(req.url, "http://127.0.0.1");res.statusCode = 200;res.statusMessage = "成功";res.setHeader("Content-type", "text/html;charset=utf-8");res.write("name");res.end("你好");
});server.listen(80, () => {console.log("服务启动成功:", "http://127.0.0.1:80");
});

4.5 设置响应体

const http = require("http");
const fs = require("fs");const server = http.createServer((req, res) => {res.setHeader("content-type", "text/html;charset=utf-8");const html = fs.readFileSync(`${__dirname}/index.html`);res.end(html);
});server.listen(80, () => {console.log("服务启动成功:", "http://127.0.0.1:80");
});

4.6 网页资源加载基本过程

输入网址按回车后,服务器首先返回 html 资源,然后根据 html 中的外部链接,继续返回相应的静态资源。静态文件一般包括 js、css、图片、视频等。

静态资源请求的路径,是启动服务文件所在路径的相对路径。举个例子,html 文件外链了 css,js,png,mp4 等资源。启动服务获取这些外联资源。

搭建一个静态资源服务

const http = require("http");
const fs = require("fs");const server = http.createServer((req, res) => {res.setHeader("Content-type", "text/html;charset=utf-8");const { pathname } = new URL(req.url, "http://127.0.0.1");let filename = __dirname + pathname;fs.readFile(filename, (err, data) => {if (err) {res.statusCode = 500;res.end("文件读取失败");return;}res.end(data);});
});server.listen(80, () => {console.log(`服务启动成功:http://127.0.0.1`);
});

4.7 静态资源目录和网站根目录

静态资源存在的文件夹称之为静态资源目录也被称为网站根目录。

5. url 模块

写接口时,浏览器的请求路径和查询字符串是很重要的。所以需要使用 url 模块去提取我们所需要的路径信息。

5.1 解析路径

使用 url.parse(path[, boolean]) 解析请求路径,path 请求路径,boolean 查询字符串是否以对象展示。url.parse(path) 返回一个路径对象 result。通过 result.pathname 获取路径。result.query 获取查询字符串。

// 浏览器访问 http://127.0.0.1/admin/login?username=yqcoder&password=111
const http = require("http");
const url = require("url");const server = http.createServer((req, res) => {let result = url.parse(req.url, true);console.log(result);// Url {//   protocol: null,//   slashes: null,//   auth: null,//   host: null,//   port: null,//   hostname: null,//   hash: null,//   search: '?username=yqcoder&password=111',//   query: [Object: null prototype] { username: 'yqcoder', password: '111' },//   pathname: '/admin/login',//   path: '/admin/login?username=yqcoder&password=111',//   href: '/admin/login?username=yqcoder&password=111'// }res.end("hello man");
});
server.listen(80, () => {console.log("服务启动成功:", "http://127.0.0.1:80");
});

5.2 网页 URL 中的绝对路径

绝对路径可靠性强,而且容易理解,直接向目标资源发送请求,多用于网站的外链。

绝对路径有三种形式:

1. http://www.baidu.com/demo.png 完全体

2. //www.baidu.com/demo.png 与页面协议拼接

3. /demo.png 与页面协议、域名、端口拼接。

我们项目中静态资源多用于第三种形式,这样部署到不同的域名,也可以直接访问到。

5.3 网页 URL 中的相对路径

相对于当前文件夹的路径。需要与当前页面 URL 进行拼接。

相对路径有三种形式:

1. ./css/index.css 等于 css/index.css

2. ../js/index.js

3. ../../assets/demo.png

5.4 网页中 URL 使用场景

a 标签 href、link 标签 href、script 标签 src、img 标签的 src、video audio 标签的 src、form 标签的 action、AJAX 请求的 URL 等。

5.5 mime 类型

用来表示文档、文件、或字节流的性质和格式。格式 [type]/[subType]。

HTTP 服务设置响应头 Content-Type 来声明响应体的 MIME 类型。MIME 类型有:

html: "text/html";
css: "text/css";
js: "text/javescript";
png: "image/png";
jpg: "image/jpg";
gif: "image/gif";
mp4: "video/mp4";
mp3: "audio/mpeg";
json: "application/json";

对未知资源使用 application/octet-stream 类型。浏览器遇到该类型的响应时,会对响应体内容进行独立储存,也就是我们常见的下载效果。

5.6 解决乱码问题

中文乱码时,通过设置响应头 Content-Type: 'mime 类型;charset=utf-8'。

5.7 GET 和 POST 区别

GET 请求情况:地址栏输入 url、a 链接、link、script、video、audio、img、form 标签 method 为 get、ajax 中的 get

POST 请求情况:form 标签 post、AJAX 的 post 请求

主要区别:

作用:GET用于获取数据、POST 用于提交数据
参数位置:GET请求是一般将参数缀到 URL 之后、POST 请求一般是将参数放到请求体中
安全性:POST相较于GET安全一些。
大小限制:GET 传参一般是2K、POST 传参没限制

四. Express 框架

express 是一个封装好的开发框架,封装了很多功能,便于我们开发 WEB 应用。

1. express 初体验

安装依赖

npm install express

构建 server.js

const express = require("express");
const app = express();app.get("/home", (req, res) => {res.end("hello express");
});app.listen(3000, () => {console.log("服务启动成功.....");
});

2. 路由介绍

路由确定了客户端对特定端点的请求,路由构成 app.<method>(path, callback),请求方法、路径、回调函数。

app.get("/home", (req, res) => {res.end("hello");
});

3. 路由的使用

匹配 get 请求

app.get("/info", (req, res) => {});

匹配 post 请求

app.post("/login", (req, res) => {});

匹配 get/post 请求。

app.all("/login", (req, res) => {});

匹配所有请求,一般用于响应 404 页面

app.all("*", (req, res) => {});

4. 获取请求报文

express 封装了一些 API 快速获取请求报文,req.path 路径req.query 请求参数req.ip 客户端 ip

// 例子 /login?username=yqcoder&password=111
app.post("/login", (req, res) => {req.path; // /loginreq.query; // { username: 'yqcoder', password: '111' }req.ip; // 127.0.0.1
});

5. 获取路由参数

类似京东详情页 10086.html、10011.html 等,我们使用占位符 id 去匹配这种客户端请求。通过 req.params.id 拿到。

app.get("/:id.html", (req, res) => {// 获取 URL 路由参数req.params.id; //res.end("成功");
});

6. 响应设置

express 封装了一些 API 快速设置响应,并且可以链式设置res.status 设置状态,res.set 设置响应头,res.send 设置响应体

app.get("/login", (req, res) => {res.status(200).set("abc", "123").send("这是OK");
});

7. 其他响应

可以对 http 请求做出其他响应。redirect 重定向,download 下载响应,json 响应 json,sendFile 响应文件内容

app.get("/login", (req, res) => {// 重定向;res.rediect("http://www.baidu.com");// 下载响应res.download(__dirname + "/demo.mp4");// JSON 响应res.json({name: "yqcoder",slogan: "yyyyy",});// 响应文件内容res.sendFile(__dirname + "/test.html");
});

8. 中间件

8.1 什么是中间件

中间件本质是一个回调函数,可以像路由回调一样访问请求对象(req)、响应对象(res)

8.2 中间件的作用

封装公共操作,简化代码

8.3 中间件的类型

中间件类型分为:全局中间件、路由中间件、静态资源中间件

全局中间件:对所有请求做处理

function recordMiddleware(req, res, next) {let { ip, url } = req;console.log(ip, url);next();
}app.use(recordMiddleware);

路由中间件:只对特定路由做处理

function checkCodeMiddleware(req, res, next) {if (req.query.code === "521") {next();} else {res.send("无权限");}
}app.get("/menu", checkCodeMiddleware, (req, res) => {res.send("后台首页");
});

静态资源中间件:express 内置的中间件,设置静态资源请求的路径。

// dirname + '/public' 这是静态资源文件夹路径
app.use(express.static(__dirname + "/public"));

静态资源中间件注意事项:

1. index.html 为默认打开资源。

2. 静态资源与路由同时匹配,谁先匹配谁就响应。

3. 路由响应动态资源,静态资源中间件响应静态资源。

to be continue....

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/859634.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

力扣-滑动窗口

文章目录 滑动窗口题目1-无重复字符的最长子串题目2-找到字符串中所有字母异位词 滑动窗口 滑动窗口是一种常用的算法技巧&#xff0c;适用于需要在一个数组或字符串中找出满足特定条件的连续子数组或子字符串的问题。它通过维护一个窗口范围来减少重复计算&#xff0c;从而优…

计算机硬件的组成与功能详解

目录 1. 组成要素 2. 运算器 运算器的组成 运算器的运作方式 3. 存储器 1. 随机存取存储器&#xff08;RAM, Random Access Memory&#xff09; 2. 只读存储器&#xff08;ROM, Read-Only Memory&#xff09; 3. 缓存&#xff08;Cache&#xff09; 4. 辅助存储器&…

零基础STM32单片机编程入门(一)初识STM32单片机

文章目录 一.概要二.单片机型号命名规则三.STM32F103系统架构四.STM32F103C8T6单片机启动流程五.STM32F103C8T6单片机主要外设资源六.编程过程中芯片数据手册的作用1.单片机外设资源情况2.STM32单片机内部框图3.STM32单片机管脚图4.STM32单片机每个管脚可配功能5.单片机功耗数据…

【EF Core】两种使用模式(Code First、Database First)

文章目录 Code FirstDatabase FirstCode First 示例安装 Entity Framework Core创建模型创建数据库创建、读取、更新和删除 Database First 示例1. 使用Scaffold-DbContext命令生成代码&#xff08;通常在Package Manager Console中执行&#xff09;2. 生成的代码类似于Code Fi…

金融行业专题|某头部期货基于 K8s 原生存储构建自服务数据库云平台

为了进一步提升资源交付效率&#xff0c;不少用户都将数据库应用从物理环境迁移到容器环境。而对于 Kubernetes 部署环境&#xff0c;用户不仅需要考虑数据库在性能方面的需求&#xff0c;还要为数据存储提供更安全、可靠的高可用保障。 近期&#xff0c;某头部期货机构基于 S…

新手摄影技巧总结

拍摄好看的照片不仅仅依赖于相机的硬件配置&#xff0c;还需要掌握一些基本的摄影技巧和相机设置。以下是一些建议&#xff0c;帮助你利用佳能EOS R62拍出更好看的照片&#xff1a; 1. 了解相机设置 模式选择&#xff1a;学习使用不同的拍摄模式&#xff08;如光圈优先、快门…

PaddleOCR C++源码编译以及demo测试

Windows10下使用PaddleOCRc 1.所需要的环境 PaddleOCR 源码文件&#xff1a;https://gitee.com/paddlepaddle/PaddleOCR &#xff08;本文选择2.6https://github.com/PaddlePaddle/PaddleOCR/archive/refs/tags/v2.6.0.zip&#xff09; opencv库&#xff1a;https://opencv…

PyTorch的环境配置和安装

PyTorch环境配置及安装 初步机器学习&#xff0c;这里记录下一些学习经过&#xff0c;之后以便于自己查看&#xff0c;同时欢迎各位大佬点评&#xff0c;本节是机器计算的一个包的安装和简单验证。 安装、使用环境 Windows环境下&#xff1a;CUDA官网使用IDM下载就很快乐&am…

LeetCode11. 盛最多水的容器题解

LeetCode11. 盛最多水的容器题解 题目链接&#xff1a; https://leetcode.cn/problems/container-with-most-water 示例 思路 暴力解法 定住一个柱子不动&#xff0c;然后用其他柱子与其围住面积&#xff0c;取最大值。 代码如下&#xff1a; public int maxArea1(int[]…

flex布局学习笔记(flex布局教程)

前端笔试⾯试经常会问到:不定宽⾼如何⽔平垂直居中。最简单的实现⽅法就是flex布局,⽗元素加上如下代码即 可: display: flex; justify-content: center; align-items :center; 。下⾯详细介绍下flex布局吧。 2009年,W3C提出了 Flex布局,可以简便⼂完整⼂响应式地实现各种…

AI儿童绘本创作

之前分享过AI儿童绘画的项目&#xff0c;但是主要问题是角色一致要花费很长的时间&#xff01; 今天发现了这款&#xff0c;非常奈斯&#xff01; 只需输入故事主题、风格、模板&#xff0c;软件就会自动创作故事内容&#xff0c;自动生成插画配图&#xff0c;自动根据模板生…

mysql问题Illegal mix of collations (utf8mb4_unicode_ci,IMPLICIT)

show table status from oa SELECT TABLE_SCHEMA 数据库, TABLE_NAME 表, COLUMN_NAME 字段, CHARACTER_SET_NAME 原字符集, COLLATION_NAME 原排序规则, CONCAT( ALTER TABLE , TABLE_SCHEMA, ., TABLE_NAME, MODIFY COLUMN , COLUMN_NAME, , COLUMN_TYPE, CH…

Spring Bean自动装配:深入解析与实战应用

何为自动装配 在使用Spring框架配置bean时&#xff0c;我们通常需要为bean的属性设置值。如果不手动设置这些值&#xff0c;它们通常会被初始化为默认值&#xff08;对于对象类型通常是null&#xff0c;对于基本类型如int则是0&#xff0c;boolean是false等&#xff09;。自动…

hevc和H.264格式的区别

HEVC&#xff08;High Efficiency Video Coding&#xff09;和H.264&#xff08;也称为Advanced Video Coding&#xff0c;AVC&#xff09;都是视频压缩标准&#xff0c;但它们之间存在一些显著的区别&#xff0c;主要集中在压缩效率、资源需求和兼容性方面。 压缩效率 HEVC&am…

python2进制移位>>><<<

1.右移动 对于正数&#xff0c;右边舍弃&#xff0c;左边补零 对于负数&#xff0c;右边舍弃&#xff0c;左边补1 2.左移动<< 运算符将一个数的二 进制位向左移动指定的位数&#xff0c;右边用 0 填充。例如&#xff0c;将数字 5&#xff08;二进制表示为 101&#xf…

Gitee的基本使用方法和基本用法

Gitee是一个类似于GitHub的代码托管和协作平台&#xff0c;用于管理、分享和合作开发项目。下面是Gitee的使用方法和基本用法&#xff1a; 注册和登录&#xff1a; 首先&#xff0c;你需要在Gitee上注册一个账号。注册完成后&#xff0c;使用你的账号登录。 创建仓库&#xff…

(超详细)YOLOV7改进-Soft-NMS(支持多种IoU变种选择)

1.在until/general.py文件最后加上下面代码 2.在general.py里面找到这代码&#xff0c;修改这两个地方 3.之后直接运行即可

网页设计软件Bootstrap Studio6.7.1

Bootstrap Studio是一个适用于Windows的程序,允许您使用流行的fre***orca Bootstrap创建和原型网站。您可以将现成的组件拖动到工作区并直观地自定义它们。该程序生成干净和语义的PDF、CSS和JS代码,所有Web浏览器都支持这些代码。 Bootstrap Studio有一个漂亮而强大的界面,它…

DataWorks重磅推出全新资源组2.0,实现低成本灵活付费和动态平滑扩缩容

背景简介 DataWorks资源组为DataWorks上的各个功能模块提供计算资源&#xff0c;属于付费服务。 资源组属于DataWorks的基础组件&#xff0c;是客户正常使用DataWorks的前提。 资源组直接影响到相关功能是否正常运行&#xff0c;以及运行的效率和稳定性。 此前DataWorks资源…

ImportError: attempted relative import beyond top-level package报错,解决方法

1.如下图所示&#xff0c;在conftest.py页面采用相对路径去引用包&#xff0c;运行提示报错信息 目录结构: D:. ├─common ├─config ├─image ├─logFile ├─page │ └─basePage.py └─test_cases└─conftest.py从目录结构中我们可以看到conftest.py文件和basePage…