Nodejs 基础
Node.js
- Node.js是一个开源的,跨平台的 JavaScript 运行环境。
- Node.js是一个事件驱动I/O服务端JavaScript环境,基于Google的V8引擎,V8引擎执行Javascript的速度非常快,性能非常好。
- Reference: 尚硅谷-NodeJS
- Website: Node.js
1 NodeJS入门
1.1 简介
Node.js® is an open-source, cross-platform JavaScript runtime environment.
- Node.js是一个开源的,跨平台的 JavaScript 运行环境。
1.2 作用
- 开发服务器应用
- 开发工具类应用
- webpack、Vite、Babel
- 开发桌面端应用
- electron
- VSCode、Figma、Postman
- electron
1.3 安装
官网安装:Node.js
安装验证
- ```sh
$ node -v
$ npm -v # 新版的nodejs已经集成了npm1
2
3
4
5
6
7
8
9
10
11
- ![image-20230726215614615](https://s2.loli.net/2023/09/29/RW79xjSgU4lq2Nm.png)
> - Nodejs是一门计算机语言,运行在系统中的Chrome v8(jvm)引擎中。文件后缀是 `js` ;
> - 运行的命令是:node ( js 是解释型语言,不需要编译,直接执行,所以不像 java 需要 java 和 javac)
- 修改安装位置
```sh
$ npm config set prefix "D:\Coding\Java\nodejs\npm_global"
$ npm config set cache "D:\Coding\Java\nodejs\npm_cache"
- ```sh
配置环境变量
- NODE_PATH = D:\Coding\Java\nodejs\npm_global
npm root -g / npm config ls
1.4 简单使用
1.4.1 命令行使用
Node.js
中不能使用BOM和DOM的API。
1.4.2 API注意点
Web JS语法
NodeJS API
console
和定时器
是通用的Node.js
中的顶级对象为global
,也可以用globalThis
访问顶级对象
1.5 Buffer
1.5.1 概念
- Buffer是一个类似于数组的对象,用于表示固定长度的字节序列
- Buffer:本质是一段内存空间,专门用来处理二进制数据。
1.5.2 特点
- Buffer:大小固定且无法调整
- Buffer性能较好,可以直接对计算机内存进行操作
- 每个元素的大小为1字节(byte)
1.5.3 使用
1)创建
Buffer.alloc
1
2// 创建了一个长度为 10 字节的 Buffer,相当于申请了 10 字节的内存空间,每个字节的值为 0
let buf_1 = Buffer.alloc(10); // 结果为 <Buffer 00 00 00 00 00 00 00 00 00 00>Buffer.allocUnsafe
1
2
3
4// 创建了一个长度为 10 字节的 Buffer
// buffer 中可能存在旧的数据(内存空间是可以复用的), 可能会影响执行结果,所以叫unsafe
let buf_2 = Buffer.allocUnsafe(10);
// 用这种方式创建速度比alloc快很多。Buffer.from
1
2
3
4// 通过字符串创建 Buffer
let buf_3 = Buffer.from('hello');
// 通过数组创建 Buffer
let buf_4 = Buffer.from([105, 108, 111, 118, 101, 121, 111, 117]);
2)Buffer 与字符串的转化
我们可以借助
toString
方法将Buffer
转为字符串。toString
默认是按照utf-8
编码方式进行转换的。1
2let buf_4 = Buffer.from([105, 108, 111, 118, 101, 121, 111, 117]);
console.log(buf_4.toString())
3)Buffer的读写
Buffer
可以直接通过[]
的方式对数据进行处理。1
2
3
4
5
6//读取
console.log(buf_3[1]);
//修改
buf_3[1] = 97;
//查看字符串结果
console.log(buf_3.toString());
- 如果修改的数值超过 255 ,则超过 8 位数据会被舍弃。
- 一个utf-8 的字符一般占 3 个字节。
2 fs模块
- fs:
file system
,文件系统。- fs模块可以实现与硬盘的交互,例如文件的创建、删除、重命名、移动、内容的写入读取以及文件夹的相关操作
2.1 文件写入
方法 | 说明 |
---|---|
writeFile |
异步写入 |
writeFileSync |
同步写入 |
appendFile |
异步追加 |
appendFileSync |
同步追加 |
createWriteStream |
流式写入= |
2.1.1 writeFile 异步写入
语法:
fs.writeFile(file,data[,options],callback)
参数说明:
file
文件名data
待写入的数据options
选项设置(可选){flag: 'a'}
: 追加写入
callback
写入回调
返回值:
undefined
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17// 1. 导入 fs 模块
const fs = require("fs");
// 2. 调用 fs.writeFile() 方法写入文件
// fs.writeFile(file, data[, options], callback)
// fiel: 要写入的文件路径
// data: 要写入的数据
// options: 选项,可以对写入进行一些设置
// callback: 回调函数,写入完成后执行
fs.writeFile("./test.txt", "Hello world!", (err) => {
if (err) {
console.log("写入失败!");
} else {
console.log("写入成功!");
}
});
![image-20230929161007482](https://s2.loli.net/2023/09/29/HXqiYjKrkOCsPox.png)
异步:如下,
程序执行完毕!
与文件写入同时执行,谁先运行完谁先输出1
2
3
4
5
6
7
8
9
10
11
12
13
14
15const fs = require("fs");
fs.writeFile("./test.txt", "Hello world!", (err) => {
if (err) {
console.log("写入失败!");
} else {
console.log("写入成功!");
}
});
console.log("程序执行完毕1!");
setTimeout(() => {
console.log("程序执行完毕2!");
}, 2000);
![image-20230929161304653](https://s2.loli.net/2023/09/29/yeR51cZwBrfuMYq.png)
2.1.2 writeFileSync 同步写入
语法:
fs.writeFileSync(file, data[, options])
参数与 fs.writeFile 大体一致,只是==没有 callback 参数==
返回值:
undefined
同步
:就是一条道,我走完你再走,从上到下顺序执行。1
2
3
4
5
6
7
8
9
10const fs = require("fs");
try {
fs.writeFileSync("test.txt", "Hello World!");
console.log("文件写入成功!");
} catch (e) {
console.log(e);
}
console.log("后续代码...");
![image-20230929161545836](https://s2.loli.net/2023/09/29/pShry4MAsj8RlVg.png)
2.1.3 appendFile / appendFileSync 追加写入
appendFile 作用是在文件尾部追加内容,appendFile 语法与 writeFile 语法完全相同。
语法:
fs.appendFile(file, data[, options], callback)
fs.appendFileSync(file, data[, options])
返回值: 二者都为
undefined
1
2
3
4
5
6
7
8
9
10
11
12const fs = require("fs");
fs.appendFile("./test.txt", "111", function (err) {
if (err) {
console.log("写入成功!");
return;
} else {
console.log("写入失败!");
}
});
console.log("后续代码...");
2.1.4 createWriteStream 流式写入
- 语法:
fs.createWriteStream(path[, options])
- 参数说明:
path
:文件路径options
:选项配置( 可选 ){flags: 'a'}
追加写入
返回值:
Object
const fs = require("fs"); const ws = fs.createWriteStream("./test.txt", { flags: 'a'}); if (ws.writable) { ws.write("11111111111111"); if (ws.write("写入文件内容")) { console.log("写入成功!"); } } ws.close();
![image-20230929162425681](https://s2.loli.net/2023/09/29/Q7hWp6VxHwbvq2O.png)
程序打开一个文件是需要消耗资源的 ,流式写入可以减少打开关闭文件的次数。
流式写入方式适用于大文件写入或者频繁写入的场景, writeFile 适合于写入频率较低的场景。
2.2 文件读取
方法 | 说明 |
---|---|
readFile |
异步读取 |
readFileSync |
同步读取 |
createReadStream |
流式读取 |
2.2.1 readFile 异步读取
语法:
fs.readFile(path[, options], callback)
参数说明:
path
:文件路径options
:选项配置callback
:回调函数
返回值:
undefined
1
2
3
4
5
6
7
8
9const fs = require("fs");
fs.readFile("./test.txt", "utf8", (err, data) => {
if (err) {
console.log("读取文件失败!");
return;
}
console.log(data);
});
2.2.2 readFileSync 同步读取
语法:
fs.readFileSync(path[, options])
参数说明:
path
:文件路径options
:选项配置
返回值:
string | Buffer
1
2
3
4const fs = require("fs");
const data = fs.readFileSync("./test.txt", "utf8");
console.log(data.toString());
2.2.3 createReadStream 流式读取
语法:
fs.createReadStream(path[, options])
参数说明:
path
:文件路径options
:选项配置( 可选 )
返回值:
Object
1
2
3
4
5
6
7
8
9
10
11const fs = require('fs');
const rs = fs.createReadStream("./test.txt");
rs.on('data', chunk => {
console.log(chunk.length); // 65536 字节 => 64KB,每次读取64KB的数据
});
rs.on('end', () => {
console.log('读取完成');
});
2.2.4 fs文件练习——文件复制
1 | const fs = require('fs'); |
- 查看内存占用量
1 | const fs = require("fs"); |
- 流式处理所占用的内存比直接读取更大是因为流式处理需要在处理数据时逐行或逐块读取数据,并将其存储在内存中以进行后续处理。这意味着在读取数据时,需要维护一个缓冲区来存储数据,因此在处理大量数据时,内存的使用率可能比直接读取更高。
- 另外,流式处理还需要在处理后及时释放内存,否则可能会导致内存泄漏和程序崩溃等问题。因此,在设计流式处理程序时需要特别注意内存的使用和释放问题,以确保程序的稳定性和可靠性。
2.3 文件移动与重命名
方法 | 说明 |
---|---|
rename |
异步重命名/移动 |
renameSync |
同步重命名/移动 |
- 在 Node.js 中,我们可以使用
rename
或renameSync
来移动或重命名 文件或文件夹 - 语法:
fs.rename(oldPath, newPath, callback)
fs.renameSync(oldPath, newPath)
参数说明:
oldPath
:文件当前的路径newPath
:文件新的路径callback
:操作后的回调
1 | const fs = require("fs"); |
2.4 文件删除
方法 | 说明 |
---|---|
unlink / rm |
异步删除 |
unlinkSync / rmSync |
同步删除 |
- 在 Node.js 中,我们可以使用
unlink
或unlinkSync
或rm
或rmSync
来删除文件。 - 语法:
fs.unlink(path, callback)
fs.unlinkSync(path)
fs.rm(path, callback)
fs.rmSync(path)
参数说明:
path
:文件路径callback
:操作后的回调
2.5 文件夹操作
方法 | 说明 |
---|---|
mkdir / mkdirSync |
创建文件夹 |
readdir / readdirSync |
读取文件夹 |
rmdir / rmdirSync |
删除文件夹 |
1)mkdir 创建文件夹
- 在 Node.js 中,我们可以使用
mkdir
或mkdirSync
来创建文件夹 - 语法:
fs.mkdir(path[, options], callback)
fs.mkdirSync(path[, options])
- 参数说明:
path
:文件夹路径options
:选项配置( 可选 )recursive: true
递归创建
callback
:操作后的回调
1 | const fs = require("fs"); |
2)readdir 读取文件夹
- 在 Node.js 中,我们可以使用
readdir
或readdirSyn
来创建文件夹 - 语法:
fs.readdir(path[, options], callback)
fs.readdirSync(path[, options])
- 参数说明:
path
:文件夹路径options
:选项配置( 可选 )callback
:操作后的回调
1 | fs.readdir("./", (err, files) => { |
3)rmdir 删除文件夹
- 在 Node.js 中,我们可以使用
rmdir
或rmdirSync
来创建文件夹 - 语法:
fs.rmdir(path[, options], callback)
fs.rmdirSync(path[, options])
- 参数说明:
path
:文件夹路径options
:选项配置( 可选 )callback
:操作后的回调
1 | fs.rmdir("./test", { recursive: true }, (err) => { |
4)fs文件练习——批量重命名
批量重命名,把1、2等变为01、02等,可应用于防止网盘文件错位。
1
2
3
4
5
6
7
8
9
10const fs = require("fs");
const files = fs.readdirSync('./test');
files.forEach(item => {
let [name, txt] = item.split('.');
if (Number(name) < 10) {
fs.rename(`./test/${item}`, `./test/0${name}.${txt}`, (err) => { });
}
})
2.6 查看资源状态
在 Node.js 中,我们可以使用
stat
或statSync
来创建文件夹语法:
fs.stat(path[, options], callback)
fs.statSync(path[, options])
参数说明:
path
:文件夹路径options
:选项配置( 可选 )callback
:操作后的回调
1 | const fs = require("fs"); |
size
文件体积birthtime
创建时间mtime
最后修改时间isFile
检测是否为文件isDirectory
检测是否为文件夹
2.7 dirname全局变量
__dirname
与require
类似,都是 Node.js 环境中的全局变量__dirname
保存着当前文件所在目录的绝对路径 ,可以使用__dirname
与文件名拼接成绝对路径```js
const fs = require(“fs”);let data = fs.readFileSync(__dirname+’/test/01.txt’, ‘utf-8’);
console.log(data);1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# 3 path模块
> path 模块提供了 **操作路径** 的功能
| API | 说明 |
| --------------- | ------------------------ |
| `path.resolve` | 拼接规范的绝对路径 |
| `path.sep` | 获取操作系统的路径分隔符 |
| `path.parse` | 解析路径并返回对象 |
| `path.basename` | 获取路径的基础名称 |
| `path.dirname` | 获取路径的目录名 |
| `path.extname` | 获取路径的扩展名 |
```js
const path = require("path");
//resolve:拼接规范(分隔符统一)的绝对路径
console.log(path.resolve(__dirname, './index.html'));
//可以不写./也表示绝对路径
console.log(path.resolve(__dirname, 'index.html'));
//sep:获取操作系统的路径分隔符
console.log(path.sep); // windows下是\,Linux下是/
//parse解析路径并返回对象
//console.log(__filename); //获取文件的绝对路径
let str = "d:\\Coding\\test\\tempWebToolsNodejs\\3_path模块\\index.html ";
console.log(path.parse(str));
//basename:快速获取文件名
console.log(path.basename(str));
//dirname:获取路径的目录名
console.log(path.dirname(str));
//extname:获取路径的扩展名
console.log(path.extname(str));
4 HTTP协议
4.1 HTTP简介
- HTTP(hypertext transport protocol) - 超文本传输协议
- 是一种基于TCP/P的应用层通信协议
- 这个协议详细规定了浏览器和万维网服务器之间互相通信的规则。
- 主要规定了两个方面的内容
- 客户端:用来向服务器发送数据,可以被称之为请求报文
- 服务端:向客户端返回数据,可以被称之为响应报文
- 报文:可以简单理解为就是一堆字符串
4.2 窥探HTTP报文 - Fiddler
Fiddler是一个http协议调试代理工具,它能够记录并检查所有你的电脑和互联网之间的http通讯,设置断点,查看所有的“进出”Fiddler的数据。
4.2.1 安装
4.2.2 配置
在
Tool
的Options
里 ,找到HTTPS勾选Decrypt..
,然后同意接下来的弹窗。记得点OK!然后重启!!设置
Web Browsers
4.2.3 使用
双击某一条则会显示详细信息
选择
Raw
会显示详细信息
4.3 请求报文的组成
4.3.1 HTTP请求行
请求方法
URL:
Uniform Reaourse Locator
,统一资源定位符,其本身也是一个字符串版本号:
4.3.2 HTTP请求头
格式:
头名:头值
4.3.3 HTTP的请求体
- 请求体内容的格式是非常灵活的,
- (可以是空):GET请求,
- (也可以是字符串,还可以是JSON):POST请求
4.4 响应报文的组成
4.4.1 响应行
4.4.2 响应头和响应体
- 响应体内容的类型是非常灵活的,常见的类型有 HTML、CSS、JS、图片、JSON。
- 相应头请求头这些都是不需要记住的,在MDN可查。
5 http模块
5.1 node.js创建HTTP服务
1 | // 1. 引入http模块 |
http.createServer
的回调函数执行时机:收到HTTP请求时,执行响应内容中文乱码的解决办法:
response.setHeader('content-type','text/html;charset=utf-8');
5.2 获取请求报文
含义 | 语法 | |
---|---|---|
请求方法 | reaquest.method |
|
请求版本 | request.httpVersion |
|
请求路径 | request.url |
只能获取路径以及查询字符串,无法获取 URL 中的域名以及协议的内容 |
URL路径 | require('url').parse(requet.url).pathname |
|
URL查询字符串 | require('url').parse(request.url).query |
|
请求头 | request.headers |
将请求信息转化成一个对象,并将属性名都转化成了小写 |
请求体 | request.on('data', function(chunk){}) request.on('end', function(){}) |
:memo:
如果访问网站的时候,只填写了 IP 地址或者是域名信息,此时请求的路径为
/
关于 favicon.ico:这个请求是属于浏览器自动发送的请求——获取图标
5.2.1 提取报文
1 | const http = require('http'); |
5.2.2 请求体
1 | const http = require("http"); |
借助form表单
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>03_请求体post</title>
</head>
<body>
<form action="http://127.0.0.1:9000/index.html" method="post">
<input type="text" name="username" />
<input type="password" name="password" />
<input type="submit" value="提交" />
</form>
</body>
</html>
![image-20230930155641216](https://s2.loli.net/2023/09/30/5YxnFA8eTyIcGop.png)
5.2.3 URL路径与查询字符串
1)方法一
1 | const http = require("http"); |
2)方法二 ==推荐==
1 | const http = require("http"); |
5.2.4 HTTP请求练习
请求类型(方法) | 请求地址 | 响应体结果 |
---|---|---|
get | /login | 登录页面 |
get | /reg | 注册页面 |
1 | const http = require("http"); |
5.3 设置响应报文
5.3.1 设置HTTP响应报文
语法 | 说明 |
---|---|
response.statusCode |
设置响应状态码 |
response.statusMessage |
设置响应状态描述 |
response.setHeader('header', 'value') |
设置响应头信息 |
response.write('xx') response.end('xxx') |
设置响应体 end只能写一次,write可以写多次 |
1 | const http = require("http"); |
5.3.2 HTTP响应练习
采用html和js分离+导入实现
html
完成表格样式1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
td {
padding: 20px 40px;
}
/* odd even交替实现隔行换色 */
table tr:nth-child(odd) {
background: rgb(202, 219, 255);
}
table tr:nth-child(even) {
background: #fcb;
}
table,
td {
border-collapse: collapse;
}
</style>
</head>
<body>
<table border="1">
<tr>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
</tr>
</table>
<script>
//实现点击换色
//获取所有的 td
let tds = document.querySelectorAll("td");
//遍历
tds.forEach((item) => {
item.onclick = function () {
this.style.background = "yellow";
};
});
</script>
</body>
</html>js
设置响应1
2
3
4
5
6
7
8
9
10
11
12const http = require("http");
const fs = require("fs");
const server = http.createServer((request, response) => {
response.setHeader("content-type", "text/html;charset=utf-8");
let data = fs.readFileSync("./08_http响应练习.html");
response.end(data);
});
server.listen(9000, () => {
console.log("服务器启动成功了, 请访问: http://localhost:9000");
});
5.4 资源加载过程
- 网页资源的加载都是循序渐进的,首先获取 HTML 的内容, 然后解析 HTML 在发送其他资源的请求,如CSS,Javascript,图片等。
5.4.1 静态资源服务
静态资源
是指内容长时间不发生改变的资源 ,例如图片,视频,CSS 文件,JS文件,HTML文件,字体文件等。动态资源
是指内容经常更新的资源 ,例如百度首页,网易首页,京东搜索列表页面等。- HTTP 服务在哪个文件夹中寻找静态资源,那个文件夹就是
静态资源目录
,也称之为网站根目录
。
5.4.2 网页中的URL
绝对路径
绝对路径可靠性强,而且相对容易理解,在项目中运用较多
| 形式 | 特点 |
| ————————————- | —————————————————————————————— |
| http://www.baidu.com/test | 直接向目标资源发送请求,容易理解。网站的外链会用到此形式 |
| //www.baidu.com/test | 与页面UL的协议拼接形成完整URL再发送请求。大型网站用的比较多 |
| /test | 与页面URL的协议、主机名、端口拼接形成完整UL再发送请求。中小型网站==避免更换域名等问题== |
相对路径
相对路径在发送请求时,需要与当前页面UL路径进行计算,得到完整UL后,再发送请求,学习阶段用的较多,例如当前网页url为http://www.test.com/resources/h5.html
| 形式 | 最终的URL |
| ————————— | ————————————————————- |
| ./css/app.css | http://www.test.com/resources/css/app.css |
| js/app.js | http://www.test.com/resources/js/app.js |
| ../img/logo.png | http://www.test.com/img/logo.png |
| ../../mp4/show.mp4 | http://www.test.com/mp4/show.mp4 |
5.4.3 资源类型(mime类型)
媒体类型(通常称为
Multipurpose Internet Mail Extensions
或MIME
类型)是一种标准,用来表示文档、文件或字节流的性质类型。- ```md
mime 类型结果: [type]/[subType]
例如:text/html, text/css, image/jpeg, image/png, application/json1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
- HTTP服务可以设置响应头 `Content-Type` 来表明响应体的MIME类型,浏览器会根据该类型觉得如何处理资源
- 常见文件对应的mime类型
- html `text/html`
- css `text/css`
- js `text/javascript`
- png `image/png`
- jpg `image/jpeg`
- gif `image/gif`
- mp4 `video/mp4`
- mp3 `audio/mpeg`
- json `application/json`
- 对于未知的资源类型,可以选择 `application/octet-stream` 类型,浏览器在遇到该类型的相应时,会对响应体内容进行独立存储,也就是常见的 `下载` 效果
```js
/**
* 创建一个 HTTP 服务,端口为 9000,满足如下需求
* GET /index.html 响应 page/index.html 的文件内容
* GET /css/app.css 响应 page/css/app.css 的文件内容
* GET /images/logo.png 响应 page/images/logo.png 的文件内容
*/
const http = require("http");
const fs = require("fs");
const path = require("path");
let mimes = {
html: "text/html",
css: "text/css",
js: "text/javascript",
png: "image/png",
jpg: "image/jpeg",
gif: "image/gif",
mp4: "video/mp4",
mp3: "audio/mp3",
json: "application/json",
};
const server = http.createServer((request, response) => {
// 获取请求的路径
let { pathname } = new URL(request.url, "http://127.0.0.1");
// 获取文件的后缀名
let filePath = path.join(__dirname, "public", pathname);
// 读取文件
fs.readFile(filePath, (err, data) => {
if (err) {
switch (err.code) {
case "ENOENT":
response.statusCode = 404;
response.end("404, 您请求的资源不存在");
break;
case "EPERM":
response.statusCode = 403;
response.end("403, 您没有权限访问该资源");
default:
response.statusCode = 500;
response.end("500, 服务器内部错误");
}
return;
}
let ext = path.extname(pathname).slice(1);
let type = mimes[ext];
if (type) {
if (type === "text/html") {
response.setHeader("Content-Type", type + ";charset=utf-8");
}
else {
response.setHeader("Content-Type", type);
}
}
else {
response.setHeader("Content-Type", "application/octet-stream");
}
response.end(data);
});
});
server.listen(9000, () => {
console.log("服务器启动成功了, 请访问: http://localhost:9000");
});
- ```md
5.4.4 GET和POST请求场景小结
- GET请求的情况:
- 在地址栏直接输入
url
访问 - 点击
a
链接 link
标签引入css
script
标签引入js
img
标签引入图片form
标签中的method
为get
(不区分大小写)ajax
中的get
请求
- 在地址栏直接输入
- POST请求的情况:
- form标签中的method为post(不区分大小写)
- AJAX的post请求
5.4.5 GET和POST请求的区别
GET
和POST
是 HTTP 协议请求的两种方式。GET
主要用来获取数据,POST
主要用来提交数据GET
带参数请求是将参数缀到 URL 之后,在地址栏中输入 url 访问网站就是 GET 请求,POST
带参数请求是将参数放到请求体中。POST
请求相对GET
安全一些,因为在浏览器中参数会暴露在地址栏。GET
请求大小有限制,一般为 2K,而POST
请求则没有大小限制。
6 Node.js模块化
6.1 模块化简介
- 模块化
- 将一个复杂的程序文件依据一定规则(规范)拆分成多个文件的过程
- 其中拆分出的每个文件就是一个模块,模块的内部数据是私有的,不过模块可以暴露内部数据以便其他模块使用
模块化项目
- 编码时是按照模块一个一个编码的,整个项目就是一个模块化的项目
优点
- 防止命名冲突
- 高复用性
- 高维护性
6.2 模块暴露
6.2.1 基本使用
创建
me.js
1
2
3
4
5
6
7// 声明函数
function hello() {
console.log("hello");
}
// 暴露模块
module.exports = hello;创建
index.js
1
2
3
4
5// 导入模块
const hello = require('./me.js');
// 调用模块
hello(); // hello
6.2.2 暴露数据
模块暴露数据的方式有两种:
module.exports = value
exports.name = value
module.exports
可以暴露任意数据。1
2//me.js
module.exports = 'hello world!';
1
2
3
4
//导入模块
const me = require('./me.js');
//输出 me
console.log(me);
不能使用
exports = value
的形式暴露数据,模块内部 module 与 exports 的隐式关系exports = module.exports = {}
,require 返回的是目标模块中module.exports
的值
6.3 导入文件模块
在模块中使用
require
传入文件路径即可引入文件require
使用的一些注意事项:- 对于自己创建的模块,导入时路径建议写相对路径 ,且不能省略 ./ 和 …/
- js 和 json 文件导入时可以不用写后缀,c/c++编写的 node 扩展文件也可以不写后缀,但是一般用不到
- 如果导入其他类型的文件,会以 js 文件进行处理
- 导入
node.js
内置模块时,直接require
模块的名字即可,无需加 ./ 和 …/
6.4 导入文件夹的情况
- 如果导入的路径是个文件夹,则会
- 首先检测该文件夹下
package.json
文件中 main 属性对应的文件 - 如果存在则导入,反之如果文件不存在会报错。
- 如果 main 属性不存在,或者
package.json
不存在,则会尝试导入文件夹下的index.js
和index.json
,如果还是没找到,就会报错。
- 首先检测该文件夹下
6.5 导入模块的基本流程
require导入自定义模块的基本流程:
- 将相对路径转为绝对路径,定位目标文件。
- 缓存检测。
- 读取目标文件代码。
- 包裹为一个函数并执行(自执行函数)。通过
arguments.callee.toString()
查看自执行函数。 - 缓存模块的值。
- 返回
module.exports
的值。
6.6 CommonJS规范
module.exports
、exports
以及require
这些都是 CommonJS 模块化规范中的内容。- 而 Node.js 是实现了 CommonJS 模块化规范,二者关系有点像 JavaScript 与 ECMAScript
7 包管理工具
7.1 概念介绍
- 包
package
- 代表了一组特定功能的源码集合
- 包管理工具
- 管理包的应用软件,可以对包进行 下载安装、更新、删除、上传 等操作
- 借助包管理工具,可以快速开发项目,提升开发效率
- 前端常用的包管理工具
- npm
- yarn
- cnpm
7.2 npm
指令 | 说明 |
---|---|
npm init | 初始化 |
npm install npm install xxx@x.x.x |
安装模块(指定版本) |
npm uninstall | 卸载模块 |
npm update | 更新模块 |
npm prefix | 查看工作空间 |
npm ls | 查看已安装模块 |
npm outdated | 查看过时的已安装模块 |
npm help | 查看命令的详情帮助 |
npm config | 管理npm的配置路径 |
npm cache | 管理模块的缓存 |
npm root | 查看包的安装路径 |
npm version | 查看模块版本号 |
npm view | 查看模块的注册信息 |
npm adduser | 用户登录 |
npm logout | 退出登录 |
npm publish | 发布模块 |
npm docs | 说明文档 |
npm remove xxx npm remove -g xxx |
移除依赖(全局移除) |
7.2.1 简介
Node Package Manager
- node.js 官方内置的包管理工具
npm -v
7.2.2 基本使用
1)创建
空目录,以此作为工作目录,执行
npm init
1
2
3
4
5
6
7
8
9
10
11{
"name": "test", // 包名
"version": "1.0.0", // 版本号
"description": "学习npm", // 描述
"main": "index.js", // 入口文件
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "bayyy", // 作者
"license": "ISC" // 开源证书
}
:warning:
package name
(包名)不能使用中文、大写,默认值是文件夹的名称,所以文件夹名称也不能使用中文和大写version
(版本号)要求x.x.x
的形式定义,x
必须是数字,默认值为1.0.0
- ISC证书与MIT证书功能上是相同的
package.json
可以手动创建与修改- 使用
npm init -y
或者npm init --yes
急速创建package.json
2)搜索包
- 命令行:
npm search/s xxx
- 网站搜索:npm (npmjs.com)
3)下载
npm install xxx
/npm i xxx
node_modules
: 存放下载的包package-lock.json
: 包的锁文件,锁定包的版本
4)require导入npm包基本流程
1 | const uniq = require("uniq"); |
- 在当前文件夹下
node_modules
中寻找同名的文件夹 - 在上级目录中下的
node_modules
中寻找同名的文件夹,直至找到磁盘根目录
7.2.3 生产与开发
1)环境
- 开发环境是程序员专门用来写代码的环境,一般是指程序员的电脑,开发环境的项目只能程序员自己访问
- 生产环境是项目代码正式运行的环境,一般是指正式的服务器电脑,生产环境的项目每个客户都可以访问
2)依赖
- 我们可以在安装时设置选项来区分依赖的类型,目前分为两类:
类型 | 命令 | 补充 |
---|---|---|
生产依赖 | npm i -S uniq npm i--save uniq |
-S 等效于-save ,-S 是默认选项包信息保存在 package.json 中dependencies 属性 |
开发依赖 | npm i -D less npm i --save-dev less |
-D 等效于-save-dev 包信息保存在 package.json 中devDependencies 属性 |
7.2.4 全局安装
npm install -g xxx
:key:
- 全局安装的命令不受工作目录位置影响
- 可以通过
npm root -g
查看全局安装包的位置- 只有全局类工具才适合全局安装,可以通过查看包的官方文档来确定安装方式
7.2.5 修改windows执行策略
- 管理员身份打开 powershell 命令行
- 键入命令
set-ExecutionPolicy remoteSigned
7.2.5 配置别名
配置
package.json
中的scripts
属性1
2
3
4"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node index.js"
}start
别名比较特殊,可以直接运行npm start
npm start
一般作为项目启动命令npm run
有自动向上级目录查找的特性,与require
函数类似- 对于模式的项目,可以查看
scripts
属性来参考项目的一些操作
7.2.6 更换镜像源
1)直接配置
淘宝 NPM 镜像是一个完整 npmjs.com 镜像,同步频率目前为 10分钟一次,以保证尽量与官方服务同步。
1 | $ npm config set registry https://registry.npm.taobao.org |
2)工具配置 nrm
- 使用
nrm
配置npm
的镜像地址npm registry manager
安装
npm i -g nrm
修改镜像
nrm use taobao
列举所有镜像
nrm list/ls
检查是否配置成功
npm config list
:key: 补充说明
- ==建议==使用第二种方式进行镜像配置,后续修改较为方便
npm
使用率高于cnpm
7.3 cnpm
7.3.1 cnpm简介
- cnpm是一个淘宝构建的
npmjs.com
的完整镜像,也称为 淘宝镜像 - cnpm服务部署在国内阿里云服务器上,可以提高包的下载速度
- 官方也提供了一个全局工具包
cnpm
,操作命令与npm大体相同
7.3.2 cnpm安装
npm install -g cnpm --registry=https://registry.npmmirror.com
7.4 yarn
7.4.1 yarn 简介
yarn
是由Facebook在20l6年推出的新的Javascript包管理工具,官方网址:Yarn | en、Yarn | cn- 特点 (官方宣称的一些特点)
- 速度超快:yarn缓存了每个下载过的包,所以再次使用时无需重复下载。同时利用并行下载以最大化资源利用率,因此安装速度更快
- 超级安全:在执行代码之前,yarn会通过算法校验每个安装包的完整性
- 超级可靠:使用详细、简洁的锁文件格式和明确的安装算法,yarn能够保证在不同系统上无差异的工作
7.4.2 yarn 使用
npm i -g yarn
- npm 的锁文件为 -
package-lock.json
- yarn 的锁文件为 -
yarn.lock
- npm 的锁文件为 -
7.4.3 yarn常用命令
功能 | 命令 |
---|---|
初始化 | yarn init / yarn init -y |
安装包 | yarn add xxx 生产依赖yarn add xxx -dev 开发依赖yarn global addd xxx 全局安装 |
删除包 | yarn remove xxx 删除项目依赖包yarn global remove xxx 全局删除包 |
安装项目依赖 | yarn |
运行命令别名 | yarn <别名> # 不需要添加run |
:warning:
yarn
全局安装包不可用 -> 需要手动配置环境变量
- 可通过
yarn global bin
查看全局安装包的位置
7.4.4 配置淘宝镜像
yarn config set registry https://registry.npmmirror.com/
查看
yarn
的配置项yarn config list
7.5 管理发布包
7.5.1 创建与发布
- 我们可以将自己开发的工具包发布到npm服务上,方便自己和其他开发者使用,操作步骤如下:
创建文件夹,并创建文件
index.js
,在文件中声明函数,使用module.exports
暴露npm
初始化工具包,package.json
填写包的信息(==包的名字是唯一的==)激活账号
修改为官方的官方镜像(命令行中运行
nrm use npm
)命令行下
npm login
填写相关用户信息命令行下
npm publish
提交包
测试结果:
7.5.2 更新/删除包
更新
- 更新包中的代码
- 测试代码是否可用
- 修改
package.json
中的版本号 - 发布更新
npm publish
删除
npm unpublish
:key: 删除包的条件
- 是包的作者
- 发布小于24小时
- 大于24小时,且没有其他包依赖,且每周小于300下载量,且只有一个维护者
7.6 扩展内容
7.6.1 编程语言的包管理工具
语言 | 包管理工具 |
---|---|
PHP | composer |
Python | pip |
Java | maven |
Go | go mod |
JavaScript | npm/yarn/cnpm/other |
Ruby | rubyGems |
7.6.2 操作系统的包管理工具
操作系统 | 包管理工具 |
---|---|
Centos | yum |
Ubuntu | apt |
MacOS | homebrew |
Windows | chocolatey |
7.7 NVM
7.7.1 nvm介绍
- NVM
Node Version Manager
node版本管理工具- 方便切换不同版本的Node.js
7.7.2 nvm使用
常用操作 | 说明 |
---|---|
nvm list available |
显示所有可以下载的 Node.js 版本 |
nvm list |
显示已安装的版本 |
nvm current |
显示当前使用的 Node.js 版本 |
nvm install xx.xx.xx |
安装 xx.xx.xx 版本的 Node.js |
nvm install latest |
安装最新版本的 Node.js |
nvm uninstall xx.xx.xx |
删除某个版本的 Node.js |
nvm use xx.xx.xx |
切换到 xx.xx.xx 版本的Node.js |
nvm alias <name> <vesion> |
为指定的版本设置一个别名 |
nvm unalias <name> |
删除别名 |
8 express框架
8.1 介绍
- 基于 Node.js平台,快速、开放、极简的 Web 开发框架 Express
- 简单来说,express是一个封装好的工具包,封装了很多功能,便于我们开发WEB应用(HTTP服务)
8.2 express使用
8.2.1 安装
npm i express
8.2.2 基本使用
- 创建 JS 文件
1 | // 1. 导入 express |
- 执行脚本命令
node 01.js
8.3 express路由
8.3.1 定义
- 路由确定了应用程序如何响应客户端对特定端点的请求
8.3.2 路由的使用
- 一个路由的组成有请求方法,路径和回调函数组成
app.<method>(path, callback)
1 | const express = require("express"); |
8.4 参数获取
8.4.1 获取请求参数
方法 | 说明 |
---|---|
method |
请求方法 |
url |
请求地址 |
httpVersion |
http版本 |
headers |
请求头 |
path |
请求路径(不包含请求参数) |
query |
请求参数 |
ip |
请求ip |
get() |
获取请求头的某个字段 |
params |
路由参数 |
1 | // 原生方法 |
- 测试结果:
8.4.2 获取路由参数
1 | const express = require("express"); |
- 测试结果:
8.4.3 路由参数练习
1 | const express = require("express"); |
8.4.4 响应设置
1 | // 原生响应 |
:key:
express
会自动根据需要设置中文编码响应:key: 可以链式调用
- 其他响应
1 | res.redirect('http://www.baidu.com') // 重定向(url, 默认状态码 302) |
8.5 express中间件
- 中间件(Middle ware)本质是一个回调函数
- 中间件函数可以像路由回调一样访问请求对象(request)+响应对象(response)
- 作用:中间件的作用就是使用函数封装公共操作,简化代码
- 类型
- 全局中间件
- 路由中间件
8.5.1 全局中间件
function recordMiddleware(req, res, next) {...}
每一个请求到达服务端之后都会执行全局中间件函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41const express = require("express");
const fs = require("fs");
const path = require("path");
const ws = fs.createWriteStream(path.resolve(__dirname, "./log.txt"), {
flags: "a",
});
// 创建应用对象
const app = express();
// 声明中间件函数
function recordMiddleware(req, res, next) {
let { url, ip } = req;
let time = new Date().toLocaleString();
let log = `url: ${url}, ip: ${ip}, time: ${time}\n`;
ws.write(log);
next(); // 调用下一个中间件函数
}
// 使用中间件函数
app.use(recordMiddleware);
app.get("/home", (req, res) => {
res.send("<h1>首页</h1>");
});
app.get("/admin", (req, res) => {
res.send("<h1>后台管理</h1>");
});
app.all("*", (req, res) => {
res.status(404).send("<h1>404 Not Found</h1>");
});
app.listen(8000, () => {
console.log(
"服务已经启动(8000端口监听中...), 请访问: http://localhost:8000/"
);
});:key: 注意要使用
next()
进行放行
8.5.2 路由中间件
1 | const express = require("express"); |
- :key: 路由中间件 添加在需要添加此中间件路由处理函数之前
8.5.3 静态资源中间件
1 | app.use(express.static(__dirname + '/public')); |
1 | const express = require("express"); |
:key: 会自动设置静态资源类型
index.html
文件为默认打开的资源 ->http://localhost:8000/
- 如果静态资源与路由规则同时匹配,==谁先匹配谁就响应==
- 路由响应动态资源,静态资源中间件响应静态资源
8.5.4 请求体数据 body-parser
安装
npm i body-parser
导包
const bodyParser = require('body-parser');
获取中间件函数
1
2
3
4
5// 创建 json 解析器
const jsonParser = bodyParser.json();
// 创建 querystring 解析器
const urlencodedParser = bodyParser.urlencoded({ extended: false });设置路由中间件,使用
requese.body
来获取请求体数据1
2
3
4app.post("/login", urlencodedParser, (req, res) => {
console.log(req.body); // 中间件函数会将解析后的数据放入 req.body 中 - [Object: null prototype] { username: '123', password: '123' }
res.send("登录成功");
});
8.5.5 防盗链
禁止设置域名之外其他网站访问服务资源
- 请求区别在于 请求头
referer
包含所在域名
8.5.6 路由模块化
- xxxRouter.js
1 | // 1. 导入 express |
- 路由模块引入
1 | // 导入路由模块 |
:key: 可以设置路由前缀
1
2 app.use('/', indexRouter);
app.use('/users', usersRouter);
8.6 EJS 模板引擎
8.6.1 简介
模板引擎
模板引擎是分离用户界面和业务数据的一种技术
“E” 代表什么?可以表示 “可嵌入(Embedded)”,也可以是“高效(Effective)”、“优雅(Elegant)”或者是“简单(Easy)”。EJS 是一套简单的模板语言,帮你利用普通的 JavaScript 代码生成 HTML 页面。EJS 没有如何组织内容的教条;也没有再造一套迭代和控制流语法;有的只是普通的 JavaScript 代码而已。
EJS
- EJS是一个高效的Javascript的模板引擎。
- 特点:纯 JavaScript、快速开发、语法简单、执行迅速、易于调试、社区活跃
EJS特性
- 快速编译与绘制输出
- 简洁的模板标签:
<% %>
- 自定义分割符(例如:用
<? ?>
替换<% %>
) - 引入模板片段
- 同时支持服务器端和浏览器 JS 环境
- JavaScript 中间结果静态缓存
- 模板静态缓存
- 兼容 Express 视图系统
8.6.2 EJS使用
1)基本使用
安装:
npm i ejs --save
简单使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19const fs = require("fs");
// 1. 导入 ejs
const ejs = require("ejs");
// 字符串
let china = "中国";
let str = `我爱你 ${china}`;
console.log(str); // 我爱你 中国
// 使用 ejs 渲染 - 1. 字符串
let str2 = "我爱你 <%= china %>";
let result2 = ejs.render(str2, { china: china });
console.log(result2); // 我爱你 中国
// 使用 ejs 渲染 - 2. 文件
let str3 = fs.readFileSync(__dirname + "/01_html.html").toString();
let nowTime = new Date().toLocaleString();
let result3 = ejs.render(str3, { china, nowTime });
console.log(result3); // 我爱你 中国可以在html或其他文件中设置坑位,由js文件引入时进行填充
2)列表渲染
1 | // 列表 |
3)条件渲染
1 | let isLogin = false; |
8.6.3 express配合ejs
- 创建
xxx.ejs
模板文件
1 | // 1. 设置模板引擎 |
模板名会自动寻找此名称的
ejs
文件
8.6.4 express-generator
安装
npm i -g express-generator
使用
express -e <name>
进入文件安装依赖
npm i
-
- ```md
bin:可执行文件。里面包含了一个启动文件 www 默认监听端口是 3000。
public:静态文件。用来存放项目静态文件目录如js,css以及图片。
routes:路由文件。路由主要定义 url 和 资源 的映射关系 ( 一一对应关系 ),
主要用来接收前端发送的请求 响应数据给前端
views:后端模版文件。
app.js:入口文件。
package.json:工程信息和模块依赖。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
- 运行 `node ./bin/www`
- 自动生成的 `app.js`
```js
var createError = require('http-errors'); // 错误处理模块(http-errors)
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser'); // cookie解析中间件(cookie-parser)
var logger = require('morgan'); // 日志中间件(morgan)
var indexRouter = require('./routes/index'); // 路由
var usersRouter = require('./routes/users'); // 路由
var app = express();
// 设置模板引擎
app.set('views', path.join(__dirname, 'views')); // 模板文件存放的目录
app.set('view engine', 'ejs'); // 模板引擎
app.use(logger('dev')); // 用来记录日志的
app.use(express.json()); // 用来解析json格式的请求体
app.use(express.urlencoded({ extended: false })); // 用来解析urlencoded格式的请求体
app.use(cookieParser()); // 用来解析cookie
app.use(express.static(path.join(__dirname, 'public'))); // 静态文件服务
app.use('/', indexRouter);
app.use('/users', usersRouter);
// 捕获404错误并转发到错误处理器
app.use(function(req, res, next) {
next(createError(404));
});
// 错误处理器
app.use(function(err, req, res, next) {
// 设置本地变量,只提供开发中的错误
res.locals.message = err.message; // 错误信息
res.locals.error = req.app.get('env') === 'development' ? err : {}; // 错误对象
// 渲染错误页面
res.status(err.status || 500); // 设置状态码
res.render('error'); // 渲染模板
});
module.exports = app;
- ```md
-
8.7 文件上传
- formidable formidable - npm
1 | // 显示上传表单 |
8.8 记账本案例
8.8.1 注意点
静态资源最好使用 绝对地址
- 避免加
./
-> 这会导致指向根目录
- 避免加
express-generator 已经自动应用了 解析部分数据的中间件,故可直接进行 请求体数据的获取
8.8.2 logdb
1)简介
- 简介:lowdb是一个本地的json文件数据库,简单来说就是用一个json文件来充当数据库,来实现增删改查这些数据库的基本的功能。
- 优点
- 轻量化
- 纯js实现
- 更好的typescript支持
- 原子化写入:简单理解就是不会错误写入,你指定A,就会写入A,这对数据库十分重要
2)基本使用
- 安装
npm i lowdb
npm i lowdb@1.0.0
=> 最新版为ES6语法
1 | // 导入 lowdb |
3)shortid
安装
npm i shortid
使用
let id = shortid.generate();
9 MongoDB
9.1 简介
MongoDB是一个基于分布式文件存储的数据库
特性
- 面向文档存储,基于JSON/BSON 可表示灵活的数据结构
- 动态 DDL能力,没有强Schema约束,支持快速迭代
- 高性能计算,提供基于内存的快速数据查询
- 容易扩展,利用数据分片可以支持海量数据存储
- 丰富的功能集,支持二级索引、强大的聚合管道功能,为开发者量身定做的功能,如数据自动老化、固定集合等等。
- 跨平台版本、支持多语言SDK…
9.2 相关概念
:key: 一般情况下
- 一个项目使用一个数据库
- 一个集合会存储同一种类型的数据
-
- database 数据库,与SQL的数据库(database)概念相同,一个数据库包含多个集合(表)
- collection 集合,相当于SQL中的表(table),一个集合可以存放多个文档(行)。 不同之处就在于集合的结构(schema)是动态的,不需要预先声明一个严格的表结构。更重要的是,默认情况下 MongoDB 并不会对写入的数据做任何schema的校验。
- document 文档,相当于SQL中的行(row),一个文档由多个字段(列)组成,并采用bson(json)格式表示。
- field 字段,相当于SQL中的列(column),相比普通column的差别在于field的类型可以更加灵活,比如支持嵌套的文档、数组。
- 此外,MongoDB中字段的类型是固定的、区分大小写、并且文档中的字段也是有序的
-
- _id 主键,MongoDB 默认使用一个_id 字段来保证文档的唯一性。
- reference 引用,勉强可以对应于 外键(foreign key) 的概念,之所以是勉强是因为 reference 并没有实现任何外键的约束,而只是由客户端(driver)自动进行关联查询、转换的一个特殊类型。
- view 视图,MongoDB 3.4 开始支持视图,和 SQL 的视图没有什么差异,视图是基于表/集合之上进行动态查询的一层对象,可以是虚拟的,也可以是物理的(物化视图)。
- index 索引,与SQL 的索引相同。
- $lookup,这是一个聚合操作符,可以用于实现类似 SQL-join 连接的功能
- transaction 事务,从 MongoDB 4.0 版本开始,提供了对于事务的支持
- aggregation 聚合,MongoDB 提供了强大的聚合计算框架,group by 是其中的一类聚合操作。
9.3 基本使用
9.3.1 安装
建议选择zip类型,通用性更强
配置步骤如下:
- 将压缩包移动到
C:\Program Files
下,然后解压 - 创建
C:\data\db
目录,mongodb会将数据默认保存在这个文件夹 - 以mongodb中
bin
目录作为工作目录,启动命令行 运行命令
mongod
,如图证明启动成功运行命令
mongo
- 将压缩包移动到
配置环境变量
9.3.2 命令行交互
1)数据库命令
命令 | 说明 |
---|---|
show dbs |
显示所有数据库 |
db |
显示当前所在的数据库 |
show collections |
查询库中的连接 |
use xxx |
创建数据库/切换数据库 |
db.dropDatabase() |
删除当前的数据库 |
db.getName() |
获取数据库名称 |
db.stats() |
获取数据库状态 |
db.version() |
获取数据库版本 |
db.getMongo() |
查看当前数据库的链接机器地址 |
db.cloneDatabase("127.0.0.1") |
从指定主机上克隆数据库 |
db.copyDatabase("yhb", "test1", "127.0.0.1") |
从指定的机器上复制指定数据库数据到某个数据库 |
2)集合命令
命令 | 说明 |
---|---|
db.createCollection("human" [,{"size":1024,capped:true,max:100}]) |
创建一个集合 // 指定数据库大小size,满了,会删除旧文档 |
show collections |
获取当前db中的所有集合 |
db.getCollection("human") |
获取指定集合 |
db.<name>.renameCollection('xxx') |
重命名集合 |
db.xxx.stats() |
查看集合状态 |
db.xxx.drop() |
删除某个集合 |
db.printCollectionStats() |
显示当前db所有聚集索引的状态 |
3)文档命令
命令 | 说明 |
---|---|
db.xxx.find() |
查询所有记录 |
db.xxx.distinct("xxx") |
查询去掉后的当前聚集集合中的某列的重复数据 |
db.xxx.find(查询条件) |
查询文档 |
db.xxx.insert(xxx) db.people.save(xxx) |
插入文档 |
db.xxx.updata(查询条件, 新文档) db.xxx.updata({name: xxx}, {$set: {age: 24}}) |
更新文档 |
db.xxx.remove(查询条件) |
删除文档 |
9.4 Mongoose
- Mongoose是一个对象文档模型库
- Mongoose为模型提供了一种直接的,基于scheme结构去定义你的数据模型。它内置数据验证, 查询构建,业务逻辑钩子等,开箱即用
- 方便使用代码操作mongodb数据库
9.4.1 安装
- 安装
npm i mongoose
9.4.2 连接数据库
1 | // 1. 导入 mongoose 第三方模块 |
9.4.3 文档操作
1)构建
1 | const Book = mongoose.model('Book', yourSchema); |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 mongoose.connection.once("open", () => {
// console.log("数据库连接成功");
// 1. 创建文档的结构对象(schema)
// 设置约束条件: 约束文档中的属性名和属性值的类型
const BookSchema = new mongoose.Schema({
name: String,
author: String,
price: Number,
});
// 2. 创建模型对象, 对文档操作的封装对象
const BookModel = mongoose.model("book", BookSchema);
// 3. 新增
BookModel.create({
name: "西游记",
author: "吴承恩",
price: 100,
})
.then((data) => {
console.log(data);
mongoose.disconnect(); // 关闭数据库连接
})
.catch((err) => {
console.log(err);
});
});
2)查询
可以使用
model
的 find、findById、findOne 或 where 静态函数检索文档
1 | await Book.find({ name: '历险记').where('createdDate').gt(oneYearAgo).exec(); |
1
2
3
4 // 查询所有
Book.find().then((data) => {
console.log(data);
});
3)删除
模型具有静态
deleteOne()
和deleteMany()
函数,用于删除与给定filter
匹配的所有文档
1 | Book.deleteOne({ name: '历险记' }); |
4)更新
- 修改不返回
1 | await Book.updateOne({ name: '历险记' }, { name: '历险记II' }); |
- 修改并返回
1 | A.findOneAndUpdate(conditions, update, options) // returns Query |
5)条件控制
1 | Book.find({ price: { $lt: 100 } }).then((data) => { |
9.4.4 字段类型
类型 | 描述 |
---|---|
String | 字符串 |
Number | 数字 |
Boolean | 布尔值 |
Array | 数组,也可以使用 [] 来标识 |
Date | 日期 |
Buffer | Buffer 对象 |
Mixed | 任意类型,需要使用 mongoose.Schema.Types.Mixed 指定 |
ObjectID | 对象ID,需要使用 mongoose.Schema.Types.ObjectID 指定 |
Decimal128 | 高精度数字,需要使用 mongoose.Schema.Types.Decimal128 指定 |
Map | 集合,键值必须是字符串 |
UUID | 通用唯一标识符,要创建 UUID,建议使用 Node 的内置 UUIDv4 生成器 |
BigInt | 大整型 |
- 字段项验证
验证值 | 说明 |
---|---|
retuired: true |
必填项 |
default: 'xxx' |
默认值 |
enum: ['111', '222'] |
枚举值 |
unique: true |
唯一值 |
1 | const BookSchema = new mongoose.Schema({ |
9.4.5 个性化读取
1 | Book.find({ price: { $gt: 0 } }) |
9.4.6 代码模块化
- index.js
1 | // 导入 db 文件 |
- db.js
1 | /** |
- BookModel.js
1 | const mongoose = require("mongoose"); |
- config.js
1 | module.exports = { |
9.5 图形化工具
Robo 3T Robo 3T | Free, open-source MongoDB GUI (formerly Robomongo)
Navicat Navicat
9.6 优化记账本联系
9.6.1 日期对象转换 moment
- 需求:
2023-10-06 -> Object -> new Date()
- Moment.js
- 将时间字符串转换为时间对象,
moment('yyyy-mm-dd').toDate()
- 格式化字符串
moment(time).format('YYYY-MM-DD')
10 API接口
10.1 简介
API(Application Program Interface),是前后端通信的桥梁
- 简单理解:一个接口就是服务中的一个路由规则,根据请求响应结果,==一般为JSON格式==
- 这里的接口指的是“数据接口”
开发与调用
- 后端开发
- 一般情况下,前端调用
组成
- 请求方法,接口地址(URL)、请求参数、接口结果
示例
10.2 RESTful API
- RESTful(Representational State Transfer) API是一种特殊风格的接口,主要特点有如下几个:
- URL中的路径表示资源,路径中不能有动词,例如create,delete,update等这些都不能有
- 操作资源要与HTTP请求方法对应
- 操作结果要与HTTP响应状态码对应
- 特点
- REST 是 Representational State Transfer 的缩写,如果一个架构符合 REST 原则,就称它为 RESTful 架构
- RESTful 架构可以充分的利用 HTTP 协议的各种功能,是 HTTP 协议的最佳实践
- RESTful API 是一种软件架构风格、设计风格,可以让软件更加清晰,更简洁,更有层次,可维护性更好
- 服务端回应数据
- 客户端请求时,要明确告诉服务器,接受 JSON 格式,请求的 HTTP 头的 ACCEPT 属性要设成 application/json
- 服务端返回的数据,不应该是纯文本,而应该是一个 JSON 对象。服务器回应的 HTTP 头的 Content-Type 属性要设为 application/json
- 错误处理 如果状态码是4xx,就应该向用户返回出错信息。一般来说,返回的信息中将 error 作为键名,出错信息作为键值即可。 {error: “Invalid API key”}
- 认证 RESTful API 应该是无状态,每个请求应该带有一些认证凭证。推荐使用 JWT 认证,并且使用 SSL
- Hypermedia 即返回结果中提供链接,连向其他API方法,使得用户不查文档,也知道下一步应该做什么
10.3 json-server
json-server
- 本身是一个JS编写的工具包,可以快速搭建RESTful API服务
- typicode/json-server: Get a full fake REST API with zero coding in less than 30 seconds (seriously) (github.com)
操作步骤:
全局安装
npm i -g json-server
创建JS0N文件(db.json),编写基本结构
1
2
3
4
5
6
7
8{
"song": [
{ "id": 1, "name": "青花瓷", "singer": "周杰伦" },
{ "id": 2, "name": "干杯", "singer": "五月天" },
{ "id": 3, "name": "当", "singer": "动力火车" },
{ "id": 4, "name": "不能说的秘密", "singer": "周杰伦" }
]
}以JS0文件所在文件夹作为工作目录,执行命令
json-server --watch db.json
- 默认监听端口为3000
参数说明
参数 | 简写 | 默认值 | 说明 |
---|---|---|---|
—config | -c | 指定配置文件 | 默认值 ‘json-server.json’ |
—port | -p | 设置端口 [默认值: 3000] | Number |
—host | -H | 设置域 [默认值: “0.0.0.0”] | String |
—watch | -w | 监听文件 | 是否监听 |
—routes | -r | 指定自定义路由 | |
—middlewares | -m | 指定中间件 文件 | [数组] |
—static | -s | 设置静态文件路径 | 静态目录 |
10.4 接口测试工具 apipost
10.4.1 基本使用
10.4.2 设置默认前缀
10.4.3 请求体
10.4.4 公共参数与文档功能
公共参数
新建目录
公共内容
- 文档功能
10.5 优化记账本-API
./routes/api/account.js
1 | var express = require("express"); |
11 会话控制
11.1 简介
- 会话控制就是对会话进行控制
- HTTP是一种无状态的协议,它没有办法区分多次的请求是否来自于同一个客户端,无法区分用户
- 而产品中又大量存在的这样的需求,所以我们需要通过会话控制来解决该问题
- 常见的会话控制技术有三种
- cookie
- session
- token
11.2 cookie
11.2.1 简介
cookie是HTTP服务器发送到用户浏览器并保存在本地的一小块数据
- cookie是保存在浏览器端的一小块数据
- cookie是按照域名划分保存的
特点:浏览器向服务器发送请求时,会自动将当前域名下可用的
cookie
设置在请求头中,然后传递给服务器cookie设置响应报文
set-cookie:name=xxx
11.2.2 express操作cookie
1)增加
res.cookie('key', 'value', )
1 | // 设置cookie |
- 测试结果:
2)删除
1 | // 删除cookie |
3)读取 cookie-parser
安装
npm i cookie-parser
插件导入
1
2const cookieParser = require("cookie-parser");
app.use(cookieParser());读取
req.cookies
11.3 session
11.3.1 简介
- session是保存在服务器端的一块儿数据,保存当前访问用户的相关信息
- 作用:实现会话控制,可以识别用户的身份,快速获取当前用户的相关信息
- 运行流程
- 填写账号和密码校验身份,校验通过后创建session信息,然后将
session_id
的值通过响应头返回给浏览器 - 有了cookie,下次发送请求时会自动携带cookie,服务器通过cookie中的
session_id
的值确定用户的身份
- 填写账号和密码校验身份,校验通过后创建session信息,然后将
11.3.2 express-session
1)安装+配置
安装
npm i express-session connect-mongo
express-session
connect-mongo
简单配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29const express = require("express");
const session = require("express-session");
const MongoStore = require("connect-mongo");
const app = express();
app.use(
session({
name: "sid", // 设置cookie的name,默认值是:connect.sid
secret: "bayyy", // 用来对session id相关的cookie进行签名(加盐)
saveUninitialized: false, // 是否自动保存未初始化的会话,建议false
resave: true, // 是否每次都重新保存会话
store: MongoStore.create({
mongoUrl: "mongodb://localhost:27017/users", // mongodb链接地址
}),
cookie: {
httpOnly: true, // 开启后前端无法通过 JS 操作cookie
maxAge: 1000 * 60 * 60 * 24, // 设置cookie过期时间(毫秒) -> 1天
},
})
);
app.get("/", (req, res) => {
res.send("<h1>HOME</h1>");
});
app.listen(8000, () => {
console.log("http://localhost:8000");
});
2)session操作
1 | // session 创建 |
11.4 session和cookie读取
- 存在的位置
- cookie 浏览器端
- session 服务端
- 安全性
- cookie是以明文的方式存放在客户端的,安全性相对较低
- session存放于服务器中,所以安全性相对较好
- 网络传输量
- cookie设置内容过多会增大报文体积,会影响传输效率
- session数据存储在服务器,只是通过cookie传递id,所以不影响传输效率
- 存储限制
- 浏览器限制单个cookie保存的数据不能超过4K,且单个域名下的存储数量也有限制
- session数据存储在服务器中,所以没有这些限制
11.5 优化记账本-注册功能
11.5.1 密码加密 MD5
安装
npm i md5
使用
1
2const md5 = require("md5");
let pwd = md5(xxx)
11.5.2 CSRF跨站请求伪造
link href 等进行对其他(跨域)网站直接操作,会导致携带其他网站的cookie
解决 操作使用post请求
- ```html
- ```html
11.6 token
11.6.1 简介
- token是服务端生成并返回给HTTP客户端的一串加密字符串,token中保存着用户信息
- 作用:实现会话控制,可以识别用户的身份,主要用于移动端APP
工作流程
- 填写账号和密码校验身份,校验通过后响应token,token一般是在响应体中返回给客户端的
- 后续发送请求时,需要手动将token添加在请求报文中,一般是放在请求头中
特点
- 服务端压力更小
- 数据存储在客户端
- 相对更安全
- 数据加密
- 可以避免CSRF(跨站请求伪造)
- 扩展性更强
- 服务间可以共享
- 增加服务节点更简单
- 服务端压力更小
11.6.2 JWT
JWT(JSON Web Token)是目前最流行的跨域认证解决方案,可用于基于token的身份验证
- JWT使token的生成与校验更规范
- 我们可以使用
jsonwebtoken
包来操作token - jsonwebtoken - npm
安装
npm i jsonwebtoken
使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22// 导入 jwt
const jwt = require("jsonwebtoken");
// 创建(生成) token
// let token = jwt.sign(用户数据, 加密字符串, 配置对象);
const token = jwt.sign(
{
username: "zhangsan",
},
"bayyy",
{
expiresIn: 3, // 过期时间(单位: 秒)
}
);
// 校验 token
jwt.verify(token, "bayyy", (err, data) => {
if (err) {
console.log(err);
}
console.log(data);
});
11.7 记账本-token
11.7.1 本地域名
- 所谓本地域名就是只能在本机使用的域名,一般在开发阶段使用
- 操作流程
- 编辑文件
C:\Windows\System32\drivers\etc\hosts
127.0.0.1 www.bayyy.com
- 如果修改失败,需要修改该文件的权限
- 编辑文件
- 端口号 监听
80
默认端口,无需在域名后标注
11.7.2 HTTPS
https本意是http+SSL(Secure Sockets Layer安全套接层)
- https可以加密HTTP报文,所以也可以理解为是安全的HTTP
工具官网:Certbot (eff.org)
操作流程
安装工具
管理员运行命令
certbot certonly-standalone
代码配置如下
1
2
3
4
5
6
7
8
9
10
11
12
13const https = require("https");
https
.createServer(
{
key: fs.readFileSync("/etc/letsencrypt/path/to/privkey.pem"),
cert: fs.readFileSync("/etc/letsencrypt/path/to/cert.pem"),
ca: fs.readFileSync("/etc/letsencrypt/path/to/chain.pem"),
},
app
)
.listen(443, () => {
console.log("HTTPS Server is running on: https://localhost:%s", 443);
});证书更新,有效期为3个月
1
2
3
4## 一般更新
certbot renew
## 强制更新
certbot --force-renewal