global
浏览器中全局对象window
node 中的全局对象 global
process
process进程管理相关
// 1.node命令行参数
// node test2.js 1 2
// [
// 'C:\\Program Files\\nodejs\\node.exe',
// 'D:\\vue_project\\learn_node\\test2.js',
// '1',
// '2'
// ]
console.log(process.argv);
// 2. 用户环境
// 设置和查看当前环境
process.env.NODE_ENV = 'dev'
console.log(process.env.NODE_ENV);
// 3.当前进程的工作目录
console.log(process.cwd());
// 4.屏幕输入输出
process.stdout.write("输入文字:")
// process监听data事件
process.stdin.on("data", (res) => {
console.log(res.toString());
// 5.退出当前进程
process.exit()
})
// 5.内存使用情况
console.log(process.memoryUsage());
util
util.callbackify()
将返回值为promise的函数改为回调
const util = require('util')
function foo() {
return new Promise((resolve) => {
setTimeout(() => {
resolve(666)
}, 500)
})
}
// 将返回值为promise的函数改为回调
const callbackFoo = util.callbackify(foo)
// node中所有异步操作都是通过回调函数获取结果
// 回调函数的第一个参数err,第二个参数res
callbackFoo(function (err, res) {
console.log(res);
})
// 打印:666
util.promisify()
将异步回调的函数改为promise
const util = require('util')
const fs = require('fs')
// node中所有异步操作都是通过回调函数获取结果
// 回调函数的第一个参数err
// fs.readFile('./a.txt',(err,res)=>{
// console.log(res);
// })
// 将异步的回调函数转化为promise形式
const promiseReadFile = util.promisify(fs.readFile)
promiseReadFile('./a.txt').then(res => {
console.log(res.toString());
})
fs
文件操作
- 文件方法都为异步操作,都有对应的同步版本,函数名+Sync
- 异步函数的回调函数,第一个参数为err,第二个参数为data
- 都有fs.promises.xxx形式
const fs = require('fs')
// 1.写入
fs.writeFileSync('./a.txt', '123')
// 2.追加
fs.appendFileSync('./a.txt', '\n456')
// 3.读取
fs.readFile('./a.txt', (err, res) => {
console.log(res.toString());
})
// 4.剪切
fs.rename('./a.txt', './dir/a.txt', (err) => { })
// 重名名
// fs.rename('./dir/a.txt', './dir/aaaa.txt', (err) => { })
// 5.拷贝
fs.copyFileSync('./test.js', './test_copy.js')
// 6.删除
fs.rm("./a_copy2.txt", (err) => {});
文件夹操作
- 文件方法都为异步操作,都有对应的同步版本,函数名+Sync
- 异步函数的回调函数,第一个参数为err,第二个参数为data
- 都有fs.promises.xxx形式
const fs = require('fs')
// 1.读取
fs.readdir('./', (err, res) => {
console.log(res);
})
// 2.创建文件夹
fs.mkdir("./B/BB/b.txt", { recursive: true }, (err) => {});
fs.mkdirSync("./C/CC/c.txt", { recursive: true });
// 3.删除空文件夹,不能删除有内容的文件夹
fs.rmSync("./A/AA", { recursive: true });
// 4.文件或文件夹信息
// 判断文件或文件夹
const state = fs.statSync('./A/a.txt')
console.log(state);
console.log(state.isDirectory());
console.log(state.isFile());
// 5.文件或文件夹是否存在
console.log(fs.existsSync('./A/b.txt'));
递归遍历文件夹
function handleDirection(path) {
fs.readdir(path, { withFileTypes: true }, (err, files) => {
if (err) {
console.log(err);
return;
}
files.forEach((item) => {
if (item.isDirectory()) {
console.log("文件夹:", item.name);
handleDirection(`${path}/${item.name}`);
} else {
console.log("文件:", item.name);
}
});
});
}
handleDirection("./A");
案例1:清除文件夹中所有的文件
const fs = require('fs')
const path = require('path')
function clearDir(dirPath) {
// 文件夹存在
if (fs.existsSync(dirPath)) {
// 获取文件夹信息
const dirArray = fs.readdirSync(dirPath)
dirArray.forEach((item) => {
// 拼接路径,判断是文件还是文件夹
let fullPath = path.join(dirPath, item)
const state = fs.statSync(fullPath)
if (state.isDirectory()) {
clearDir(fullPath)
} else {
fs.unlinkSync(fullPath)
}
})
}
}
clearDir('./A')
案例2:处理数据后写入新文件
const fs = require('fs')
// 1.读文件
fs.readFile(path.join(__dirname,'成绩.txt'), 'utf8', function (err, dataStr) {
if (err) throw err
// 处理数据
let data = dataStr.replaceAll('=', ':').split(' ').join('\n')
console.log(data);
// 2.写文件
fs.writeFile('./成绩2.txt', data, (err) => {
if (err) throw err;
console.log('The file has been saved!');
});
})
//promise方式
//fs.promises.readfile()
成绩1.txt
小红=99 小白=100 小黄=70 小黑=66 小绿=88
成绩2.txt
小红:99
小白:100
小黄:70
小黑:66
小绿:88
案例3 封装readDir函数,能递归遍历所有文件夹,每个文件/文件夹包装为一个对象
const fs = require("fs");
const path = require("path");
class File {
constructor(filename, name, ext, isFile, size, createTime, updateTime) {
this.filename = filename;
this.name = name;
this.ext = ext;
this.isFile = isFile;
this.size = size;
this.createTime = createTime;
this.updateTime = updateTime;
}
// 读取文件内容
async getContent(isBuffer = false) {
if (this.isFile) {
if (isBuffer) {
return await fs.promises.readFile(this.filename);
} else {
return await fs.promises.readFile(this.filename, "utf-8");
}
}
return null;
}
// 文件夹的所有子文件对象
async getChildren() {
if (this.isFile) {
return [];
}
let children = await fs.promises.readdir(this.filename);
children = children.map(name => {
const result = path.resolve(this.filename, name);
return File.getFile(result);
});
return Promise.all(children);
}
// 每个文件/文件夹 返回为一个对象
static async getFile(filename) {
const stat = await fs.promises.stat(filename);
const name = path.basename(filename);
const ext = path.extname(filename);
const isFile = stat.isFile();
const size = stat.size;
const createTime = new Date(stat.birthtime);
const updateTime = new Date(stat.mtime);
return new File(filename, name, ext, isFile, size, createTime, updateTime);
}
}
async function readDir(dirname) {
const file = await File.getFile(dirname);
return await file.getChildren();
}
module.exports = {
readDir
}
fs-extra
fs增强版,能处理文件、文件夹的增删改查
npm i fs-extra
compressing
npm i compressing
const fs = require('fs-extra')
const path = require('path')
const compressing = require('compressing')
// 压缩文件夹
compressing.zip.compressDir(path.join(__dirname, './A.zip'), './A.zip').then((res) => {
console.log('压缩完成');
})
// 解压
compressing.zip.uncompress(path.join(__dirname, './A.zip'), './A2').then(res => {
console.log('解压完成');
}).catch(err => {
console.log(err);
})
path
node中文件的相对路径与绝对路径
相对路径:相对于命令行的工作目录
绝对路径(__dirname):当前文件所在目录的绝对路径
URL的相对路径和绝对路径
绝对路径:
相对路径:
路径拼接
const path = require('path')
const pathStr1 = path.join('/a','/b/c','../','./d')
console.log(pathStr1);
//结果:\a\b\d
//拼接为绝对路径,从右向左拼接,拼接出绝对路径为止
const pathStr2 = path.resolve()
console.log(pathStr2);
//结果:D:\vue_project\learn_webpack
const pathStr3 = path.resolve('/a','./b/c','../')
console.log(pathStr3);
//结果:D:\a\b
const pathStr4 = path.resolve('./a','./b/c','../')
console.log(pathStr4);
//结果:D:\vue_project\learn_webpack\a\b
__dirname 当前所在文件的目录的绝对路径
__filename 当前所在文件的绝对路径
解析路径
let path = require('path')
let demo = "./src/a/b/c/test.js"
// 1.文件名
// test.js
console.log(path.basename(demo));
// 2.目录名
// ./src/a/b/c
console.log(path.dirname(demo));
// 3.将路径解析为对象
// {
// root: '',
// dir: './src/a/b/c',
// base: 'test.js',
// ext: '.js',
// name: 'test'
// }
console.log(path.parse(demo));
// 4.将对象解析为路径
// ./src/a/b/c\test.js
console.log(path.format({
root: '',
dir: './src/a/b/c',
base: 'test.js',
ext: '.js',
name: 'test'
}));
总结
events
events全局事件总线
const EventEmitter = require("events");
const emitter = new EventEmitter();
// 1. 监听e1事件,接收参数
// emitter.on("e1", (name,age) => {
// console.log(name);
// console.log(age);
// });
function listenerE1(name,age){
console.log(name);
console.log(age);
}
emitter.on("e1", listenerE1);
// 2. 抛出事件,传递参数
emitter.emit("e1", "CCC", 999);
// 3. 取消事件监听
emitter.off('e1',listenerE1)
Buffer
将数据保存为二进制,用16进制表示
buffer相当于固定长度的字节数组,每一项为一个字节
buffer可以使用字符串的方法
//创建buffer(编码)
const bf2 = Buffer.from("hello")
//创建3字节的空间
const bf1 = Buffer.alloc(10,'1')
//打印buffer结果
//<Buffer 00 00 00>
console.log(bf1);
//buffer转原始数据(解码)
//1111111111
console.log(bf1.toString());
//true
console.log(Buffer.isBuffer(bf1));
stream
概念
流:表现为连续的字节
所有的流都是EventEmitter的实例,故流都能事件监听
流的类型:
可读流
const fs = require('fs')
// 创建可读流
const readStream = fs.createReadStream('./a.txt', {
// 下标0开始 [3,4]之间的buffer数组
start: 3,
end: 4,
// 一次性读取5个字节
highWaterMark: 5
})
// 监听data事件(因为流是EventEmitter的实例)
// 每读5个字节,回调一次
readStream.on('data', (res) => {
console.log(res.toString());
// 暂停
readStream.pause()
// 2s后继续
setTimeout(() => {
readStream.resume()
}, 2000);
})
监听其他事件
曹操123abc
const fs = require("fs");
// 创建可读流
const readStream = fs.createReadStream("./a.txt", {
// 下标0开始 [3,4]之间的buffer数组
start: 6,
end: 8,
// 一次性读取2个字节
highWaterMark: 2,
});
readStream.on("open", (fd) => {
console.log("文件描述符:", fd);
});
// 监听data事件(因为流是EventEmitter的实例)
// 每读2个字节,回调一次
readStream.on("data", (res) => {
console.log(res.toString());
});
readStream.on("end", () => {
console.log("读取完成");
});
readStream.on("close", () => {
console.log("文件关闭");
});
//打印结果:
文件描述符: 3
12
3
读取完成
文件关闭
可写流
const fs = require('fs')
const writeStream = fs.createWriteStream('./a.txt',{
flags: 'r+',
start: 6
})
writeStream.write('1')
writeStream.write('2',()=>{
console.log('2写入完成');
})
// 将'end!!'写入完成后,自动关闭关闭文件
// 手动关闭文件 writeStream.close()
writeStream.end('end!!')
// 监听文件关闭
// 注:close能监听到手动关闭的文件
writeStream.on('close',()=>{
console.log('close:文件关闭');
})
writeStream.on('finish',()=>{
console.log('finish:文件关闭');
})
pipe管道
pipe能将可读流和可写流拼接
案例:文件复制
方式1 一次性读写
const data = fs.readFileSync('./a.txt')
fs.writeFileSync('./a_copy2.txt',data)
方式2 可读可写流
const fs = require('fs')
const readStream = fs.createReadStream('./a.txt')
const writeStream = fs.createWriteStream('./a_copy.txt')
readStream.on('data',(data)=>{
writeStream.write(data)
})
readStream.on('end',()=>{
console.log('读取文件a.txt完成');
// 手动关闭a_copy.txt
writeStream.close()
})
方式3 pipe函数
- 可解决背压问题
// 实现文件复制
const fs = require('fs')
const readStream = fs.createReadStream('./a.txt')
const writeStream = fs.createWriteStream('./a_copy.txt')
// 从可读流流向可写流
readStream.pipe(writeStream)
网络协议
网络协议共同特点;
- 都有客户端和服务端
- 大多数操作以流的形式
http
IP分类
端口被占用
- 打开资源监视器,找到端口对应的PID
- 打开任务管理器,根据刚刚的PID结束对应的进程
请求报文
获取请求报文
响应报文
响应状态码
服务端
服务端:根据不同网址显示不同页面内容
-
服务端接收到请求req,故req是可读流
-
服务端返回响应res,故res是可写流
1.根据不同路径显示不同页面
const http = require('http')
const url = require('url')
const fs = require('fs')
const serve = http.createServer()
// 服务端中,req为可读流,res为写入流
serve.on("request", (req, res) => {
// 解析url
const urlObj = url.parse(req.url, true)
// 根据不同网址显示不同页面
if (urlObj.pathname === '/page1') {
res.write("hi ")
res.end("page1")
}
if (urlObj.pathname === '/page2') {
res.write("hi ")
res.end("page2")
}
if (urlObj.pathname === '/htmlPage') {
// 创建可读流,避免html文件过大,影响响应速度
const _html = fs.createReadStream('./test.html')
_html.on('data', (chunk) => {
res.write(chunk)
})
_html.on('end', () => {
res.end()
})
}
if (urlObj.pathname === '/test.css') {
// 创建可读流,避免css文件过大,影响响应速度
const _css = fs.createReadStream('./test.css')
_css.on('data', (chunk) => {
res.write(chunk)
})
_css.on('end', () => {
res.end()
})
}
})
// 主机host默认为 0.0.0.0 能监听IPV4所有地址
// 127.0.0.1(localhost) 本地回环地址
serve.listen(3000)
2.根据不同请求方法、路径,返回不同数据(即api接口)
const http = require("http");
const url = require("url");
const server = http.createServer((req, res) => {
// 解析URL
const urlObj = url.parse(req.url, true);
// 1.get的api接口
if (urlObj.pathname === "/api1" && req.method === "GET") {
// 将解析出来的query参数返回
res.end(
JSON.stringify({
data: [1, 2, 3],
user: urlObj.query,
})
);
}
// 2.post的api接口
else if (urlObj.pathname === "/api2" && req.method === "POST") {
// post携带的参数在body中,通过流读取
let postParam = "";
req.on("data", (chunk) => {
postParam += chunk;
});
req.on("end", () => {
res.end(
JSON.stringify({
data: [4, 5, 6],
postParam: JSON.parse(postParam),
})
);
});
} else {
res.statusCode = 400;
res.end(
JSON.stringify({
msg: "400 客户端错误",
})
);
}
});
server.listen(3000, "localhost", () => {
console.log("服务器地址:", "http://localhost:3000");
});
客户端
客户端:客户端请求服务端接口
- 客户端接收到响应res,故res是可读流
- 客户端请求服务器,故req是可写流
const http = require("http");
// 1.客户端发送get请求
http.get("http://localhost:3000/api1?name=cjc&age=100", (res) => {
let data = "";
res.on("data", (chunk) => {
data += chunk;
});
res.on("end", () => {
console.log(JSON.parse(data));
});
});
// 2.客户端发起post请求
const req = http.request(
{
method: "POST",
hostname: "localhost",
port: "3000",
path: "/api2",
},
(res) => {
let data = "";
res.on("data", (chunk) => {
data += chunk;
});
res.on("end", () => {
console.log(JSON.parse(data));
});
}
);
// post请求,请求体body中携带参数
req.write(
JSON.stringify({
a: 1,
b: 2,
c: 3,
})
);
req.end();
使用axios发送请求
const { default: axios } = require("axios");
axios
.get("http://localhost:3000/api1?name=cjc&age=100")
.then((res) => {
console.log(res.data);
})
.catch((err) => {
console.log(err);
});
axios
.post("http://localhost:3000/api2", JSON.stringify({a:1,b:2,c:3}))
.then((res) => {
console.log(res.data);
})
.catch((err) => {
console.log(err);
});
postman工具
浏览器请求一个网址时,会发送两次请求,为避免干扰,可以使用postman工具
https
https对比于http服务,多了加密的过程
服务器传来的通过公钥加密,本地客户端有私钥,用来解密
生成本地证书
- 生成的本地证书,不会被信任
npm i -g mkcert
// 查看安装完成
mkcert --help
// 生成相关证书
mkcert create-ca
mkcert create-cert
服务端
const https = require('https')
const fs = require('fs')
const path = require('path')
const options = {
// 私钥
key: fs.readFileSync(path.resolve(__dirname, "./cert.key")),
// 由私钥生成的证书
cert: fs.readFileSync(path.resolve(__dirname, "./cert.crt"))
}
const server = https.createServer(options, (req, res) => {
res.end("https server")
})
server.listen(4000,()=>{
console.log('https://localhost:4000');
})
websocket
npm i ws -S
服务端
const ws = require('ws')
const webSocketServer = ws.Server
const wss = new webSocketServer({ port: 4000 })
wss.on('connection', (wsconnect) => {
wsconnect.on('message', (msg, err) => {
// 在终端中打印信息
console.log('Server: 接收到客户端的消息'+msg);
// 响应给客户端的信息
wsconnect.send("Server: hi", () => { })
})
})
客户端
- 以浏览器为例
<!DOCTYPE html>
<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>
</head>
<body>
<script>
const ws = new WebSocket("ws://localhost:4000/")
ws.onopen = function () {
console.log('connect');
}
ws.onmessage = function (e) {
console.log(e.data);
}
</script>
</body>
</html>
客户端发送消息给服务器:
ws.send("test")