Node架构
Natives Modles
- 当前层由JS实现
- 提供应用程序可直接调用库,例如fs,path,http等
- JS语言无法直接操作底层硬件设置,需要bingings来充当桥梁
Bingings
由C或者C++代码实现,它可以帮助我们调用具体的C++函数,但是函数的代码不在这一层
具体功能模块
- V8,执行JS代码,将JS代码转化成C
- libuv,事件循环,事件队列,异步IO
- 其他第三方模块
图示
Node 特性
轻量级,更适用于IO密集型高并发请求
path
内置模块,require之后直接使用。 用于处理文件/目录的路径。
常见API
- basename 获取路径中基础名称
- dirname 获取路径中的目录名称
- extname 获取路径中的扩展名
- isAbsolute 获取路径是否为绝对路径
- join 拼接多个路径片段
- resolve 返回绝对路径
- parse 解析路径
- format 序列化路径
- normalize 规范化路径
const path = require('path')
console.log(__filename)
// console.log(path.basename(__filename))
// console.log(path.dirname(__filename))
// console.log(path.extname(__filename))
// console.log(path.isAbsolute(__filename))
// console.log(path.join('a/b', 'c', 'index.html'))
// console.log(path.resolve())
// const obj = path.parse('./a/b/c/')
// console.log(path.format(obj))
// console.log(path.normalize('a///b/c../d'))
// 1 获取路径中的基础名称
/**
* 01 返回的就是接收路径当中的最后一部分
* 02 第二个参数表示扩展名,如果说没有设置则返回完整的文件名称带后缀
* 03 第二个参数做为后缀时,如果没有在当前路径中被匹配到,那么就会忽略
* 04 处理目录路径的时候如果说,结尾处有路径分割符,则也会被忽略掉
*/
/* console.log(path.basename(__filename))
console.log(path.basename(__filename, '.js'))
console.log(path.basename(__filename, '.css'))
console.log(path.basename('/a/b/c'))
console.log(path.basename('/a/b/c/')) */
// 2 获取路径目录名 (路径)
/**
* 01 返回路径中最后一个部分的上一层目录所在路径
*/
/* console.log(path.dirname(__filename))
console.log(path.dirname('/a/b/c'))
console.log(path.dirname('/a/b/c/')) */
// 3 获取路径的扩展名
/**
* 01 返回 path路径中相应文件的后缀名
* 02 如果 path 路径当中存在多个点,它匹配的是最后一个点,到结尾的内容
*/
/* console.log(path.extname(__filename))
console.log(path.extname('/a/b'))
console.log(path.extname('/a/b/index.html.js.css'))
console.log(path.extname('/a/b/index.html.js.')) */
// 4 解析路径
/**
* 01 接收一个路径,返回一个对象,包含不同的信息
* 02 root dir base ext name
*/
// const obj = path.parse('/a/b/c/index.html')
// const obj = path.parse('/a/b/c/')
/* const obj = path.parse('./a/b/c/')
console.log(obj.name) */
// 5 序列化路径
/* const obj = path.parse('./a/b/c/')
console.log(path.format(obj)) */
// 6 判断当前路径是否为绝对
/* console.log(path.isAbsolute('foo'))
console.log(path.isAbsolute('/foo'))
console.log(path.isAbsolute('///foo'))
console.log(path.isAbsolute(''))
console.log(path.isAbsolute('.'))
console.log(path.isAbsolute('../bar')) */
// 7 拼接路径
/* console.log(path.join('a/b', 'c', 'index.html'))
console.log(path.join('/a/b', 'c', 'index.html'))
console.log(path.join('/a/b', 'c', '../', 'index.html'))
console.log(path.join('/a/b', 'c', './', 'index.html'))
console.log(path.join('/a/b', 'c', '', 'index.html'))
console.log(path.join('')) */
// 8 规范化路径
/* console.log(path.normalize(''))
console.log(path.normalize('a/b/c/d'))
console.log(path.normalize('a///b/c../d'))
console.log(path.normalize('a//\\/b/c\\/d'))
console.log(path.normalize('a//\b/c\\/d')) */
// 9 绝对路径
// console.log(path.resolve())
/**
* resolve([from], to)
*/
// console.log(path.resolve('/a', '../b'))
// console.log(path.resolve('index.html'))
buffer
- 无须require的一个全局变量,可以直接使用
- Buffer让JS可以操作二进制
- Node中Buffer是一片内存空间,不占用V8堆内存大小,占用的是C++内存。
- 内存的使用由node来控制,由V8的GC回收。
- 一般配合Stream流使用,充当数据缓冲区。
创建Buffer实例
- alloc 创建指定字节大小的buffer
- allocUnsafe 创建指定大小的buffer 不安全
- from 接收数据,创建buffer
/* const b1 = Buffer.alloc(10)
const b2 = Buffer.allocUnsafe(10)
console.log(b1)
console.log(b2) */
// from
/* const b1 = Buffer.from('中')
console.log(b1) */
// const b1 = Buffer.from([0xe4, 0xb8, 0xad])
/* const b1 = Buffer.from([0x60, 0b1001, 12])
console.log(b1)
console.log(b1.toString()) */
/* const b1 = Buffer.from('中')
console.log(b1)
console.log(b1.toString()) */
const b1 = Buffer.alloc(3)
const b2 = Buffer.from(b1)
console.log(b1)
console.log(b2)
b1[0] = 1
console.log(b1)
console.log(b2)
buffer 实例方法
- fill: 使用数据填充buffer
- write 向buffer中写入数据
- toString 从buffer中提取数据
- slice 截取buffer
- indexOf 在buffer中查找数据
- copy 拷贝buffer中的数据
let buf = Buffer.alloc(6)
// fill
/* buf.fill(123)
console.log(buf)
console.log(buf.toString()) */
// write
/* buf.write('123', 1, 4)
console.log(buf)
console.log(buf.toString()) */
// toString
/* buf = Buffer.from('拉勾教育')
console.log(buf)
console.log(buf.toString('utf-8', 3, 9)) */
// slice
/* buf = Buffer.from('拉勾教育')
let b1 = buf.slice(-3)
console.log(b1)
console.log(b1.toString()) */
// indexOf
/* buf = Buffer.from('zce爱前端,爱拉勾教育,爱大家,我爱所有')
console.log(buf)
console.log(buf.indexOf('爱qc', 4)) */
// copy
let b1 = Buffer.alloc(6)
let b2 = Buffer.from('拉勾')
b2.copy(b1, 3, 3, 6)
console.log(b1.toString())
console.log(b2.toString())
Buffer静态方法
- concat 将多个buffer拼接成一个新的buffer
- isBuffer 判断当前数据是否是Buffer
/* let b1 = Buffer.from('拉勾')
let b2 = Buffer.from('教育')
let b = Buffer.concat([b1, b2], 9)
console.log(b)
console.log(b.toString()) */
// isBuffer
let b1 = '123'
console.log(Buffer.isBuffer(b1))
fs模块
FS是内置核心模块,提供文件系统操作的API。
权限位:用户对于文件所具备的操作权限。
标识符:node中flag表示对文件操作方式
- r 可读
- w 可写
- s 同步
- +相反操作
- x 排他操作
- a 追加操作
操作符:fd就是操作系统分配给被打开文件的标识
文件的读写与拷贝操作
- readFile 从指定文件读取数据
- write 向指定文件写入数据
- appendFile 追加的方式向指定文件写入数据
- copyFile 将某个文件的数据拷贝至另一文件
- watchFile 对指定文件进行监控
const fs = require('fs')
const path = require('path')
// readFile
/* fs.readFile(path.resolve('data1.txt'), 'utf-8', (err, data) => {
console.log(err)
if (!null) {
console.log(data)
}
}) */
// writeFile
/* fs.writeFile('data.txt', '123', {
mode: 438,
flag: 'w+',
encoding: 'utf-8'
}, (err) => {
if (!err) {
fs.readFile('data.txt', 'utf-8', (err, data) => {
console.log(data)
})
}
}) */
// appendFile
/* fs.appendFile('data.txt', 'hello node.js',{}, (err) => {
console.log('写入成功')
}) */
// copyFile
/* fs.copyFile('data.txt', 'test.txt', () => {
console.log('拷贝成功')
}) */
// watchFile
fs.watchFile('data.txt', {interval: 20}, (curr, prev) => {
if (curr.mtime !== prev.mtime) {
console.log('文件被修改了')
fs.unwatchFile('data.txt')
}
})
文件的打开与关闭
- open
- close
const fs = require('fs')
const path = require('path')
// open
/* fs.open(path.resolve('data.txt'), 'r', (err, fd) => {
console.log(fd)
}) */
// close
fs.open('data.txt', 'r', (err, fd) => {
console.log(fd)
fs.close(fd, err => {
console.log('关闭成功')
})
})
大文件的读写操作
针对大文件的读写,一般放到buffer中逐步读写操作,而不是在内存中一次性读写。
常见目录操作API
- access 判断文件或目录是否具有操作权限
- stat 获取目录及文件信息
- mkdir 创建目录
- rmdir 删除目录
- readdir 读取目录中内容
- unlink 删除指定文件
const fs = require('fs')
// 一、access
/* fs.access('a.txt', (err) => {
if (err) {
console.log(err)
} else {
console.log('有操作权限')
}
}) */
// 二、stat
/* fs.stat('a.txt', (err, statObj) => {
console.log(statObj.size)
console.log(statObj.isFile())
console.log(statObj.isDirectory())
}) */
// 三、mkdir
/* fs.mkdir('a/b/c', {recursive: true}, (err) => {
if (!err) {
console.log('创建成功')
}else{
console.log(err)
}
}) */
// 四、rmdir
fs.rmdir('a', {recursive: true}, (err) => {
if (!err) {
console.log('删除成功')
} else {
console.log(err)
}
})
// 五、readdir
/* fs.readdir('a/b', (err, files) => {
console.log(files)
}) */
// 六、unlink
/* fs.unlink('a/a.txt', (err) => {
if (!err) {
console.log('删除成功')
}
}) */
模块化规范
- common.js require 同步导入 一般用在node环境中,浏览器环境不支持
- amd
- cmd
- esm import 浏览器常用的导入,异步加载,官方规范
vm
创建独立运行的沙箱环境
Node中的事件环
队列说明
- timers 执行setTimeout 与 setInterval 回调
- pending callbacks 执行操作系统的回调 例如tcp udp
- idle prepare 只在系统内部使用
- poll 执行与i/o相关的回调 文件、网络等
- check 执行setimmediate 中的回调
- close callbacks 执行close事件的回调
close事件(如socket.on('close'))
6个队列阶段会依次执行,但是每个阶段结束后,会先清空微任务队列,在进入下一阶段。
procrss.nexttick 优先级高于promise.then
如果微任务中又触发了微任务,会一直执行到微任务队列清空,可能阻塞事件循环。
如果宏任务中嵌套了宏任务,会放到下一次事件循环中
如果poll阶段过快,或者读取的是缓存,poll阶段回调函数来不及注册,那么就会优先执行check阶段,等到下一次循环在执行poll阶段的回调
const fs = require('fs');
setTimeout(() => {
console.log('1️⃣ setTimeout');
Promise.resolve().then(() => {
console.log('1️⃣🔁 微任务 after setTimeout');
});
}, 0);
setImmediate(() => {
console.log('2️⃣ setImmediate');
Promise.resolve().then(() => {
console.log('2️⃣🔁 微任务 after setImmediate');
});
});
fs.readFile(__filename, () => {
console.log('3️⃣ fs.readFile (I/O 完成)');
Promise.resolve().then(() => {
console.log('3️⃣🔁 微任务 after fs.readFile');
});
});
Promise.resolve().then(() => {
console.log('0️⃣🔁 初始微任务');
});
process.nextTick(() => {
console.log('0️⃣🌀 process.nextTick');
});
- 0️⃣🌀 process.nextTick
- 0️⃣🔁 初始微任务
- 1️⃣ setTimeout
- 1️⃣🔁 微任务 after setTimeout
- 2️⃣ setImmediate
- 2️⃣🔁 微任务 after setImmediate
- 3️⃣ fs.readFile (I/O 完成)
- 3️⃣🔁 微任务 after fs.readFile
流
- 可写流:
- 可读流
- 管道流(可读可写)
- transform流 边读边压缩
背压机制
当写入速度 < 读取速度 时,写入流会返回 false。你需要监听 'drain' 事件,避免内存溢出。
const fs = require('fs');
const readable = fs.createReadStream('bigfile.txt');
const writable = fs.createWriteStream('output.txt');
readable.on('data', (chunk) => {
const canWrite = writable.write(chunk);
if (!canWrite) {
console.log('⚠️ 写入缓冲区满了,暂停读取...');
readable.pause(); // 暂停读取
}
});
writable.on('drain', () => {
console.log('✅ drain: 写入缓冲区已清空,恢复读取');
readable.resume(); // 恢复读取
});