再学Node.js

133 阅读13分钟

Node.js介绍

  1. 重要性:Node.js在前端核心技术体系起到承上启下的作用 image.png
  2. Node.js能做服务端:小程序、App、网站、游戏

3.学习Node.js的十大模块

  • 计算机基础
  • Node.js ApI
  • 包管理工具
  • HTTP协议
  • Express框架
  • Mongodb数据库
  • 会话控制
  • API接口
  • 实战案例
  • 服务器部署

Node.js是什么

官网定义:Node.js是一个开源的,跨平台的JavaScript运行环境

通俗来说:Node.js就是一款应用程序,是一款软件,它可以运行JavaScript

Node.js的作用

  1. 开发服务器应用
  2. 开发工具类应用 webpack vite babel
  3. 开发桌面端应用 electron VScode Figma Postman

image.png ![image.png](p9-juejin.byteimg.com/tos-cn-i-k3…

Node.js的初体验

image.png

Node.js注意点

1.Node.js中不能使用BOM和DOM的API

image.png

2.Node.js的顶级对象:global或者globalThis,相当于jswindow

console.log(global === globalThis) //true

Buffer

什么是Buffer

Buffer缓冲区,是一个类似于Array的对象,用于表示固定长度的字节序列。换句话说,Buffer就是一段固定长度的内存空间,用于处理二进制数据

image.png

Buffer的特点

  1. Buffer大小固定且不能修改
  2. Buffer性能较好,可以直接对计算机内存进行操作
  3. 每个元素大小为1字节

创建Buffer

  1. Buffer.alloc
//创建了一个长度为 10 字节的 Buffer,相当于申请了 10 字节的内存空间,每个字节的值为 0 
let buf_1 = Buffer.alloc(10); // 结果为
  1. Buffer.allocUnsafe
//创建了一个长度为 10 字节的 Buffer,buffer 中可能存在旧的数据, 可能会影响执行结果,所以叫 unsafe 
let buf_2 = Buffer.allocUnsafe(10);

3.Buffer.from

//通过字符串创建 
Buffer let buf_3 = Buffer.from('hello'); 
//通过数组创建 Buffer 
let buf_4 = Buffer.from([105, 108, 111, 118, 101, 121, 111, 117]);

Buffer操作与注意事项

1.Buffer与字符串的转化

我们可以借助toString方法将Buffer转为字符串

let buf_4 = Buffer.from([105, 108, 111, 118, 101, 121, 111, 117])
console.log(buf_4.toString())

toString 默认是按照 utf-8 编码方式进行转换的

Buffer的读写

Buffer 可以直接通过[] 的方式对数据进行处理。

//读取
console.log(buf_3[1]); 
//修改 
buf_3[1] = 97; 
//查看字符串结果 
console.log(buf_3.toString())

计算机的基本组成

image.png image.png

fs模块

fs模块可以实现与硬盘的交互,例如文件的创建、删除、重命名、移动,还有文件内容的写入、读取,以及文件夹的相关操作

文件写入

文件写入就是将数据保存到文件中,可以用下面一些方法

  • writeFile 异步写入
  • writeFileSync 同步写入
  • appendFile/appendFileSync 追加写入
  • createWriteStream 流式写入

writeFile 异步写入

语法:fs.writeFile(file, data[, options], callback)

参数说明:

  • file 文件名
  • data 待写入的数据
  • options 选项设置
  • callback 写入回调

返回值: undefined

// require 是 Node.js 环境中的'全局'变量,用来导入模块
const fs = require('fs');
//将 『三人行,必有我师焉。』 写入到当前文件夹下的『座右铭.txt』文件中
fs.writeFile('./座右铭.txt', '三人行,必有我师焉。', err => {
    //如果写入失败,则回调函数调用时,会传入错误对象,如写入成功,会传入 null
    if (err) {
        console.log(err);
        return;
    }
    console.log('写入成功')
})

writeFileSync 同步写入

语法: fs.writeFileSync(file, data[, options])

参数:与fs.writeFile 大体一致,只是没有 callback 参数

返回值: undefined

// require 是 Node.js 环境中的'全局'变量,用来导入模块
const fs = require('fs');
//将 『三人行,必有我师焉。』 写入到当前文件夹下的『座右铭.txt』文件中
try {
    fs.writeFileSync('./座右铭.txt', '三人行,必有我师焉。');
    console.log('创建成功');
} catch (e) {
    console.log(e);
}

console.log('fuck you');

Node.js 中的磁盘操作是由其他 线程完成的,结果的处理有两种模式:

同步处理 JavaScript 主线程 效率较低

异步处理 JavaScript 主线程 效率较好

文件追加写入

appendFile 作用是在文件尾部追加内容,appendFile 语法与 writeFile 语法完全相同

语法:

fs.appendFile(file, data[, options], callback)

fs.appendFileSync(file, data[, options])

返回值: 二者都为 undefined

const fs = require('fs')

fs.appendFile('./座右铭.txt', '择其善者而从之,其不善者而改之。', err => {
    if (err) throw err;
    console.log('追加成功')
});
fs.appendFileSync('./座右铭.txt', '\r\n温故而知新, 可以为师矣');

createWriteStream 流式写入

语法: fs.createWriteStream(path[, options])

参数说明:

  • path 文件路径
  • options 选项配置

返回值: Object

const fs = require('fs')

let ws = fs.createWriteStream('./观书有感.txt');
ws.write('半亩方塘一鉴开\r\n');
ws.write('天光云影共徘徊\r\n');
ws.write('问渠那得清如许\r\n');
ws.write('为有源头活水来\r\n');
ws.close()

程序打开一个文件是需要消耗资源的,流式写入可以减少打开关闭文件的次数。 流式写入方式适用于 大文件写入或者频繁写入的场景, writeFile 适合于 1-5 写入文件的场景 写入频率较低的场景

文件写入场景

  • 下载文件
  • 安装软件
  • 保存程序日志,如果Git
  • 编辑器保存文件
  • 视频录制

当需求持久化保存数据的时候,应该想到文件写入

文件读取

文件读取就是通过程序从文件中拿到数据,可以使用如下几种方式

  • readFile 异步读取
  • readFileSync 同步读取
  • createReadStream 流式读取

readFile 异步读取

语法:fs.readFile(path[, options], callback)

参数说明:

  • path 文件路径
  • options 配置项
  • callback回调函数

返回值:undefined

const fs = require('fs')

fs.readFile('./座右铭.txt', (err, data) => {
    if (err) throw err
    console.log(data.toString());
})

readFileSync 同步读取

语法:fs.readFileSync(path[, options])

参数说明:

  • path 文件路径
  • options 配置项

返回值:Buffer或者String

let fileData = fs.readFileSync('./观书有感.txt')
console.log('fileData', fileData.toString());
let fileData1 = fs.readFileSync('./座右铭.txt', 'utf-8')
console.log('fileData1', fileData1);

createReadStream 文件的流式读取

什么是流式读取:把文件一块一块读

语法:fs.createReadStream(path[, options])

参数说明:

  • path文件路径
  • options 选项配置 返回值:Object
//创建读取流对象
let rs = fs.createReadStream('./观书有感.txt');
//每次取出 64k 数据后执行一次 data 回调
rs.on('data', data => {
console.log(data);
console.log(data.length);
});
//读取完毕后, 执行 end 回调
rs.on('end', () => {
console.log('读取完成')
})

读取文件的应用场景

  • 编辑器打开文件
  • 查看图片
  • 播放视频
  • 播放音乐
  • Git查看日志
  • 上传文件
  • 查看聊天记录

文件复制

const fs = require('fs')

// 方式一:readFile
let fileData = fs.readFileSync('./嘻嘻嘻.mp4')

fs.writeFileSync('./嘻嘻嘻-2.mp4', fileData)

// 方式二:createReadStream (流式方式更好,所占资源更少)
let readStream = fs.createReadStream('./嘻嘻嘻.mp4')
let writeStream = fs.createWriteStream('./嘻嘻嘻-3.mp4')

readStream.on('data', chunk => {
    writeStream.write(chunk)
})
//另一种快速写法
readStream.pipe(writeStream) 

文件重命名和移动

在 Node.js 中,我们可以使用 renamerenameSync移动重命名文件或文件夹

语法:

fs.rename(oldPath, newPath, callback)

fs.renameSync(oldPath, newPath)

const fs = require('fs')

fs.rename('./file.txt', './fileNew.txt', (err) => {
    if (err) throw err
    console.log('文件重命名成功')
})

fs.renameSync('./fileNew.txt', './fileDir/file.txt')

文件删除

在Node.js中,我们可以使用unlinkunlinkSync来删除文件

语法:

fs.unlink(path, callback)

fs.unlinkSync(path)

const fs = require('fs');

fs.unlink('./嘻嘻嘻.mp4', (err) => {
    if (err) {
        console.log(err);
    } else {
        console.log('删除成功');
    }
});

fs.unlinkSync('./嘻嘻嘻-2.mp4')

14.4版本有个新方法rm与rmSync、

// rm rmSync方法 14.4版本新方法
fs.rm('./嘻嘻嘻-3.mp4', (err) => {
    if (err) {
        console.log(err);
    } else {
        console.log('删除成功');
    }
});

文件夹操作

Node.js可以对文件夹进行创建 读取 删除等操作

  • mkdir/mkdirSync 创建文件夹
  • readdir/readdirSync 读取文件夹
  • rmdir/rmdirSync 删除文件夹

mkdir创建文件夹

我们可以使用 mkdirmkdirSync 来创建文件夹

语法: fs.mkdir(path[, options], callback)

fs.mkdirSync(path[, options])

创建单个文件夹

const fs = require('fs')

// 单个文件夹创建
fs.mkdir('./Video', (err) => {
    if (err) throw err
    console.log('创建成功');
})

fs.mkdirSync('./Img')

递归创建文件夹

// 递归创建
fs.mkdir('./A/B/C', { recursive: true }, (err) => {
    if (err) throw err
    console.log('创建成功');
})

读取文件夹

在Node.js中,我们可以使用readdirreaddirSync来上实现读取文件夹

语法:

fs.readdir(path[, options], callback)

fs.readdirSync(path[, options])

const fs = require('fs')

fs.readdir('./', (err, data) => {
    if (err) throw err
    console.log('data', data); //获取名称
})

const data = fs.readdirSync('./')
console.log('data', data); //readdirSync 返回值是一个数组

删除文件夹

可以使用rmdir或者rmdirSync来删除文件夹

语法: fs.rmdir(path[, options], callback)

fs.rmdirSync(path[, options])

单个删除

const fs = require('fs')

fs.rmdir("./html", (err) => {
    if (err) throw err
    console.log('删除成功');
})

递归删除

const fs = require('fs')

//未来要被弃用了,建议用rm
fs.rmdir("./fileDir", { recursive: true }, (err) => {
    if (err) throw err
    console.log('删除成功');
})

// rm
fs.rm("./fileDir", { recursive: true }, (err) => {
    if (err) throw err
    console.log('删除成功');
})

查看资源状态

在Node.js中,我们可以使用stat或statSync来查看资源的详细信息

语法: fs.stat(path[, options], callback)

fs.statSync(path[, options])

const fs = require('fs')

fs.stat('./fileDir/test.txt', (err, data) => {
    if (err) throw err
    console.log('data', data);
})

结果值对象结构:

  • size 文件体积
  • birthtime 创建时间
  • mtime 最后修改时间
  • isFile 检测是否为文件
  • isDirectory 检测是否为文件夹
  • ....

__dirname

__dirname是Node.js环境中的全局变量

__dirname保存着当前文件所在目录的绝对路径,可以与文件名拼接成绝对路径

使用 fs 模块的时候,尽量使用 __dirname 将路径转化为绝对路径,这样可以避免相对路径产生的 Bug

path模块

path模块提供了操作路径的功能,介绍几个常用的几个API:

  • path.resolve 拼接规范的绝对路径
  • path.sep 获取操作系统的路径分隔符
  • path.parse 解析路径并返回对象
  • path.basename 获取路径的基础名称
  • path.dirname 获取路径的目录名
  • path.extname 获取路径的扩展名
const path = require('path');
//获取路径分隔符
console.log(path.sep);
//拼接绝对路径
console.log(path.resolve(__dirname, 'test')); //用的很多
//解析路径
let pathname = 'D:/program file/nodejs/node.exe';
console.log(path.parse(pathname));
//获取路径基础名称
console.log(path.basename(pathname))
//获取路径的目录名
console.log(path.dirname(pathname));
//获取路径的扩展名
console.log(path.extname(pathname));

Http协议(非常重要)

什么是Http协议

HTTP(hypertext transport protocol)协议;中文叫超文本传输协议

这个协议详细规定了 浏览器 和万维网 服务器 之间互相通信的规则。

协议中主要规定了两个方面的内容:

  • 客户端:用来向服务器发送数据,可以被称之为请求报文
  • 服务端:向客户端返回数据,可以被称之为响应报文

报文:可以理解成就是一堆字符串

窥探Http报文

fiddler

fiddler是一个可以查看http报文的软件,可以理解为浏览器与服务器传输报文的中介。

请求报文结构

  • 请求行
  • 请求头
  • 空格
  • 请求体 image.png

请求行

image.png

常见的几种方法:

image.png

请求头

image.png

格式:『头名:头值』

常见的请求头有:

  • Host 主机名
  • Connection 连接的设置 keep-alive(保持连接);close(关闭连接)
  • Cache-Control 缓存控制 max-age = 0 (没有缓存)
  • Upgrade-Insecure-Requests 将网页中的http请求转化为https请求(很少用)老网站升级
  • User-Agent 用户代理,客户端字符串标识,服务器可以通过这个标识来识别这个请求来自 哪个客户端 ,一般在PC端和手机端的区分
  • Accept 设置浏览器接收的数据类型
  • Accept-Encoding 设置接收的压缩方式
  • AcceptLanguage 设置接收的语言 q=0.7为喜好系数,满分为1
  • Cookie

Http的请求体

请求体内容的格式是非常灵活的

(可以是空)===>GET请求

(也可以是字符串,还可以是JSON)===>Post请求

例如:

  • 字符串:keywords=手机&price=2000
  • JSON:{"keywords":"手机","price":2000}

响应报文

  • 响应行

Http/1.1 200 Ok

HTTP/1.1:HTTP协议版本号

200:响应状态码

OK:响应状态描述

响应状态码和响应字符串关系是一一对应的

  • 响应头
  • 空行
  • 响应体

http模块

创建http服务

// 1.导入http模块
const http = require('http')

// 2.创建服务对象
// request,是对请求报文的封装对象,通过request对象可以获得请求报文的数据
// response,是对响应报文的封装对象,通过response对象可以设置响应报文的数据
const server = http.createServer((request, response) => {
    response.end('hello server')
})

// 3.监听端口,启动服务
server.listen(8080, () => {
    console.log('服务已经启动,端口8080正在监听中...')
})

http.createServer 里的回调函数的执行时机: 当接收到 HTTP 请求的时候,就会执行

http服务的注意事项

1.响应内容中文乱码的解决办法

response.setHeader('content-type','text/html;charset=utf-8');

获取http请求报文

想要获取请求的数据,需要通过request对象

  • 请求方法 request.method
  • 请求版本 request.httpVersion
  • 请求路径 request.url
  • URL路径 require('url').parse(request.url).pathname
  • URL查询字符串 require('url').parse(request.url,true).query
  • 请求头 request.headers
  • 请求体

request.on('data',function(chunk){}) request.on('end',function{})

获取请求体

模拟请求过程

index.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>模拟请求</title>
</head>

<body>
    <form action="http://localhost:8080" method="post">
        <input type="text" name="username">
        <input type="text" name="password">
        <input type="submit" value="提交">
    </form>
</body>

</html>

index.js

const http = require('http')

const server = http.createServer((request, response) => {
    let body = ''
    // 绑定data事件
    request.on('data', chunk => {
        body += chunk
    })
    // 绑定end事件
    request.on('end', () => {
        console.log(body) //会有请求体数据
        // 响应
        response.end('hello world')
    })
})

server.listen(8080, () => {
    console.log('服务已经启动,端口8080正在监听中...')
})

获取请求路径与查询字符串

旧版

const http = require('http')
// 导入url模块 
const url = require('url') //url也是Node.js的一个内置模块,专门用来解析url的

const server = http.createServer((request, response) => {
    // 解析request.url
    let res = url.parse(request.url, true)
    console.log('res', res);
    // 路径
    let pathname = res.pathname
    console.log('pathname', pathname);
    // 查询字符串
    let keyword = res.query.keyword
    console.log('查询的关键字', keyword);
    response.end('url')
})

server.listen(8080, () => {
    console.log('服务已经启动,端口8080正在监听中...')
})

新版本(建议)

const http = require('http')

const server = http.createServer((request, response) => {
    console.log('request.url', request.url);
    // 实例化URL对象
    let url = new URL(request.url, 'http://localhost:8080')
    // 输出路径
    console.log('路径名称', url);
    // 输出查询字符串
    console.log('查询字符串', url.searchParams.get('keyword'));
    response.end('url new')
})

server.listen(8080, () => {
    console.log('服务已经启动,端口8080正在监听中...')
})

http请求练习

//1、引入http模块
const http = require("http");
//2、建立服务
const server = http.createServer((request, response) => {
    let { url, method } = request; //对象的解构赋值
    //设置响应头信息
    //解决中文乱码
    response.setHeader("Content-Type", "text/html;charset=utf-8")
    if (url == "/register" && method == "GET") {
        response.end("注册页面");
    } else if (url == "/login" && method == "GET") {
        response.end("登录页面");
    } else {
        response.end("<h1>404 Not Found</h1>")
    }
})
//3、监听端口
server.listen(8000, () => {
    console.log('服务启动中....');
})
//1、引入http模块
const http = require("http");
//2、建立服务
const server = http.createServer((request, response) => {
    let { url, method } = request; //对象的解构赋值
    //设置响应头信息
    //解决中文乱码
    response.setHeader("Content-Type", "text/html;charset=utf-8")
    if (url == "/register" && method == "GET") {
        response.end("注册页面");
    } else if (url == "/login" && method == "GET") {
        response.end("登录页面");
    } else {
        response.end("<h1>404 Not Found</h1>")
    }
})
//3、监听端口
server.listen(8000, () => {
    console.log('服务启动中....');
})

设置http响应报文

  • 设置响应状态码 response.statusCode
  • 设置响应状态描述 response.statusMessage用的非常少
  • 设置响应头信息 response.setHeader('头名','头值')
  • 设置响应体 response.write('xx') response.end('xx')

write 和 end 的两种使用情况:

//1. write 和 end 的结合使用 响应体相对分散

response.write('xx');

response.write('xx');

response.write('xx');

response.end(); //每一个请求,在处理的时候必须要执行 end 方法

//2. 单独使用 end 方法 响应体相对集中

response.end('xxx');

网络资源加载基本过程

网页资源的加载都是循序渐进的,首先获取 HTML 的内容, 然后解析 HTML 再发送其他资源的请求,如 CSS,Javascript,图片等。 理解了这个内容对于后续的学习与成长有非常大的帮助

设置mime类型

mime类型(媒体类型)是一种标准,用来表示文档、文件或者字节流的性质和格式。

mime 类型结构:[type]/[subType]

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 类型,浏览器在遇到该类型的响应 时,会对响应体内容进行独立存储,也就是我们常见的 下载 效果

get和post请求的区别

  • GET主要用来获取数据,Post主要用来提交数据
  • GET带参数请求是将参数放到URL之后,Post是将参数放到请求体中
  • Post请求相对GET安全一些,因为get会把参数暴露在地址栏
  • GET请求大小有限制,一般为2k,而Post请求则没有大小限制

Node.js模块化

express