NodeJs笔记
本文最后更新于 2025-01-05,文章内容可能已经过时。
概念
NodeJs
是一个开源跨平台
的javascript 运行环境
,可以运行Javascript代码
,Nodejs
中内置了V8引擎
!
NodeJs
可以帮我们实现,web服务器、开发工具、桌面端应用
开发!
1. web服务器:
服务器
就是计算机电脑
,通过服务应用程序
在计算机
中部署安装
后便可部署web应用
!
2. 开发工具:
比如目前的webpack
vite
以及babel
等开发工具
都是基于Node开发
的!
3. 桌面应用:
比如vscode
和postman
它们是基于,electron
开发的,而electron
又是基于Node
开发的!
浏览器和Node
浏览器:
Javascript
在浏览器环境
中有两部分模块组成,分别是ECMAScript
和WebApi(BOM 和 DOM)
!js
在浏览器
中的全局对象
是window
和globalThis
;
NodeJs:
Javascript
在Node环境
中也有两部分模块组成,分别是ECMAScript
和Node Api
!js
在node环境
中的全局对象
是global
也可以是globalThis
globalThis
是es2020
新增的全局对象
,用来兼容浏览器
和Node环境
中的全局变量
使用!
注意: 在
Node环境
中不能使用BOM
和DOM
相关API!
但是可以使用console
和定时器
!
Buffer
Buffer
是一组不可变
的固定长度
的内存空间
,主要用来处理二进制数据流
!
Buffer
可以直接对计算机内存
进行操作,性能比较好!
Buffer
的长度大小无法改变
!
Buffer
中的内存空间
,每8个bit为1个字节
!
创建Buffer
Buffer
是一个全局对象
,可以通过这个对象
来使用内置的方法
,来操作Buffer数据
!
alloc
:参数
: 指定字节长度
,每次创建时之前的缓冲数据都会清零
!console.log(Buffer.alloc(10));
创建
10个字节
长度的buffer
allocUnsafe
:参数
: 指定字节长度
,每次创建时都会保留
旧的内存数据
!已经使用过的缓冲数据
会在allocUnsafe
中继续保留!console.log(Buffer.allocUnsafe(10));
创建
10个字节
长度的buffer
from
:参数
: 类型可以是字符串
,或者是数组
,将两者以Buffer的形式存储
!console.log(Buffer.from("hello")) // 数组形式 Buffer.from([(105, 108, 111, 118, 101, 121, 111, 117)])
这里每一个
字母
,都会在ASCII码表
中找到对应的数字
,然后转换为二进制数据
进行存储
!而
控制台打印
的时候,不是以二进制
数据展示
的,而是以16进制
去显示
的!
输出结果如下:
alloc <Buffer 00 00 00 00 00 00 00 00 00 00>
allocUnsafe <Buffer 90 76 81 32 f2 7f 00 00 00 00>
from -> <Buffer 68 65 6c 6c 6f>
allocUnsafe
这个方法创建的Buffer
是不安全
的,每次创建内存
时,都会保留旧的内存数据
,不会有清空的操作
!
注意:
Buffer
中存储
的是二进制数据
,但在显示时以16进制
形式展示
!
ASCII码表参照
码表
中对应的数字
都是十进制
数!
Buffer的操作与注意
基本操作
toString
:- 可以将
buffer
通过toString
的方式转换为字符串
,格式为utf-8
!
let buf = Buffer.from([(105, 108, 111, 118, 101, 121, 111, 117)]); console.log("buf", buf.toString());
- 可以将
数组下标
:- 以
数组下标
的形式
来获取字符
中的某一项
,且返回这个字符
所对应的ASCII码10进制数
!
let buf = Buffer.from("hello"); console.log("buf", buf[0]) // h -> 104 console.log("buf", buf[0].toString(2)) // h -> 所对应的 2进制数据 1101000
- 以
注意事项
通过下标的方式修改Buffer数据:
buf[0] = 95;
- 这里的
95
是10进制数
,10进制最大
且是255
,如果超出这个数值
,会有精确显示问题
,会舍去高数位
!
buf[0] = 300;
- 这里的
中文字符:
中文字符
utf-8
在buffer
中一个字符占用3个字节
,且1个字节为8个bit
!
let buf = Buffer.from("你好"); // 6个字节 // log buf <Buffer e4 bd a0 e5 a5 bd>
计算机基础了解组成
计算机的组成部分
主要有,CPU 内存 硬盘
还有主板
以及显卡
等!
组成部件 | 描述 |
---|---|
CPU |
CPU 是计算机的大脑 ,主要程序运算 过程都是靠CPU 来完成的! |
内存 |
可以存储数据 ,主要都是0 和 1二进制的数据 ,断电时 ,数据会丢失 ,但是读写速度比较快 ! |
硬盘 |
可以存储数据 ,断电时 ,不会丢失数据 ,但是读写数据比较慢 ! |
主板 |
主板 是一块电路板 ,可以将以上cpu 内存 硬盘等部件 通过接口 结合在一起使用 ! |
显卡 |
显卡 主要用来外接显示器 ,可以在显示器 中呈现运算后的效果 ! |
风扇 |
风扇 的作用是用来给CPU降温 的,在计算过程 中遇到复杂大型 的程序 ,会对CPU运行负荷 加大,发热现象 ! |
计算机的运行过程
当把
计算机
的基本硬件组装完毕
后是不能直接运行
的,还需要对计算机进行系统安装
!
系统:
是一个应用程序
,系统有windows mac
以及linux
等系统
,在系统中,我们可以通过界面
以及命令
的形式来操作系统
!
比如:
基本的文件存储操作
,以及其它程序
的安装
以及运行
操作等,最终经过硬件运转流程
,呈现在显示器
中!
系统
可以看做一个所有程序的集装箱
,所有的程序运行等操作
都是基于系统
来完成的!
一个软件运行的基本流程
软件(qq 微信)
,通过官方网站下载
后,会存放到电脑的硬盘
当中!
运行软件,
将软件运行文件
载入到内存中
,然后通过CPU
去计算,分配任务,如果遇到显示任务 ,会触发指令,将显示任务
转移给显卡去做处理,
最终呈现在屏幕
中,如果遇到声音任务
,则会将声音指令,转移给声卡去处理
,最终由音响设备去播放
进程与线程
进程:
在系统中,每运行一个程序
时,都会产生一个进程!
线程:
线程
是进程
中的子集
,每个进程
下都会有多个线程
在执行任务
!
NodeApi
异步
和同步
NodeApi
中,有异步方法
和同步方法
!
异步代码:
表示同一时间内可以做多件事
,比如以上文件写入
的代码就是异步
的,写入文件的同时,不影响下面同步代码执行
!
同步代码:
表示在执行异步代码时
,下面代码无法执行
,是阻塞状态
,需要等异步代码执行有结果
时,才能继续执行
!
在Node Api
中,一般方法名
带有Sync
后缀的,都是同步方法
!
异步代码块
// 异步代码
fs.writeFile(`${__dirname}/test.txt`, "三人行,必有我师焉!", (err) => {
if (err) {
console.log(err);
} else {
console.log("写入成功"); // 其次打印
}
});
console.log("11111!") // 先打印 以上异步代码 不影响这一行代码输出
同步代码块
fs.writeFileSync(`${__dirname}/test.txt`,"三人行,必有我师焉!");
console.log("11111!") // 会等待 以上文件内容写入完毕后,才会打印!
fs模块
fs模块
,主要用来操作文件
的,可以在硬盘上创建文件
,以及写入文件
和删除文件
等相关操作!
文件创建与写入
writeFile 异步
writeFile方法
,可以对文件内部写入指定内容
,如果指定的文件不存在
,则会创建这个文件
,并写入内容
!
返回值: undefined!
参数 | 类型 | 描述 |
---|---|---|
filePath |
String |
文件的路径 ,包含文件后缀 ,若该文件不存在 ,则会在路径下创建这个文件 ! |
fileContent |
String |
文件内容 |
options(可选) |
Object |
配置对象 ,如果不写,可以直接写callback |
callback |
Function |
回调函数 ,如果文件创建失败 时,会有个err回调参数 |
fs.writeFile(`${__dirname}/test.txt`, "三人行,必有我师焉!", (err) => {
if (err) {
console.log(err);
} else {
console.log("写入成功");
}
});
writeFileSync 同步
writeFileSync
该方法与writeFile
方法一样,只不过该方法,是同步
的,会阻塞其它代码运行
!
let result = fs.writeFileSync(`${__dirname}/test.txt`, "三人行,则必有我师焉!");
console.log("result", result); // 返回值 undefined
文件内容追加写入
appendFile 异步追加
参数 | 类型 | 描述 |
---|---|---|
filePath |
String |
文件的路径 ,包含文件后缀 ,若该文件不存在 ,则会在路径下创建这个文件 ! |
fileContent |
String |
文件内容 |
options(可选) |
Object |
配置对象 ,如果不写,可以直接写callback |
callback |
Function |
回调函数 ,如果文件创建失败 时,会有个err回调参数 |
fs.appendFile(`${__dirname}/test.txt`, " \r\n天下无双", (err) => {
if (err) {
console.log(err);
} else {
console.log("追加成功");
}
});
在
文本内容
中加入转移字符
实现换行\r\n!
appendFileSync 同步追加
fs.appendFileSync(`${__dirname}/test.txt`, " \r\n天下无双");
writeFile
也可以追加文本内容
,不过需要,设置配置项
!
fs.writeFile(`${__dirname}/test.txt`, "\r\n writeFile追加内容", { flag: "a" }, err => {
if (err) {
console.log(err);
} else {
console.log("追加成功");
}
})
{ flag: "a" }
a
表示append追加
的意思,除了a(append)
,还有w(write)默认
和r(read)!
文件流式写入
文件流式写入
,适用于批量文本内容写入
,该方法会开启一个通道
,当我们需要写入内容
时,可以借助通道
,将内容进行写入
!
const fs = require('fs');
// 打开文件流
const writeStream = fs.createWriteStream(`${__dirname}//观沧海.txt`);
// 写入数据
writeStream.write("观沧海\r\n");
writeStream.write("东临碣石,以观沧海。\r\n");
writeStream.write("水何澹澹,山岛竦峙。\r\n");
writeStream.write("树木丛生,百草丰茂。\r\n");
writeStream.write("秋风萧瑟,洪波涌起。\r\n");
// 关闭文件流
writeStream.end();
文件内容读取
文件内容读取
用到了readFile API
, 它可以通过指定路径文件
,来读取文件中的内容
!
const fs = require('fs');
// 使用异步方法 读取文件内容
fs.readFile(`${__dirname}/观沧海.txt`, 'utf8', (err, data) => {
if (err) {
console.error(err);
return;
}
console.log(data);
});
// 使用同步方法 读取文件内容
try {
const data = fs.readFileSync(`${__dirname}/观沧海.txt`, 'utf8');
console.log(data);
} catch (err) {
console.error(err);
}
文件流式读取
createReadStream
文件的
流式读取
,是将文件中的内容进行分块儿处理
,每一次读取65536(64kb)字节
大小,当读取完毕
时,会触发end回调函数
事件!
const fs = require('fs');
// 读取文件内容
const rs = fs.createReadStream(`${__dirname}/登录信息.txt`, { encoding: 'utf8' });
// 监听data事件,每次读取一行数据
rs.on('data', (chunk) => {
// 如果是 媒体类型的数据 还是不要 toString()
console.log(chunk.toString());
});
// 监听end事件,文件读取完毕
rs.on('end', () => {
console.log('文件读取完毕');
});
不过可以试着读取下
mp4文件类型
的数据,返回二进制文件流
,针对媒体类型
而使用toString
时,内容会显示乱码
!
文件复制
首先
需要引入的模块
,process
可以获取运行的内存占用情况
!
const fs = require('fs');
const process = require("process");
使用
readFileSync
和writeFileSync
来实现const data = fs.readFileSync(`{__dirname}/71685787471_.pic_hd.jpg`); fs.writeFileSync(`{__dirname}/71685787471_.pic_hd_copy.jpg`, data); const memoryUsage = process.memoryUsage(); // rss 内存使用的总量 console.log(`Memory usage: ${Math.round(memoryUsage.rss / 1024 / 1024)} MB`);
使用
流式读写形式实现复制操作
const rs = fs.createReadStream(`{__dirname}/71685787471_.pic_hd.jpg`); const ws = fs.createWriteStream(`{__dirname}/71685787471_.pic_hd_copy.jpg`) rs.on("data", (chunk) => { ws.write(chunk); }) // rs.pipe(ws); 或者使用管道与写入流建立通道,写入完毕时,将数据传输给写入流! rs.on("end", () => { const memoryUsage = process.memoryUsage(); // rss 内存使用的总量 console.log(`Memory usage: ${Math.round(memoryUsage.rss / 1024 / 1024)} MB`); })
rs.pipe(ws):
使用管道与写入流建立通道
,读取完毕
时,将数据传输给写入流
!
文件重命名
和移动
文件重命名
和移动
,用到了rename API
,该方法可以完成重命名
和移动文件位置
操作!
重命名:
保持和原有文件路径位置不变
,只需要改变名字
即可完成!
移动文件:
需要改变文件移动的目标路径
,也可以移动同时
,改变文件的名称!
const fs = require('fs');
// 重命名文件
/* fs.rename(`${__dirname}/test.txt`, `${__dirname}/text_rename.txt`, (err) => {
if (err) throw err;
console.log("文件重命名成功!");
}); */
// 移动文件
fs.rename("./text_rename.txt", `${__dirname}/text.txt`, (err) => {
if (err) throw err;
console.log("文件移动成功!");
});
第一个参数为
源文件路径
,第二个参数为修改或移动目标路径
,第三个参数就是回调参数
,操作完毕
后会执行回调函数
!同样,
rename API
也有同步
的版本renameSync
!
文件删除
文件删除
有两种方法,unlink
和rm
两个方法,rm
是Node 14.4
版本新出的,所以注意版本
问题!
const fs = require("fs");
fs.unlink(`${__dirname}/text.txt`, (err) => {
if (err) {
console.error(err);
} else {
console.log("文件删除成功!");
}
});
// node v14.4
/* fs.rm(`${__dirname}/test.txt`, (err) => {
if (err) {
console.error(err);
} else {
console.log("文件删除成功!");
}
}); */
文件夹操作
文件夹操作
,包含文件夹创建(mkdir)、读取(readdir)、删除(rmdir)
相关操作,三个方法都有对应的同步方法Sync
!
参数
参数 | 类型 | 描述 |
---|---|---|
folderPath |
String |
文件夹路径 ,可以是多个,递归创建 (需要配置) |
options |
Object |
配置项 |
callback |
Function |
回调函数 ,创建结果回调函数 |
引入fs模块
const fs = require("fs");
创建文件夹
mkdir
:fs.mkdir(`${__dirname}/testFolder`, err => { if( err ) { console.log("创建失败!") return; } console.log("创建成功!") })
- 递归创建文件夹,需要增加配置项,
recursive
设置为true
!
fs.mkdir(`${__dirname}/testFolder/a/b`,{ recursive: true }, err => { if( err ) { console.log("创建失败!") return; } console.log("创建成功!") })
- 递归创建文件夹,需要增加配置项,
读取文件夹
readdir
:fs.readdir(`${__dirname}/`, (err, files) => { if( err ) { console.log("读取失败!") return; } console.log("读取成功!", files) })
文件夹删除
rmdir
被rm
方法替代:fs.rmdir(`${__dirname}/testFolder/`,{ recursive: true }, err => { if( err ) { console.log("删除失败!") return; } console.log("删除成功!", files) })
recursive
为true
时,可递归删除多层
文件夹!注意事项:
rmdir
需要替换为rm
方法,rmdir
估计已经被移除
!
获取文件信息
获取文件信息
,需要通过stat
方法来完成,它可以获取文件
的大小
,创建时间
以及修改时间
等状态信息!
const fs = require('fs')
// 获取文件信息
fs.stat(`${__dirname}/71685787471_.pic_hd.jpg`, (err, data) => {
if( err ) {
console.log("获取文件信息失败")
return
}
console.log("判断是否文件", data.isFile());
console.log("判断是否目录", data.isDirectory());
});
/*
birthtime: 创建时间
ctime: 最后一次修改文件状态的时间
mtime: 最后一次修改时间
atime: 最后一次访问时间
size: 文件大小
*/
stat
文件信息内容
{
dev: 16777234,
mode: 33060,
nlink: 1,
uid: 501,
gid: 20,
rdev: 0,
blksize: 4096,
ino: 52017648,
size: 1421100,
blocks: 2776,
atimeMs: 1735563016333.3337,
mtimeMs: 1735563015153.2031,
ctimeMs: 1735563015154.8784,
birthtimeMs: 1735563015151.1604,
atime: 2024-12-30T12:50:16.333Z,
mtime: 2024-12-30T12:50:15.153Z,
ctime: 2024-12-30T12:50:15.155Z,
birthtime: 2024-12-30T12:50:15.151Z
}
批量修改文件名案例
const fs = require('fs');
// 批量修改文件名
let files = fs.readdirSync(`${__dirname}/`);
let needModifyFiles = files.filter(file => file.endsWith('.jpg'));
needModifyFiles.forEach((file, index) => {
let [ name ] = file.split(".jpg");
console.log("newFileName", `${name}_img_${index}`);
fs.renameSync(`${__dirname}/${file}`, `${__dirname}/${name}_img_${index}.jpg`);
});
path模块
path模块
主要是用来处理路径
的,文件路径拼接,路径信息获取,获取路径中的文件名
等相关操作!
API | 描述 |
---|---|
path.resolve |
用来拼接路径 的,第一个参数为绝对路径 ,后面的参数为相对路径 ,最后做路径拼接 ! |
path.sep |
获取当前系统的分隔符 ,在windows下分隔符为\ ,在linux系统下为/ ! |
path.parse |
根据指定绝对路径进行解析 ,返回路径对象信息 |
path.basename |
根据指定绝对路径 ,返回当前路径中的文件名 |
path.dirname |
获取路径的目录名 |
path.extname |
获取路径文件的扩展名 |
const path = require('path');
// resolve() 方法用于将多个路径片段组合成一个完整路径。
console.log(path.resolve('/foo', 'bar', 'baz')); // /foo/bar/baz
// join() 方法用于连接多个路径或路径片段,并规范化生成的路径。
console.log(path.join('/foo', 'bar', 'baz')); // /foo/bar/baz
// dirname() 方法用于返回路径的目录名。
console.log(path.dirname('/foo/bar/baz')); // /foo/bar
// basename() 方法用于返回路径的最后一部分。
console.log(path.basename('/foo/bar/baz')); // baz
// extname() 方法用于返回路径的扩展名。
console.log(path.extname('/foo/bar/baz.js')); // .js
// parse() 方法用于将路径字符串解析为对象。
console.log(path.parse('/foo/bar/baz.js')); // { root: '/', dir: '/foo/bar', base: 'baz.js', ext: '.js', name: 'baz' }
http模块
http
与网络请求
有关,是浏览器
与服务器
之间的一个约定
!
网络基础 IP
IP地址
是每台设备
的唯一标识
,通过这个唯一标识
,就能在互联网
中和其它设备
进行通信
!
IP地址
192.168.1.1
这是一个十进制数字
,其实背后是32位2进制数字组成
,且每8位为一个字节
!
IP地址
分为局域网IP
和公网IP
!
局域网IP:
比如家庭环境
,通过路由
,将IP分配
给每一台设备
,这样在局部内的设备
可以进行文件传输通信
等!
公网IP:
当需要访问外网
时,需要通过厂商携带光纤宽带网线
等工具,进行安装
后便会生成一个公网的IP
,这时便可以对外网进行访问
!
网络基础 端口
端口
是每一个应用程序
的一个标识
,主要作用
是实现服务器
相互不同应用之间的通信
手段!
http协议
中的默认端口为80
,默认端口
,在输入ip地址
发送请求时,不需要书写端口号
,则默认为80端口
!
例如 端口9000 http://localhost:9000/
默认端口 http://localhost:80 则 80可以省去不写
http
协议
协议
就是一种约定关
系,需要互相遵守双方约定
,而这里的双方
就是浏览器
与服务器
之间的约定
!
http (Hypertext transfer protocol): 超文本传输协议!
http请求报文
http报文
,是浏览器
与服务器
之间传输
时所携带的一些文本信息
,包含请求行、请求头、请求体
等相关信息!
请求行
请求行
主要包含,请求方法、请求URL
还有HTTP版本号
组成!
请求方法
方法 | 描述 |
---|---|
Get |
用于获取 数据 |
Post |
用于新增 数据 |
Put/Patch |
用于修改 数据 |
Delete |
用于删除 数据 |
请求URL
请求地址: https://www.baidu.com/search?name=张三&age=18
组成部分: http协议、主机地址域名、端口号、查询参数!
http协议: https://
主机地址域名: www.baidu.com 也称之为统一资源定位符,最终会被解析为IP地址,获取主机的位置!
查询参数: ?name=张三&age=18,向服务器携带的一些额外参数!
search: 路径,表示这个服务器内部的资源!
http版本
http版本号
目前有,1.0 、 1.1、 2.0、 3.0!
请求头
请求头
,包含了浏览器的基本信息
,其内容都是以键值对儿
的形式保留
的!
例如:
请求的域名
,以及发送请求的平台信息
以及cookie
等相关信息!
请求体
请求体
,是向服务器
发送请求
时,所携带
的一些数据参数
,通常POST请求
会将数据通过请求体的方式
,向服务器传输数据
!
http响应报文
响应报文
,就是从服务端
向浏览器
回应的文本信息
,主要包含响应行、响应头、响应体
!
响应行
响应行,主要包含,
http版本号、状态码、描述
等信息!
状态码
状态码 | 描述 |
---|---|
200 |
请求成功 |
403 |
拒绝请求 |
404 |
访问资源不存在 |
500 |
服务器内部错误 |
响应头和响应体
响应头
响应头
,包括了响应内容
的描述信息
,例如,响应内容的格式
,以及内容长度
等信息!
content-type
为响应内容的格式
,content-length
为响应内容的大小
,单位为字节
!
响应体
响应体
根据响应(MIME)类型
来返回不同结果,
MIME类
型有很多种,包含文本,媒体,图片,json,html,css,js
等类型
格式!
创建http
服务
创建
http
服务,需要用到http模块
,引入后,调用createServer
方法来创建一个本地服务
!
const http = require('http');
创建
http服务
const server = http.createServer((request, response) => {
response.end("hello");
})
server.listen(9000, ()=> {
console.log("服务已启动 9000端口!")
})
listen
方法会给本地服务
添加一个端口
,当浏览器客户端
,访问本地服务加端口
时,就会调用服务内部的回调函数
,可以获取客户端
的请求报文信息
,并通过response
向客户端响应请求内容
!
response.end("hello") , end 代表结束请求,并向客户端返回响应内容 hello!
获取http
请求报文
在
createServer回调函数中,
回调函数的第一个形参request请求对象
,可以帮我们获取有关请求报文
的信息!
/*
获取请求报文信息
*/
const http = require("http");
const server = http.createServer((req, res) => {
res.setHeader("Content-Type", "text/plain;charset=utf-8");
res.end(`
请求方法: ${req.method}
请求路径: ${req.url}
请求头: ${JSON.stringify(req.headers, null, 2)}
请求参数: ${JSON.stringify(req.query)}
请求ip: ${req.ip}
请求地址: ${req.socket.remoteAddress}
请求端口: ${req.socket.remotePort}
`);
});
server.listen(9000, () => {
console.log("Server is running on port 9000");
});
获取
请求体
,当客户端
将参数
放入请求体
,向服务端
发送请求
时,服务端
需要通过data来监听数据的获取!
const http = require("http");
const server = http.createServer((req, res) => {
res.setHeader("Content-Type", "text/plain;charset=utf-8");
if (req.method === "POST") {
let body = "";
req.on("data", (chunk) => {
// chunk 是一个buffer数据
body += chunk.toString();
});
req.on("end", () => {
req.body = body;
res.end(`
POST请求成功
请求主体: ${req.body}
`);
});
return;
}
});
server.listen(9000, () => {
console.log("Server is running on port 9000");
});
获取url查询参数
使用
nodejs
中内置的url模块
来解析url参数
! 已经弃用,建议使用URL
对象!
/*
查询参数提取
*/
const http = require("http");
const url = require("url");
const server = http.createServer((req, res) => {
/*
@param {string} req.url 请求的url
@param {boolean} true 解析查询参数s
@return {object} url.parse(req.url, true).query 查询参数对象
*/
res.setHeader("Content-Type", "text/plain;charset=utf-8");
const urlObj = url.parse(req.url, true);
res.end(`查询参数提取
查询参数对象 ${JSON.stringify(urlObj.query)}
查询路径 ${urlObj.pathname}`);
});
server.listen(9000, () => {
console.log("http://localhost:9000");
});
这里用到了
url 模块
,url模块
主要用来处理路径
,可以获取路径名
以及查询参数
等!
使用
URL对象
,获取查询字符串
!
/*
查询参数提取
*/
const http = require("http");
const server = http.createServer((req, res) => {
res.setHeader("Content-Type", "text/plain;charset=utf-8");
// 第一个参数: /search?id=92389892183
// 第二个参数: http://localhost:9000
const urlObj = new URL(req.url, "http://localhost:9000");
res.end(`查询参数提取
查询参数对象 ${JSON.stringify(urlObj.searchParams.get("id"))}
查询路径 ${urlObj.pathname}`);
});
server.listen(9000, () => {
console.log("http://localhost:9000");
});
设置响应报文
设置响应状态码
设置响应状态码,通过
statusCode
属性来设置!response.statusCode = 404; // 200 201 500
设置响应头
设置
响应类型
,具体响应类型可查看MIME
相关内容!https://developer.mozilla.org/zh-CN/docs/Web/HTTP/MIME_types/Common_types
response.setHeader("content-type", "text/plain;charset=utf-8");
设置
自定义响应头
!
response.setHeader("myHeader", "test test");
设置
多个同样的响应头
response.setHeader("myHeader", ["a","b","c"])
设置响应体
设置
响应体
,有两种方法,write
和end
方法!当
write
和end
同时出现时,会将结果进行拼接返回响应内容!
而
write
可以调用多次
,且end
只能调用一次
!
response.write("响应内容!")
response.end("响应内容!")
静态资源和动态资源
静态资源:
一般文件内容不会变动
的资源,称为静态资源
!动态资源:
文件内容
根据场景需求经常发生内容变动
的情况,可称为动态资源
!
静态服务器搭建
静态资源服务
,根据路径返回不同路径
下的静态文件
内容!
目录
为page文件夹内的静态文件!
page > css > app.css
├── css
│ └── app.css
├── image
│ └── img.jpg
├── index.html
└── js
└── app.js
const http = require("http");
const fs = require("fs");
const path = require("path");
const server = http.createServer((req, res) => {
const filePath = path.join(__dirname, `/page/${req.url}`);
// console.log(req.url, filePath);
fs.readFile(filePath, (err, data) => {
if (err) {
res.writeHead(404, { "Content-Type": "text/plain;utf-8" });
res.end("Not Found");
return;
}
res.end(data);
});
});
server.listen(9000, () => {
console.log("Server is running at http://localhost:9000");
});
启动服务后
,访问http://localhost:9000/image/img.jpg
,则node服务
会根据/image/img.jpg 路径去查找返回文件内容!
网页URL之相对路径
代码
中书写的相对路径地址
,映射
到浏览器URL路径
的表现
!
http://localhost:9000/currentPath/index.html
相对路径 | URL路径 |
---|---|
./css/app.css |
http://localhost:9000/currentPath/css/app.css |
js/app.js |
http://localhost:9000/currentPath/js/app.js |
../img/image.jpg |
http://localhost:9000/img/image.jpg |
../../img/image.jpg |
http://localhost:9000/img/image.jpg |
网页中URL
场景使用
a
标签的href
属性link
标签的href
属性script
标签的src
属性img
标签的src
属性video audio
标签的src
属性
MIME类型
MIME类型
是一种响应各种类型
的标准和规范
,例如有js文件、css文件、html文件、媒体视频和图片相关文件
等!
text/javascript 、text/css 、text/html、image/png
MIME格式: [type]:[subtype]
常见的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
类型,当浏览器遇到此类型
,会将内容进行下载!
const http = require("http");
const fs = require("fs");
const path = require("path");
const MIME = {
html: "text/html",
css: "text/css",
js: "text/javascript",
png: "image/png",
jpeg: "image/jpeg",
gif: "image/gif",
mp4: "video/mp4",
mp3: "audio/mpeg",
json: "application/json",
};
const server = http.createServer((req, res) => {
const rootPath = "/page";
const filePath = path.join(
__dirname,
rootPath,
req.url === "/" ? "index.html" : req.url
);
// ".html".slice(1); "html"
const extname = path.extname(filePath).slice(1);
fs.readFile(filePath, (err, data) => {
if (err) {
console.log(err);
res.writeHead(404, { "Content-Type": "text/plain;utf-8" });
res.end("文件读取失败!");
return;
}
/*
如果是未知的资源,使用application/octet-stream作为类型返回,浏览器会根据该类型,对内容进行本地下载
*/
let mimeType =
(MIME[extname] && MIME[extname]) || "application/octet-stream";
res.writeHead(200, { "Content-Type": `${mimeType};utf-8` });
res.end(data);
});
});
server.listen(9000, () => {
console.log("Server is running at http://localhost:9000");
});
案例
登录和注册案例
根据
路径返回不同响应
,login
返回登录页
,register
返回注册页
,否则404
!
/*
get 请求示例
/login 时响应 登录页面
/register 时响应 注册页面
*/
const http = require("http");
const server = http.createServer((request, response) => {
const { method } = request;
const { pathname } = new URL(request.url, `http://${request.headers.host}`);
// 设置响应头 相应类型为 html
response.setHeader("Content-Type", "text/html;charset=utf-8");
if (method === "GET" && pathname === "/login") {
response.end("<h1>登录页面</h1>");
} else if (method === "GET" && pathname === "/register") {
response.end("<h1>注册页面</h1>");
} else {
response.end("<h1>404 Not Found</h1>");
}
});
server.listen("9000", () => {
console.log("Server running at http://localhost:9000");
});
响应4行3列的表格
const http = require("http");
const fs = require("fs");
const path = require("path");
function getHtmlContent(){
const templatePath = path.resolve(__dirname, "./template.html");
const html = fs.readFileSync( templatePath, "utf-8" );
return html;
}
const server = http.createServer((request, response) => {
// 设置响应类型 为html文本
response.setHeader("Content-type", "text/html;utf-8");
const html = getHtmlContent();
response.end(html);
});
server.listen("9000", ()=> {
console.log("server at http://localhost:9000 ")
})
响应html内部引入外部资源路径
html
文件解析过程
,内部遇到外部资源(.js .css img等)
时,会向服务发送请求
,获取对应资源
!
/*
响应html文件内部并引入外部资源路径
*/
const http = require("http");
const fs = require("fs");
const path = require("path");
const server = http.createServer((req, res) => {
const { pathname } = new URL(req.url, `http://${req.headers.host}`);
// html 文件路径
const filePath = path.join(__dirname, "./template.html");
// 读取 html 文件内容
const html = fs.readFileSync(filePath, "utf8");
if (pathname.endsWith(".css")) {
// 引入外部 css 文件
const cssfilePath = path.join(__dirname, "./index.css");
const css = fs.readFileSync(cssfilePath, "utf8");
res.end(css);
} else if (pathname === "/") {
res.end(html);
} else {
res.statusCode = 404;
res.end("Not Found");
}
// res.end(html);
// 这里不能直接返回html,当html文件内部引入外部资源(.js .css img等)html解析时,会想服务发送请求获取资源,这里如果直接响应html时,则结果是不对的
});
server.listen(9000, () => {
console.log("Server is running on port 9000");
});
模块化
模块化
是一种规范
,通常将一个比较臃肿的文件模块
,根据业务类型拆分成多个子模块
!
模块
之间代码
都是私有独立
的,可以通过特定的语法
,将模块里
的数据
进行暴露和导出
,以便提供给其它所依赖的模块使用
!
模块化的好处
:
- 对
大型文件模块
的拆分
,根据业务类型拆分
成多个子模块
,可读可维护
好! - 每个
模块之间的代码
都是独立
的,避免变量命名冲突污染
的问题! - 每个
模块
之间可以将重复的代码块抽离
出一个子模块
,供其它模块
使用,可复用代码逻辑
减少代码量的开发!
模块暴露
通过
module.exports
可以实现模块中单个功能变量的暴漏
,也可以对模块中多个变量进行统一暴漏
!
单个功能
模块暴漏:utils.js
function add(a, b) { return a + b; } module.exports = add;
- 这里将
utils.js
中的add方法
暴漏出去! index.js
- 使用
require
方法来对模块的引用!
const add = require("./utils.js"); let result = add(12,20); console.log("result", reuslt);
多个功能
模块进行统一暴漏
;utils.js
function add(a, b) { return a + b; } function minus(a, b) { return a - b; } module.exports = { add, minus };
index.js
// 这里获取的是个对象,只不过这里用到了对象结构 const { add, minus } = require("./utils.js"); let addResult = add(15, 20); let minusResult = minus(20, 15); console.log(addResult, minusResult); // 35
exports注意点
使用
module.exports
对某个值
进行暴漏
时需要注意的问题
,就是不能直接使用exports = "value"
作为一个值的暴漏!
exports = "value";
exports
是一个对象
,它的值等同于module.exports
,而module.exports
的值默认
是一个空的对象
,且它们的关系就是共同指向
了引用地址
的对象
!
exports === module.exports // true
因此直接通过
exports = "value"时
,通过require
获取到的便是一个"{}"空对象
,原因,导出的值,是默认将module.exports
中的值向外进行暴露
的,这里我们只是改的exports
,所以,module.exports
还是空对象
!
总而言之,就是
两个变量共同引用了同一个对象的引用地址
,其中向外暴漏的值
,是module.exports中的值
!
exports.value = "absd";
这样是可以的,因为这里是向
空对象
中添加
了一个属性为value的值
!既然
exports
和module.exports
的对象引用地址是共用
的,那么同样module.exports
中的对象
也会存储一个叫value属性
的值!
模块导入
导入模块
是通过require
函数来实现的,可接受一个文件路径名
,是一个相对路径
!
const module = require("./test.js")
这里导入了
test.js
模块!
导入模块的注意事项
引入路径中的
./
和../
不能省略,否则会找不到模块!以
.js
和.json
为后缀
的文件
可以省略!导入
其它文件格式的文件
,会以js形式
去处理文件
内容!如果
.js
文件和.json
文件名一样,则优先导入js模块
!如果
require
中引入的是一个文件夹,而不是文件时!const module = require("./module")
- 去
module文件夹
内部查找package.json
中的main
属性是否有提供文件模块路径
,如果没有,则会找index.js
或者是index.json
,否则就会报错
! - 这里
文件查找规则
,在包管理工具
时会用到,导入
的第三方模块
,都是以文件夹方式去导入
的!
- 去
require
除了能够引入,自定义模块文件
,也可以用在引入第三方包管理的相关模块
!
自定义模块导入流程
自定模块导入流程
,通常会调用require("./xxx.js")函数
传递一个相对路径的模块文件
,最后返回一个module.exports对象!
具体流程如下
- 将
相对路径的文件
路径,转换为绝对路径
! - 判断
缓存中
是否有当前模块所对应的缓存值
,如果有直接返回缓存中的值
! - 如果
缓存中没
有,就会读取文件模块中的代码
,并包裹成函数
执行(自执行函数)
! - 缓存
返回值
! - 返回
module.exports
的值!
const path = require("path");
const fs = require("fs");
function myRequire( filePath ) {
// 1. 解析文件路径转换为绝对路径
const absoltePath = path.resolve(__dirname,filePath);
// 2. 判断缓存中的文件是否存在
if (caches[absoltePath]){
return caches[absoltePath];
}
// 3. 读取文件内容
const content = fs.readFileSync(absoltePath,"utf-8");
// 4. 解析文件内容,生成模块对象
const module = {
exports: {}
};
// 5. 执行模块代码,将模块的 exports 属性指向 module.exports
const fn = new Function("exports",content);
fn(module.exports);
// 6. 缓存模块对象
caches[absoltePath] = module.exports;
}
const module = myRequire("./module.js");
包管理工具
包管理工具(
npm
),全称(node package manager
),是一个第三方资源仓库
,可以根据需求,通过npm
来安装
这些第三方资源包
!
初始化项目
npm init
init
命令可以帮我们以命令交互方式来创建一个node项目
,过程会提示,项目的名称、描述
、以及入口文件
等相关配置,最终都会以package.json
的形式来表示!
package.json
是包管理配置文件
,不仅有项目的基本配置
,还有安装的第三方依赖包等相关配置
!
➜ npm-init npm init
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.
See `npm help init` for definitive documentation on these fields
and exactly what they do.
Use `npm install <pkg>` afterwards to install a package and
save it as a dependency in the package.json file.
Press ^C at any time to quit.
package name: (npm-init)
version: (1.0.0)
description: npm 学习
entry point: (index.js)
test command:
git repository:
keywords:
author:
license: (ISC)
About to write to /Users/miaojiangwei/Study/FrontEnd/Node/code/包管理工具/npm-init/package.json:
{
"name": "npm-init",
"version": "1.0.0",
"description": "npm 学习",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC"
}
Is this OK? (yes)
{
"name": "npm-init", // 包的名称
"version": "1.0.0", // 包的版本
"description": "npm 学习", // 项目描述
"main": "index.js", // 入口文件
"scripts": { // 脚本配置
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "", // 作者
"license": "ISC" // ISC开源证书
}
注意事项
package name:
不能是大写,也不能是中文名
,不写默认为当前目录下的文件夹名
!version:
版本号, 书写形式为x.x.x
必须为数字1.0.0
!
通过npm init -y
可以快速创建一个node项目
,减少交互提示行为
!
搜索包
通过命令的方式
搜索包信息:
npm s calendar
通过
s(search)
指令,根据关键字
,查询对应的第三方包信息
!
➜ ~ npm s calendar
NAME | DESCRIPTION | AUTHOR | DATE | VERSION | KEYWORDS
calendar | calendar generator | =ramalho | 2019-11-04 | 0.1.1 |
rc-calendar | React Calendar | =yiminghe… | 2020-06-06 | 9.15.11 | react react-calendar r
rmc-calendar | React Mobile… | =silentcloud… | 2018-06-27 | 1.1.4 |
js-calendar | JavaScript calendar… | =sergiocrisost… | 2017-12-27 | 1.2.3 |
v-calendar | A clean and… | =nathanreyes | 2023-10-13 | 2.4.2 | vue vuejs plugin calen
react-calendar | Ultimate calendar… | =wojtekmaj… | 2024-10-23 | 5.1.0 | calendar date date-pic
react-big-calendar | Calendar! with… | =monastic.panic… | 2024-12-19 | 1.17.1 | scheduler react-compo
vue-calendar-component | 基于vue2.0的一款日… | =zwhgithub | 2019-02-18 | 2.8.2 | vue-calendar calendar vue-da
@react-types/calendar | Spectrum UI… | =devongovett… | 2024-11-21 | 3.5.0 |
@nextui-org/calendar | A calendar displays… | =juniorgarciad… | 2024-12-24 | 2.2.8 | calendar
@react-stately/calendar | Spectrum UI… | =devongovett… | 2024-11-21 | 3.6.0 |
d3-time | A calculator for… | =mbostock… | 2022-12-02 | 3.1.0 | d3 d3-module time inte
@react-aria/calendar | Spectrum UI… | =devongovett… | 2024-11-21 | 3.6.0 |
react-native-calendars | React Native… | =wix.mobile… | 2024-09-18 | 1.1307.0 |
boom-calendar | Powerful and… | =azaryan | 2024-10-30 | 1.6.20 |
tui-calendar | TOAST UI Calendar | =nhnent | 2022-02-17 | 1.15.3 | nhn nhnent toast tui c
vue-full-calendar | FullCalendar… | =brockreece | 2020-06-21 | 2.8.1-0 | vue fullcalendar calen
@fullcalendar/core | FullCalendar core… | =arshaw | 2024-07-12 | 6.1.15 | calendar event full-si
@types/react-big-calendar | TypeScript… | =types | 2024-11-22 | 1.16.0 |
angular-calendar | A calendar… | =mattlewis92 | 2024-04-19 | 0.31.1 | angular angular2 calen
在网页中使用官方地址进行搜索
下载和安装包
npm install uniq
通过
install
命令,后面跟一个包的名称
,即可下载安装
!
npm i uniq
也可以使
用简写方式
!
安装成功后,会多出一个
文件夹node_modules
,文件夹内部
便是安装后存放的依赖文件包
!同时也会新增一个
文件package-lock.json
,主要用来锁定包版本信息的!并且在
package.json内部
,会增加安装依赖包的配置项
!
{
"dependencies": {
"uniq": "^1.0.1"
}
}
在代码中使用
uniq依赖包
插件!
index.js
const uniq = require("uniq");
let arr = [1, 2, 3, 2, 4, 5, 3, 6];
let result = uniq(arr);
console.log(result); // [1, 2, 3, 4, 5, 6]
require引入依赖包流程
const uniq = require("uniq");
// const uniq = require("./node_modules/uniq/uniq.js");
基本流程是,找到
当前目录
下的node_modules
下的uniq文件夹
,找到后,看package.json
中main属性
对应的入口文件
,如果没有
则向上级文件夹
,看上层的node_modules
有没有,直到找到磁盘根目录
!
开发依赖和生产依赖
开发依赖和生产依赖
,是指的是在开发环境
下所使用的依赖包
,而生产依赖
,指的是构建发布后线上环境
所使用的依赖包
!
开发环境
依赖包安装:npm install uniq --save-dev npm i uniq -D # 简写形式
- 安装完后在
package.json
会增加devDependencies
属性对象,其内部包含了开发环境
相关的依赖!
- 安装完后在
生产环境
依赖包安装:npm install uniq --save npm i uniq -S # 简写形式
- 安装完后
package.json
会增加dependencies
属性对象,其内部包含了生产线上环境
的依赖!
- 安装完后
全局安装
npm i -g nodemon
使用
-g
命令后面跟一个依赖包名
可进行全局安装
,一般全局安装
的工具都是可通过命令
来进行一系列操作
!
nodemon
是一个插件工具
,在使用http服务
时,每次改动
都需要手动重新启动服务
,使用nodemon
工具可以避免这个问题 ,实现文件内容改变
时,可以自动重启
服务!
npm root -g
以上命令,可以
查看全局安装的目录位置
!
指定版本安装
npm i jquery@1.11.2
删除依赖
npm r jquery
# npm remove jquery
# npm uninstall jquery
配置脚本简化命令操作
在
package.json
中,有一个属性名为scripts,
该属性,可以配置一些别名
,将比较复杂的命令
,通过script
别名配置后,来进行简化操作!
{
"name": "npm-init",
"version": "1.0.0",
"description": "npm 学习",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node index.js",
"serve": "node index.js"
},
"author": "",
"license": "ISC",
"dependencies": {
"uniq": "^1.0.1"
}
}
在
命令
行中使用时,不需要node index.js
来启动服务
,只需要运行npm run serve
或者是npm run start
即可!
npm run
有自动向上查找
的特性,当前文件夹
所执行的文件不存在
时,会向上查找匹配文件
的特性!
npm run start
其中run
可以省略npm start
,一般用于启动项目
!
yarn
包管理工具
yarn
是一个包管理工具
,其特点下载速度快
,对包的完整性检测比较好
!
全局安装yarn
npm install -g yarn
yarn
常用命令
命令 | 描述 |
---|---|
初始化 |
yarn init / yarn init -y |
安装包 |
yarn add uniq 生产环境 / yarn add uniq --dev 开发环境 |
全局安装 |
yarn global add nodemon |
删除依赖 |
yarn remove uniq / yarn global remove nodemon 全局依赖删除 |
发布包
发布包,包的名称不能带有test等字眼,
npm会有垃圾检测
,否则无法发布!