提示
具体内容查看 Node 的 API 文档 (opens new window)
无论是前面的学习,还是官网文档中的案例(同时演示了 CJS/ESM 规范两种写法),都表明 Node 也在跟随着时代发展的脚步
# 使用环境 🤔
浏览器中执行(前端)
浏览器中可以借助 webpack 等工具实现对 CommonJS 代码的转换;
浏览器本身是支持 ESModule 代码;
<script src="./index.js" type="module"></script>
Copied!
Node 环境中执行 ✍(后端)
乖乖的使用 CommonJS 规范吧 😂
实在不爽,使用 node v13.2.0 之后的版本,并进行如下操作:
{ // 在package.json加入 "type": "module" }
Copied!
# 一、Buffer 类
# 1、字节
Buffer 是 Node 中的一个全局的类,可以用于操作二进制数据。
writeFile 中的应用
const { writeFile } = require('fs') const { Buffer } = require('node:buffer') const data = new Uint8Array(Buffer.from('Hello Node.js')) // 没有就创建该文件 writeFile('./bundle.txt', data, { flag: 'a' }, (err, data) => { if (err) throw err console.log('追加(append)写入成功') })
Copied!
我们通常会把 8 位的二进制 合并为一个单元(字节 byte),也就是:
1 byte = 8 bit
,例如:1 表示为 0000 0001
编程 与 字节
java 中:
int 类型是 4 个字节,long 类型是 8 个字节(可以表示的数值范围更大)
TCP 中:
其传输的就是 字节流,读取和写入都需要标明 字节个数
RGB 中:
其 rgb(255,255,255)在计算机上采用的是一个字节存储,例如:255 表示的是 1111 1111
图片中:
在 webpack 中,url-loader 中的 limit 值的单位是字节(bytes),例如:{ limit: 10 * 1024 }
常见的文本 txt、图片 png/jpg 等通常是 kb 为单位,其中
1 kb = 1024 byte
如果是音频、视频等那还会使用更大的单位 M,其中
1 M = 1024 kb = 1024 * 1024 byte
# 2、buffer
通常情况下,我们可以把 Buffer 看成是一个存储字节(byte)的数组。
提示
这些通过 Buffer 存储的二进制数据,可以通过查看 ASCII 码表转换为对应的 字符
值得注意的是,一个中文字符需要 3 个字节 才能完成存储
// buffer数据 [一个字节] ——> [11111111] ——> [ff] // 示例1 // console.log(new Buffer('buffer')) 已弃用 console.log(Buffer.from('buffer')) // <Buffer 62 75 66 66 65 72> // 示例2 const { readFile } = require('fs') readFile('./data.txt', (err, data) => { if (err) throw err console.log(data) // <Buffer e6 95 b0 e6 8d ae> ——> 数据 })
Copied!
# 3、编码解码
const { Buffer } = require('node:buffer') // 编码 const buf = Buffer.from('buffer') const buf = Buffer.from('buffer', 'base64') // 解码 console.log(buf.toString()) console.log(buf.toString('base64'))
Copied!
# 4、字节操作
我们可以通过 charCodeAt()
获取指定字符的 ASCII 码 (opens new window) (128 个)
进一步的,我们也可以使用
fromCharCode()
将 ASCII 码还原为 指定字符
const { Buffer } = require('node:buffer') // 初始化 8个字节 大小的 buffer内存空间 const buf = Buffer.alloc(8) // 编码 buf[0] = 97 buf[1] = 0x62 buf[2] = 'c'.charCodeAt() console.log(buf) // <Buffer 61 62 63 00 00 00 00 00> // 解码 console.log(buf.toString()) // abc
Copied!
# 二、文件相关
# 1、event 模块
提示
对应 event 模块的学习,我们可以联想一下 🤔 eventButs 的使用
events 事件触发器 ├─ EventEmitter 类 │ ├─ evemtNames() # 获取所有监听事件的名称 │ ├─ listeners(eventName) # 获取某个事件名称对应的监听器数组 │ ├─ listenerCount(eventName) # 获取某个事件名称对应的监听器个数 │ ├─ setMaxListeners(n) # 设置监听最大的监听个数(默认值 10) | └─ …… └─ ……
Copied!
设置监听器:emitter.on 、emitter.once 、emitter.prependListener 、emitter.prependOnceListener
取消监听器:emitter.off 、emitter.removeAllListeners
request 对象中的应用
http 基于 stream,而 stream 基于 event 模块
const http = require('http') const server = http.createServer() server.on('request', (req, res) => { // 代码逻辑 }) server.listen(6061, () => { console.log('Server running at http://localhost:6061') })
Copied!
const EventEmitter = require('node:events') const emitter = new EventEmitter() // 开启监听事件 function fooFn(payload) { console.log('监听的事件被触发') console.log('获得回传的数据为:', payload) } emitter.on('foo', fooFn) // ====== setTimeout(() => { // 发射事件 emitter.emit('foo', { code: 1, msg: { user: 'lencamo', pwd: 666666 } }) // 取消监听事件 emitter.off('foo', fooFn) }, 2000)
Copied!
事件触发器 应用场景
以 get 请求为例,我们为了解决拿取猫眼数据时存在异步的问题,采用了回调的方式解决了问题。
下面,我们使用 Node 触发器来实现一下。
const http = require('http') const url = require('url') const https = require('https') const EventEmitter = require('events') // 创建web服务器 http .createServer((req, res) => { const urlobj = url.parse(req.url, true) callbackName = urlobj.query.callback res.writeHead(200, { 'content-type': 'application', // 跨域问题解决方案:CORS头 'Access-Control-Allow-Origin': '*' }) // 案例升级: var event = null switch (urlobj.pathname) { case '/api/list': // 创建event对象 event = new EventEmitter() // 1、监听paly事件 event.on('play', (data) => { res.end(data) }) // 发起数据请求 http_get() // http_get((data) => { // res.end(data) // }) break default: res.end('404') break } res.end() }) .listen(3000, () => { console.log('Server running at http://localhost:3000') }) function http_get() { var data = '' https.get( `https://i.maoyan.com/api/mmdb/movie/v3/list/hot.json?ct=%E5%8C%97%E4%BA%AC&ci=1&channelId=4`, (res) => { res.on('data', (chunk) => { data += chunk }) // 2、数据获取完后,触发play事件 res.on('end', () => { // cb(data) event.emit('play', data) }) } ) }
Copied!
# 2、Stream 流 🎈
http 基于 stream,而 stream 基于 event 模块
http 模块的 Request 和 Response 对象是基于 Stream 流实现的
所有的 Stream 流 都是 EventEmitter 的实例
提示
我们可以使用 Stream 流对数据进行精细控制
stream 流 ├─ API for stream consumers │ ├─ Writable 流 │ ├─ Readable 流 │ ├─ Duplex and Transform 流 | └─ …… └─ API for stream implementers
Copied!
Stream 流是对连续字节的一种表现形式和抽象概念,是可读可写的。
常见的流类型有:
- Writable (opens new window) 流(例如: fs.createWriteStream() (opens new window))
- Readable (opens new window) 流(例如: fs.createReadStream() (opens new window))
- Duplex and Transform (opens new window) 流
# 3、Readable
fs.createReadStream()
:以流的方式读取文件内容
const { createReadStream } = require('fs') const readStream = createReadStream('./data.txt', { encoding: 'utf8' }) // 常见的事件 readStream.on('data', (chunk) => { console.log(chunk) }) readStream.on('end', () => { console.log('Finished reading file.') }) readStream.on('error', (err) => { console.error(err) })
Copied!
我们可以使用其 option 选项对流进行 精细控制:
如果 data.txt 的内容为:
12345数据abc----
,则依次打印的内容为:数、据、abc
const { createReadStream } = require('fs') const readStream = createReadStream('./data.txt', { start: 5, // 起始字节位置 end: 13, // 结束字节位置 highWaterMark: 3 // 每次读几个字节 }) // 常见的方法 readStream.on('data', (chunk) => { console.log(chunk.toString()) readStream.pause() setTimeout(() => { readStream.resume() }, 2000) })
Copied!
# 4、Writable
fs.createWriteStream()
:以流的方式写入文件内容
const { createWriteStream } = require('fs') const writeStream = createWriteStream('./data.txt', { flags: 'a', autoClose: true }) // 常见的方法 writeStream.write('hello node!', (err) => { console.log(err) }) // writeStream.close() writeStream.end('\n') // close的同时追加内容 // 常见的事件 writeStream.on('finish', (chunk) => { console.log('写入完成') }) writeStream.on('close', (chunk) => { console.log('文件被关闭') })
Copied!
# 5、pipe 管道
有时候,往往存在一些文件的拷贝操作
- 传统方式
const { readFile, writeFile } = require('fs') readFile('./data.txt', (err, data) => { if (err) throw err writeFile('./data-copy.txt', data, (err) => { if (err) throw err }) })
Copied!
- 流的方式
const { createReadStream, createWriteStream } = require('fs') const readStream = createReadStream('./data.txt', { encoding: 'utf8' }) const writeStream = createWriteStream('./data-copy.txt', { encoding: 'utf8' }) // 方式1:流 // readStream.on('data', (chunk) => { // writeStream.write(chunk) // }) // readStream.on('end', () => { // writeStream.close() // }) // 方式2:管道 readStream.pipe(writeStream)
Copied!
# 三、其他
# 1、crypto 模块
crypto 模块的目的是为了提供通用的加密和哈希算法。常见的应用场景有:文件完整性校验、数据加密等等
加密后的结果格式通常有:
hex(十六进制数)、base64、binary(二进制数)等等
- 哈希算法(Hash)
常见的哈希算法有:MD5、sha1,默认生成的是一个 hash 值
const crypto = requrie('crypto') // 加密前 const password = '123456' // 1、采用md5算法 const md5 = crypto.createHash('md5') const pwd_md5 = md5.updata(password).digest('hex') // 结果转换为了hex格式 // 2、采用sha1算法 const sha1 = crypto.createHash('sha1') const pwd_sha1 = sha1.updata(password).digest('hex') // 结果转换为了hex格式 // 加密后 console.log(pwd_md5) console.log(pwd_sha1)
Copied!
- 随机数增强的哈希算法(Hmac)
常见的哈希加密算法有:MD5、sha、sha256
const crypto = requrie('crypto') // 注意:加了一个🚩秘钥 const hmac = crypto.createHmac('sha256', 'secret-key') hmac.update('My blog is deer-sir.cn') console.log(hmac.digest('hex'))
Copied!
- 对称加密算法(AES)
常见的对称加密算法:aes-128-cbc
// 1、封装加密 和 解密函数 function encrypt(key, iv, data) { let dep = crypto.createCipheriv('aes-128-cbc', key, iv) // 设置输入、输出格式 return dep.update(data, 'binary', 'hex') + dep.final('hex') } function decrypt(key, iv, cryted) { crypted = Buffer.from(crypted, 'hex').toString('binary') let dep = crypto.createDecipheriv('aes-128-cbc', key, iv) // 返回utf8格式的数据 return dep.update(crypted, 'binary', 'utf8') + dep.final('utf8') } // 2、使用示例 // 16*8=128 let key = 'abcdef1234567890' let iv = '12abc34567890def' let data = 'lencamo' let cryted = encrypt(key, iv, data) console.log('加密结果为:', cryted) let decrypted = decrypt(key, iv, cryted) console.log('解密结果为:', decrypted)
Copied!
# 2、zlib 压缩
图示:

- 数据传输
const http = require('http') const fs = require('fs') const zlib = require('zlib') const gzip = zlib.createGzip() http .createServer((req, res) => { // 可读流 const rs = fs.createReadStream('./1.txt') // 为浏览器配置让其对打包的文件解压 res.writeHead(200,{'Content-Type':'application/x-javascript;charset=utf-8';'Content-Encoding':'gzip'}) // 要求浏览器先对文件进行压缩 pipe(gzip),然后显示数据 // res:本质上也是一个🌈可写流(ws) rs.pipe(gzip).pipe(res) res.end() }) .listen(8080, () => { console.log('Server running at http://localhost:8080') })
Copied!