NodeJs笔记
本文最后更新于 2025-01-05,文章内容可能已经过时。
概念
NodeJs是一个开源跨平台的javascript 运行环境,可以运行Javascript代码,Nodejs中内置了V8引擎!
NodeJs可以帮我们实现,web服务器、开发工具、桌面端应用开发!
1. web服务器:服务器就是计算机电脑,通过服务应用程序在计算机中部署安装后便可部署web应用!
2. 开发工具:比如目前的webpackvite以及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.jsconst 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 yarnyarn常用命令
| 命令 | 描述 |
|---|---|
初始化 |
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会有垃圾检测,否则无法发布!




