Node.js学习

Node.js学习
LiSirNode与终端
Node.js就是一款应用程序,是一款软件,可以运行JavaScrpt
在终端用node命令去运行文件,比如有一个名为1.js的文件
那么打开终端
1 | node 1.js |
Buffer(缓冲器)
概念
Buffer 是一个类似于数组的 对象 ,用于表示固定长度的字节序列
Buffer 本质是一段内存空间,专门用来处理 二进制数据 。
特点
- Buffer 大小固定且无法调整
- Buffer 性能较好,可以直接对计算机内存进行操作
- 每个元素的大小为 1 字节(byte)
使用
创建 Buffer
Node.js 中创建 Buffer 的方式主要如下几种:
Buffer.alloc1
2// 创建了一个长度为 10 字节的 Buffer,相当于申请了 10 字节的内存空间,每个字节的值为 0
let buf_1 = Buffer.alloc(10) //=>结果为<Buffer 00 00 00 00 00 00 00 00 00 00>Buffer.allocUnsafe1
2// 创建了一个长度为 10 字节的 Buffer,buffer 中可能存在旧数据,可能会影响执行结果,所以叫 unsafe ,但是效率比 alloc 高
let buf_2 = Buffer.allocUnsafe(10)Buffer.from1
2
3
4// 通过字符串创建 Buffer
let buf_3 = Buffer.from('hello')
// 通过数组创建 Buffer
let buf_4 = Buffer.from([105, 108, 111, 118, 101, 121, 111, 117])
Buffer 与字符串的转化
我们可以借助
toString方法将 Buffer 转为字符串
1 | let buf_4 = Buffer.from([105, 108, 111, 118, 101, 121, 111, 117]) |
**注意: **toString 默认是按照 utf-8 编码方式进行转换的
Buffer 的读写
Buffer 可以直接通过
[]的方式对数据进行处理。
1 | let buf_3 = Buffer.from('hello') |
注意:
- 如果修改的数值超过
255,则超过8位数据会被舍弃 - 一个
utf-8的字符 一般 占3个字节
fs 模块
fs 全称为
file system,称之为文件系统,是 Node.js 中的内置模块,可以对计算机中的磁盘进行操作。
本章节会介绍如下几个操作:
- 文件写入
- 文件读取
- 文件移动与重命名
- 文件删除
- 文件夹操作
- 查看资源状态
文件写入
文件写入就是将 数据 保存到 文件 中,我们可以使用如下几个方法来实现该效果
| 方法 | 说明 |
|---|---|
| writeFile | 异步写入 |
| writeFileSync | 同步写入 |
| appendFile / appendFileSync | 追加写入 |
| createWriteStream | 流式写入 |
writeFile 异步写入
语法: fs.writeFile(file, data[, options], callback)
参数说明:
- file 文件名
- data 待写入的数据
- options 选项设置
(可选) - callback 写入回调
返回值: undefined
代码示例:
1 | // require 是 Node.js 环境中的 '全局' 变量,用来导入模块 |
writeFileSync 同步写入
语法: fs.writeFileSync(file, data[, options])
参数与 fs.writeFile 大体一致,只是没有 callback 参数
返回值:undefined
代码示例:
1 | try{ |
Node.js 中的磁盘操作是由其他
线程完成的,结果的处理有两种模式:
- 同步处理 JavaScript 主线程
会等待其线程的执行结果,然后再继续执行主线程的代码,效率较低- 异步处理 JavaScript 主线程
不会等待其线程的执行结果,直接执行后续的主线程代码,效率较好
appendFile / appendFileSync 追加写入
appendFile作用是在文件尾部追加内容,appendFile语法与writeFile语法完全相同
语法:
1 | fs.appendFile(file, data[, options], callback) |
返回值:二者都为 undefined
实例代码:
1 | fs.append('./座右铭.txt', '则其善者而从之,其不善者而改之。', err =>{ |
createWriteStream 流式写入
语法:fs.createWriteStream(path[, options])
参数说明:
path文件路径options选项配置(可选)
**返回值: **Object
代码示例:
1 | let ws = fs.createWriteStream('./观书有感.txt') |
程序打开一个文件是需要消耗资源的,流式写入可以减少打开关闭文件的次数。
流式写入方式适用于 大文件写入或者频繁写入的场景,
writeFile适合于 写入频率较低的场景
写入文件的场景
文件写入 在计算机中是一个非常常见的操作,下面的场景都用到了文件写入
- 下载文件
- 安装软件
- 保存程序日志,如 Git
- 编辑器保存文件
- 视频录制
当 需要持久化保存数据 的时候,应该想到
文件写入
文件读取
文件读取顾名思义,就是通过程序从文件中取出其中的数据,我们可以使用如下几种方式:
| 方法 | 说明 |
|---|---|
| readFile | 异步读取 |
| readFileSync | 同步读取 |
| createReadStream | 流式读取 |
readFile 异步读取
语法: fs.readFile(path[, options], callback)
参数说明:
- path 文件路径
- options 选项配置
- callback 回调函数
返 回 值 : undefined
代码示例:
1 | // 导入 fs 模块 |
readFileSync 同步读取
语法:fs.readFileSync(path[, options])
参数说明:
- path 文件路径
- options 选项配置
返 回 值 :string | Buffer
代码示例:
1 | let data = fs.readFileSync('./座右铭.txt') |
createReadStream 流式读取
语法:fs.createReadStream(path[, options])
参数说明:
- path 文件路径
- options 选项配置(
可选)
返回值:Object
代码示例:
1 | // 创建读取流对象 |
读取文件应用场景
- 电脑开机
- 程序运行
- 编辑器打开文件
- 查看图片
- 播放视频
- 播放音乐
- Git 查看日志
- 上传文件
- 查看聊天记录
文件移动与重命名
在 Node.js 中,我们可以使用
rename或renameSync来移动或重命名文件或文件夹
语法:
1 | fs.rename(oldPath, newPath, callback) |
参数说明:
- oldPath 文件当前的路径
- newPath 文件新的路径
- callback 操作后的回调
代码示例:
1 | fs.rename('./观书有感.txt', './论语/观书有感.txt', err =>{ |
注: 如果还是移动到当前路径,但是修改了名字,就是重命名了
文件删除
在 Node.js 中,我们可以使用
unlink或unlinkSync来删除文件
语法:
1 | fs.unlink(path, callback) |
参数说明:
- path 文件路径
- callback 操作后的回调
代码示例:
1 | const fs = require('fs') |
文件夹操作
借助 Node.js 的能力,我们可以对文件夹进行
创建、读取、删除等操作
| 方法 | 说明 |
|---|---|
| mkdir / mkdirSync | 创建文件夹 |
| readdir / readdirSync | 读取文件夹 |
| rmdir / rmdirSync | 删除文件夹 |
mkdir 创建文件夹
在 Node.js 中,我们可以使用
mkdir或mkdirSync来创建文件夹
语法:
1 | fs.mkdir(path[, options], callback) |
参数说明:
- path 文件夹路径
- options 选项配置(
可选) - callback 操作后的回调
示例代码:
1 | // 异步创建文件夹 mk make 制作 dir directory 文件夹 |
readdir 读取文件夹
在 Node.js 中,我们可以使用
readdir或readdirSync来读取文件夹
语法:
1 | fs.readdir(path[, options], callback) |
参数说明:
- path 文件夹路径
- options 选项配置(
可选) - callback 操作后的回调
示例代码:
1 | // 异步读取 |
rmdir 删除文件夹
在 Node.js 中,我们可以使用
rmdir或rmdirSync来删除文件夹
语法:
1 | fs.rmdir(path[, options], callback) |
参数说明:
- path 文件夹路径
- options 选项配置( 可选 )
- callback 操作后的回调
示例代码:
1 | // 异步删除文件夹 rm remove 移除 |
查看资源状态
在 Node.js 中,我们可以使用
stat或statSync来查看资源的详细信息
语法:
1 | fs.stat(path[, options], callback) |
参数说明:
- path 文件夹路径
- options 选项配置( 可选 )
- callback 操作后的回调
示例代码:
1 | // 异步获取状态 |
结果值对象结构:
- size 文件体积
- birthtime 创建时间
- mtime 最后修改时间
- isFile 检测是否为文件
- isDirectory 检测是否为文件夹
- ….
相对路径问题
fs 模块对资源进行操作时,路径的写法有两种:
- 相对路径
./座右铭.txt当前目录下的 座右铭.txt座右铭.txt等效于上面的写法../座右铭.txt当前目录的上一级目录中的 座右铭.txt
- 绝对路径
D:/Program Fileswindows 系统下的绝对路径/user/binLinux 系统下的绝对路径
相对路径中所谓的
当前目录,指的是命名行的工作目录,而并非是文件的所在目录所以当命名行的工作目录与文件所在目录不一致时,会出现一些 Bug
__dirname
__dirname 与 require 类似,都是 Node.js 环境中的 ‘全局’ 变量
__dirname 保存着 当前文件夹所在目录的绝对路径,可以使用 __dirname 与文件名拼接成绝对路径
代码示例:
1 | //=>__dirname + '/data.txt' === 'D:\\Desktop\\Node\\code\\03-fs模块/data.txt' |
使用 fs 模块的时候,尽量使用
__dirname路径转换为绝对路径,这样可以避免相对路径产生的 Bug
练习
编写一个 JS 文件,实现复制文件的功能
1 | /* |
文件重命名
1 | // 1. 导入 fs 模块 |
案例:考试成绩整理
1 | //引入fs模块 |
注意:\r\n 是回车(Carriage Return, \r)和换行(Line Feed, \n)的组合,常用于表示一行的结束并开始新的一行,尤其在Windows操作系统中的文本文件格式里广泛使用
案例—时钟案例
案例难点:匹配script标签和style标签的正则表达式不容易写出
案例步骤:
1.创建写入CSS文件的js文件
1 | //导入文件处理模块 |
2.创建写入JS文件的JS文件
1 | //导入文件处理模块 |
3.创建写入HTML文件的js
1 | //导入文件处理模块 |
注意:在新写入的HTML文件中,需要进行正则匹配然后替换,已外部文件的形式引入CSS和JS文件,注意路径
path模块
path模块提供了操作路径的功能,我们将介绍如下几个较为常用的几个 API:
| API | 说明 |
|---|---|
| path.resolve | 拼接规范的绝对路径 常用 |
| path.sep | 获取操作系统的路径分隔符 |
| path.parse | 解析路径并返回对象 |
| path.basename | 获取路径的基础名称 |
| path.dirname | 获取路径的目录名 |
| path.extname | 获得路径的扩展名 |
代码示例:
1 | // 导入 fs 模块 |
http模块
概念
HTTP(hypertext transport protocol)协议;中文叫 超文本传输协议
是一种基于TCP/IP的应用层通信协议
这个协议详细规定了 浏览器 和 万维网 服务器 之间互相通信的规则
协议中主要规定了两个方面的内容:
客户端:用来向服务器发送数据,可以被称之为 请求报文
服务端:向客户端返回数据,可以被称之为 响应报文
报文:可以简单理解为就是一堆字符串
请求报文的组成
- 请求行
- 请求头
- 空行
- 请求体
HTTP 的请求行
请求方法(get、post、put、delete等)
请求 URL(统一资源定位器)
例如:http://www.baidu.com/index.html?a=100&b=200#logo
- http: 协议 (https、ftp、ssh等)
- www.baidu.com 域名
- 80 端口号
- /index.html 路径
- a=100&b=200 查询字符串
- #logo 哈希 (锚点链接)
HTTP协议版本号
HTTP 请求头
格式:『头名:头值』
常见的请求头有:
| 请求头 | 解释 |
|---|---|
| Host | 主机名 |
| Connection | 连接的设置 keep-alive(保持连接);close(关闭连接) |
| Cache-Control | 缓存控制 max-age = 0 (没有缓存) |
| Upgrade-Insecure-Requests | 将网页中的http请求转化为 https 请求(很少用)老网站升级 |
| User-Agent | 用户代理,客户端字符串标识,服务器可以通过这个标识来识别这个请求来自哪个客户端 ,一般在PC端和手机端的区分 |
| Accept | 设置浏览器接收的数据类型 |
| Accept-Encoding | 设置接收的压缩方式 |
| Accept-Language | 设置接收的语言 q=0.7 为喜好系数,满分为1 |
| Cookie | 后面单独讲 |
HTTP 的请求体
请求体内容的格式是非常灵活的,
(可以是空)==> GET请求,
(也可以是字符串,还可以是JSON)===> POST请求
例如:
字符串:keywords=手机&price=2000
JSON:{“keywords”:”手机”,”price”:2000}
响应报文的组成
响应行:
HTTP/1.1 200 OKHTTP/1.1:HTTP协议版本号
200:响应状态码 404 Not Found 500 Internal Server Error
还有一些状态码,参考:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Status
OK:响应状态描述
响应状态码 和 响应字符串 关系是 一一对应 的。
响应头
Cache-Control: 缓存控制 private 私有的,只允许客户端缓存数据Connection: 链接设置Content-Type:text/html;charset=utf-8: 设置响应体的数据类型以及字符集,响应体为html,字符集utf-8Content-Length: 响应体的长度,单位为字节
空行
响应体
响应体内容的类型是非常灵活的,常见的类型有 HTML、CSS、JS、图片、JSON
创建 HTTP 服务
使用 nodejs 创建 HTTP 服务
操作步骤
1 | //1. 导入 http 模块 |
http.createServer里的回调函数的执行时机: 当接收到 HTTP 请求的时候,就会执行
测试
浏览器请求对应端口
1 | http://127.0.0.1:9000 |
注意事项
命令行
ctrl + c停止服务当服务启动后,更新代码 必须重启服务才能生效
响应内容中文乱码的解决办法
1
2// 设置响应头
response.setHeader('content-type','text/html;charset=utf-8');端口号被占用
Error: listen EADDRINUSE: address already in use :::90001)关闭当前正在运行监听端口的服务 ( 使用较多 )
2)修改其他端口号
HTTP协议默认端口是80。HTTPS协议的默认端口是443, HTTP 服务开发常用端口有3000,8080,8090,9000等
如果端口被其他程序占用,可以使用 资源监视器 找到占用端口的程序,然后使用 任务管理器 关闭对应的程序
浏览器查看 HTTP 报文
点击步骤
查看请求行与请求头
查看请求体
查看 URL 查询字符串
查看响应行与响应头
查看响应体
获取 HTTP 请求报文
| 含义 | 语法 | 重点掌握 |
|---|---|---|
| 请求方法 | 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(){}) |
1 | // 1. 导入 http 模块 |
注意事项:
request.url只能获取路径以及查询字符串,无法获取 URL 中的域名以及协议的内容request.headers将请求信息转化成一个对象,并将属性名都转化成了『小写』- 关于路径:如果访问网站的时候,只填写了 IP 地址或者是域名信息,此时请求的路径为『
/』 - 关于
favicon.ico:这个请求是属于浏览器自动发送的请求
提取 http 报文的请求体
1 | // 1. 导入 http 模块 |
提取 http 报文中 url的路径 与 查询字符串
1 | // 导入 http 模块 |
1 | // 导入 http 模块 |
练习
按照以下要求搭建 HTTP 服务
| 请求类型**(方法)** | 请求地址 | 响应体结果 |
|---|---|---|
| get | /login | 登录页面 |
| get | /reg | 注册页面 |
1 | //1、引入http模块 |
设置 HTTP 响应报文
| 作用 | 语法 |
|---|---|
| 设置响应状态码 | response.statusCode |
| 设置响应状态描述 | response.statusMessage ( 用的非常少 ) |
| 设置响应头信息 | response.setHeader(‘头名’, ‘头值’) (可以自定义) |
| 设置响应体 | response.write(‘xx’) response.end(‘xxx’) |
1 | // 1. 设置响应状态码 |
1 | // write 和 end 的两种使用情况: |
练习
搭建 HTTP 服务,响应一个 4 行 3 列的表格,并且要求表格有 隔行换色效果 ,且 点击 单元格能 高亮显示
方法一
1 | // 导入 http 模块 |
方法二
1 | // 导入 http 模块 |
table.html
1 |
|
网页资源的基本加载过程
网页资源的加载都是循序渐进的,首先获取 HTML 的内容, 然后解析 HTML 在发送其他资源的请求,如 CSS,Javascript,图片等。理解了这个内容对于后续的学习与成长有非常大的帮助
静态资源服务
静态资源 是指 内容长时间不发生改变的资源 ,例如图片,视频,CSS 文件,JS文件,HTML文件,字体文件等
动态资源 是指 内容经常更新的资源 ,例如百度首页,网易首页,京东搜索列表页面等
网站根目录或静态资源目录
HTTP 服务在哪个文件夹中寻找静态资源,那个文件夹就是 静态资源目录 ,也称之为 网站根目录
思考:vscode 中使用 live-server 访问 HTML 时, 它启动的服务中网站根目录是谁?
- 改文件的所处的文件夹
网页中的 URL
网页中的 URL 主要分为两大类:相对路径 与 绝对路径
绝对路径
绝对路径可靠性强,而且相对容易理解,在项目中运用较多
| 形式 | 特点 |
|---|---|
| http://atguigu.com/w eb | 直接向目标资源发送请求,容易理解。网站的外链会用到此形式 |
| //atguigu.com/web | 与页面 URL 的协议拼接形成完整 URL 再发送请求。大型网站用的比较多 |
| /web | 与页面 URL 的协议、主机名、端口拼接形成完整 URL 再发送请求。中小型网站 |
相对路径
相对路径在发送请求时,需要与当前页面 URL 路径进行 计算 ,得到完整 URL 后,再发送请求,学习阶段用的较多
例如当前网页 url 为 http://www.atguigu.com/course/h5.html
| 形式 | 最终的 URL |
|---|---|
| ./css/app.css | http://www.atguigu.com/course/css/app.css |
| js/app.js | http://www.atguigu.com/course/js/app.js |
| ../img/logo.png | http://www.atguigu.com/img/logo.png |
| ../../mp4/show.mp4 | http://www.atguigu.com/mp4/show.mp4 |
网页中使用 URL 的场景小结
包括但不限于如下场景:
- a 标签 href
- link 标签 href
- script 标签 src
- img 标签 src
- video audio 标签 src
- form 中的 action
- AJAX 请求中的 URL
设置资源类型(mime类型)
媒体类型(通常称为 Multipurpose Internet Mail Extensions 或 MIME 类型 )是一种标准,用来表示文档、文件或字节流的性质和格式。
1 | mime 类型结构: [type]/[subType] |
HTTP 服务可以设置响应头 Content-Type 来表明响应体的 MIME 类型,浏览器会根据该类型决定如何处理资源
下面是常见文件对应的 mime 类型
1 | html: 'text/html', |
对于未知的资源类型,可以选择
application/octet-stream类型,浏览器在遇到该类型的响应时,会对响应体内容进行独立存储,也就是我们常见的下载效果
1 | require('http').createServer((request,response)=>{ |
很明显上面的代码,当只要有一个请求路径就需要进行判断,显然这种方式不够完美,那么我们需要封装
1 | require('http').createServer((request,response)=>{ |
GET 和 POST 请求场景小结
GET 请求的情况:
- 在地址栏直接输入 url 访问
- 点击 a 链接
- link 标签引入 css
- script 标签引入 js
- img 标签引入图片
- form 标签中的 method 为 get (不区分大小写)
- ajax 中的 get 请求
POST 请求的情况:
form 标签中的 method 为 post(不区分大小写)
AJAX 的 post 请求
GET和POST请求的区别
GET 和 POST 是 HTTP 协议请求的两种方式。
GET主要用来获取数据,POST主要用来提交数据GET带参数请求是将参数缀到 URL 之后,在地址栏中输入 url 访问网站就是 GET 请求,POST带参数请求是将参数放到请求体中POST请求相对GET安全一些,因为在浏览器中参数会暴露在地址栏GET请求大小有限制,一般为 2K,而 POST 请求则没有
3.Node模块化
1.模块化的基本概念
1.1什么是模块化?
遇到一个复杂问题时,自顶向下逐层把系统划分成若干模块的过程。模块是可组合、分解更换的单元
把代码进行模块化拆分的好处:
- 提高了代码的复用性
- 提高了代码的可维护性
- 可以实现按需加载
1.2模块化规范
就是一种规则,对代码进行模块化拆分与组合时,需要遵守的规则
2.Node.js中的模块化
2.1Node.js中模块的分类
2.2加载模块
基础语法
1 | //利用require方法 |
require在加载模块时,会执行被加载模块中的代码。
2.3模块的作用域
只能在导入的模块内部使用,但是当你通过导入的方式加入某模块时,被导入模块内定义的变量无法再当前导入模块内使用。
比如你在c1模块内导入c2模块,c2模块中的变量在c1中是无法访问的,但是c2可以访问自身的变量。
2.4向外共享模块作用域中的成员
在每个.js自定义模块中都有一个module对象,在这里面存储了和当前模块有关的信息。
利用里面的export属性来进行向外共享即可,下面介绍用法
基础语法:
1 | module.exports={ |
当我向M2中导入自定义(这里用M1解释)M1模块时,M2模块中得到的成员就是M1中module.exports所指向的对象。
打印查看
2.4.1共享成员的注意点
2.4.2exports对象
Node提供了一种简化的module.exports对象,exports对象。默认情况下,exports和module.exports指向同一个对象。
2.4.3exports和module.exports的使用误区
- require()模块时,得到的永远是module.exports所指向的对象
2.5Node.js中的模块化规范
CommonJS 规范是一种JavaScript模块化编程的标准,最初为服务器端JavaScript设计,被Node.js采纳并广泛应用。它的核心特点包括:
- 模块定义:在CommonJS中,每个
.js文件被视为一个独立的模块。这意味着文件内部定义的变量、函数等都是模块私有的,不会污染全局作用域。 - 模块导出:使用
module.exports或exports来导出模块希望公开的接口。其他模块可以访问这些导出的接口来使用该模块的功能。module.exports是模块的默认导出对象,而exports是module.exports的一个引用,通常用于导出多个属性或方法。 - 模块引入:通过
require函数来引入其他模块。require函数接受一个模块标识符作为参数,用于加载并返回导出的模块接口。模块标识符可以是相对路径或绝对路径,也可以是核心模块名。 - 加载机制:Node.js实现了懒加载(也称为延迟加载)策略,即只有当使用
require函数时,模块才会被加载和执行。这样可以提高程序的运行效率,因为不立即加载所有模块,而是按需加载。 - 缓存机制:Node.js对加载过的模块进行缓存,任何后续对该模块的
require调用都会直接从缓存中获取模块,而不是重新加载,这进一步提升了程序性能。 - 当使用
require函数加载模块时,获取到的是module.exports最终指向的那个对象,无论这个指向是否在过程中被改变过。简而言之,尽管exports常用于方便地添加模块接口,但真正对外暴露的是通过module.exports指定的内容。



















