前端宝典十二:node基础模块和常用API
Node.js是开源、跨平台的JavaScript运行时环境,构建在Chrome的v8引擎之上。
Node.js是异步事件驱动的单线程模型。
由于Node.js是异步非阻塞的特性,因此适用于I/O密集型应用场景。
需要注意的是,Node.js是单线程模型,需要避免CPU的耗时操作。
Node.js版本的特点:
- Current 版本:包含了主分支上非重大的更新。
- Active LTS 版本:经过 团队审核的新功能、错误修复和更新,已被确定为适用于发布线且稳
定。 - Maintenance 版本:关键错误修复和安全更新。
建议: 由于偶数版本获得的支持时间比较⻓,推荐在生产环境中使用偶数版本的 Node.js 。
一、CommonJs模块
Node.js应用由模块组成,默认采用的是 CommonJS 规范。即每个文件是一个模块,有自己的作 用域。在一个文件里面定义的变量、函数、类等都是私有的,对其它文件不可⻅。
不过我们可以通过在模块中通过 exports 或者 module.exports 命令将内容导出,其它文件就可 以通过 require 命令访问到这些被导出的内容。
- module: 是对当前模块对象的引用。 module.exports 用于定义模块导出的内容。
- exports: 是 对应的引用。
- require: 加载模块,访问模块导出的内容,多次加载模块使用缓存数据,第二次加载时直接返回上一次的缓存结果。
- __dirname: 当前模块的文件夹路径
- __filename: 当前模块的文件路径。
- require.cache:存储已加载的模块的缓存对象
- require.main:主入口文件模块对象
- require.resolve:返回模块解析后的文件路径
Node.js会检测出当前代码是否存在循环引用,并做相应的处理,如抛出warning
二、ECMAScript模块
ECMAScript Module( ESM)是打包Javascript代码以供重复使用的官方标准格式。模块是使用import 和 export 相关的语句定义的。
ECMAScript和CommonJs区别
- ESM没有 require 、 exports 、 module.exports ,使用的是import、export
- ESM没有 __filename 、 __dirname 。
- ESM没有 require.resolve ,不过可以使用 import.meta.resolve 。
- ESM没有 require.cache 。
三、API
1、module.builtinModules
用于验证是否是由第三方维护
const builtin = require('node:module').builtinModules;
2、module.createRequire(filename)
创建 require 函数。 参数必须是URL对象、 字符串,或者是绝对路径。
3、module.isBuiltin(moduleName)
检测模块是否是内置模块。
const isBuiltin = require('node:module').isBuiltin;
console.log(isBuiltin('node:path')); // true
console.log(isBuiltin('path')); // true
console.log(isBuiltin('wss')); // false
4、URL模块
URL模块提供了一些非常实用的用于解析 的方法。
1. new URL()
const url = require('node:url');
const myURL = new URL('https://user:pass@sub.example.com:8080/p/a/t/h?query=string#hash')
/*href: 'https://user:pass@sub.example.com:8080/p/a/t/h?query=string#hash',origin: 'https://sub.example.com:8080',protocol: 'https:',username: 'user',password: 'pass',host: 'sub.example.com:8080',hostname: 'sub.example.com',port: '8080',pathname: '/p/a/t/h',search: '?query=string',searchParams: URLSearchParams { 'query' => 'string' },hash: '#hash'
}
*/
console.log(myURL);
修改URL信息
const url = require('node:url');
const myURL = new URL('https://user:pass@sub.example.com:8080/p/a/t/h?query=string#hash')
myURL.port = 8081;
// https://user:pass@sub.example.com:8081/p/a/t/h?query=string#hash 8
console.log(myURL.toString());
2. searchParams
用于读取和写入URL的query信息
const myURL = new URL('https://example.org/?abc=123');// 123console.log(myURL.searchParams.get('abc'));myURL.searchParams.append('abc', 'xyz');// https://example.org/?abc=123&abc=xyzconsole.log(myURL.href);myURL.searchParams.delete('abc');myURL.searchParams.set('a', 'b');// false
console.log(myURL.searchParams.has('abc'));// https://example.org/?a=b
console.log(myURL.href);myURL.search = new URLSearchParams('name=lily');
// https://example.org/?name=lily
console.log(myURL.href);
手动创建URLSearchParams对象
const newSearchParams = new URLSearchParams('a=b');
newSearchParams.append('a', 'c');// a=b&a=c
console.log(newSearchParams.toString());
5、querystring
querystring模块提供了一些非常实用的用于解析查询字符串的方法。
1. querystring.parse()解析查询字符串
/*{ a: 'b', c: [ 'd', 'f' ] }*/const querystring = require('node:querystring');const obj = querystring.parse('a=b&c=d&c=f');console.log(obj);
2. querystring.stringify()将对象序列化成查询字符串
const querystring = require('node:querystring'); 2const obj = {a: 'b',c: ['d', 'f']};// a=b&c=d&c=fconsole.log(querystring.stringify(obj));// a:b;c:d;c:fconsole.log(querystring.stringify(obj, ';', ':'));
6、Path模块
Path模块提供了用于处理文件和目录路径的实用程序。
1. path.basename(path, suffix)返回路径的最后一部分。
const path = require('node:path'); 2// index.htmlpath.basename('/foo/bar/baz/asdf/index.html');// indexpath.basename('/foo/bar/baz/asdf/index.html', '.html')
2. path.delimiter 返回特定平台的路径之间的分隔符。Windows使用的是‘;’,POSIX使用的是‘;’
const path = require('node:path');
console.log(path.delimiter);
console.log(process.env.PATH);
3. path.dirname(path)返回的是路径的目录名
const path = require('node:path'); 2
3 // /foo/bar/baz/asdf
4 path.dirname('/foo/bar/baz/asdf/quux');
4. path.extname(path)返回路径扩展名
根据最后一个 . 字符到路径结尾的字符串
const path = require('node:path');
// .html
path.extname('index.html');
// .
path.extname('index.');
//
path.extname('index');
// .md
path.extname('.index.md');
5. path.isAbsoute(path)判断是否是绝对路径
如果path是绝对路径,则返回true
const path = require('node:path');
ath.isAbsolute('/foo/bar'); // true
6. path.join([…paths])连接给定路径并format
const path = require('node:path'); 2// /foo/bar/baz/asdf
path.join('/foo', 'bar', 'baz/asdf', 'quux', '..');
7. path.normalize(path) 规范化给定路径
const path = require('node:path');
// /foo/bar/baz/asdf
path.normalize('/foo/bar//baz/asdf/quux/..');
8. path.relative(from, to) 从a到b的相对路径
根据当前工作目录返回从from到to的相对路径
const path = require('node:path'); 2
// ../../impl/bbb
path.relative('/data/orandea/test/aaa', '/data/orandea/impl/bbb');
9. path.resolve([…paths])将一些路径解析成绝对路径
const path = require('node:path'); 2
// /foo/bar/baz
path.resolve('/foo/bar', './baz');
10. path.sep返回特定平台的路径中目录之间分隔符
const path = require('node:path');
// windows使用的是\,POSIX使用的是/
console.log(path.sep);
7、File模块
1. 创建文件夹mkdirSync
const { mkdirSync } = require('node:fs');
try {mkdirSync('./temp');
// 不可以同时创建不存在的多层级目录 mkdirSync('./logs/errorlog');
} catch (err) {// handle the errorconsole.log(err);
}
2. 写入文件writeFileSync(如果文件不存在,则创建文件)
const { writeFileSync } = require('node:fs');
const path = require('node:path');
try {writeFileSync(path.resolve(__dirname, './music.txt'), '新的内容', 'utf8'))
} catch (err) {// handle the error
}
3. 追加文件内容appendFileSync
const { appendFileSync } = require('node:fs');
const path = require('node:path');
try {appendFileSync(path.resolve(__dirname, './music.txt'), '新的内容88888', 'utf8'))
} catch (err) {// handle the error
}
4. 读取文件夹readdirSync
const { readdirSync } = require('node:fs');
try {const files = readdirSync('.');console.log(files);
} catch (err) {// handle the errorconsole.log(err);
5. 读取文件readFileSync
const { readFileSync } = require('node:fs');
const path = require('node:path');
try {const tent = readFileSync(path.resolve(__dirname, './music.txt'), '新的内容88888', 'utf8'))console.log(tent)
} catch (err) {// handle the error
}
6. statSync区分文件夹还是文件、判断文件或者文件夹是否存在
const { statSync } = require('node:fs');
try {const stat = statSync('./temp');if (stat.isDirectory()) {console.log('这是一个文件夹'); } else if (stat.isFile()) {console.log('这是一个文件');
} catch (err) {// handle the error
console.log(err && '文件不存在');
}
7. renameSync重命名文件
const { renameSync } = require('node:fs');
try {renameSync('./music.txt', './new-music.txt');
} catch (err) {// handle the error
}
8. rmdirSync删除文件夹
const { rmdirSync } = require('node:fs');
try {
// 目录不为空,则删除失败 rmdirSync('./temp');
} catch (err) {
// handle the errorconsole.log(err);
}
9. unlinkSync删除文件
const { unlinkSync } = require('node:fs');
try {unlinkSync('./temp');
} catch (err) {
// handle the errorconsole.log(err);
}
10. 实战:递归创建文件夹
const {mkdirSync, statSync } = require('node:fs');
const path = require('node:path');
function mkdirs(dir){const dirs = dir.split(path.sep);dirs.forEach((current, index)=>{const dirPath = dirs.slice(0, index + 1).join(path.sep);if(current){let isExisted = false;try {isExisted = statSync(dirPath).isDirectory();} catch (err) {// 说明文件不存在}if (!isExisted) {mkdirSync(dirPath)}}})
}
mkdirs(path.resolve(__dirname, './temp/logs'));
8、process模块
process 对象提供有关当前 Node.js 进程的信息。
1. cwd()返回进程的当前工作目录
const { cwd } = require('node:process');
// /xxxx/Node核心模块使用/10.process
console.log(cwd());
2. chdir()更改当前工作目录
const { chdir, cwd } = require('node:process');
// Starting directory: /xxxx/Node核心模块使用/10.process
console.log(`Starting directory: ${cwd()}`);
try {chdir('../');
// New directory: /xxxx/Node核心模块使用console.log(`New directory: ${cwd()}`);
} catch (err) {console.error(`chdir: ${err}`);
}
3. process.env 环境变量
const process = require('node:process');
console.log(process.env);process.env.mode = 'development';
console.log(process.env);
9、Events模块
Node.js的大部分核心API都是围绕着一种异步事件驱动架构构建的。在这种架构中,某些类型的对象(称为“ emitters ”)会发出特定的具名事件,因此导致调用Function对象(“listeners”)
1. on/emit基本用法
const EventEmitter = require('node:events');
class MyEmitter extends EventEmitter {}
const myEmitter = new MyEmitter();myEmitter.on('event', () => {
console.log('an event occurred!');
});myEmitter.emit('event');
2. 传递参数和 listener中的this
const EventEmitter = require('node:events');
class MyEmitter extends EventEmitter {}
const myEmitter = new MyEmitter();myEmitter.on('event', function (a, b) {
// a, b, true
console.log(a, b, this === myEmitter);
});myEmitter.emit('event', 'a', 'b');
3. 只触发一次的事件函数
const EventEmitter = require('node:events');
class MyEmitter extends EventEmitter {}
const myEmitter = new MyEmitter();
letm=0;
myEmitter.once('event', () => {console.log(++m);
});
// 1
myEmitter.emit('event');
// 没有反应
myEmitter.emit('event');
4. 绑定多个listeners
eventEmitter.listeners返回函数列表。
const EventEmitter = require('node:events');
const myEmitter = new EventEmitter();myEmitter.on('event', function firstListener() {console.log('Helloooo! first listener');
});myEmitter.on('event', function secondListener(arg1, arg2) {console.log(`event with parameters ${arg1}, ${arg2} in second listener`);
});myEmitter.on('event', function thirdListener(...args) {const parameters = args.join(', ');console.log(`event with parameters ${parameters} in third listener`);
});console.log(myEmitter.listeners('event'));myEmitter.emit('event', 1, 2, 3, 4, 5);
5. 返回所有的事件名称
eventEmitter.eventNames返回所有的事件名称列表。
onst EventEmitter = require('node:events');const myEE = new EventEmitter();
myEE.on('foo', () => {});
myEE.on('bar', () => {});// ['foo', 'bar']
console.log(myEE.eventNames());
6. 移除listener
eventEmitter.off是eventEmitter.removeListener 的别名。
const EventEmitter = require('node:events');
class MyEmitter extends EventEmitter {}
const myEmitter = new MyEmitter();const callbackA = () => {console.log('A');myEmitter.removeListener('event', callbackB);
};const callbackB = () => {console.log('B');
};myEmitter.on('event', callbackA);myEmitter.on('event', callbackB);// A、B
myEmitter.emit('event');// A
myEmitter.emit('event');
7. 移除所有的listener
eventEmitter.removeAllListeners 移除指定的事件所有绑定过的回调函数。
const EventEmitter = require('node:events');
class MyEmitter extends EventEmitter {}
const myEmitter = new MyEmitter();const callbackA = () => {console.log('A');
};const callbackB = () => {console.log('B');
};myEmitter.on('event', callbackA);myEmitter.on('event', callbackB);// A、B
myEmitter.emit('event');myEmitter.removeAllListeners('event');// 没有反应
myEmitter.emit('event');
10、Stream模块
Stream是一个抽象接口,用于处理Node.js中的流数据。node:stream模块为实现流接口提供了API。
Stream可以是可读的,也可以是可写的,或者两者都有。所有流都是EventEmitter的实例。
Node.js提供了很多流对象,比如操作文件系统、对HTTP服务的请求等。
通常在处理大体积文件的时候用到流,防止占用过多的内存。
1. 创建文件只读流
// 创建只读流
const { createReadStream, statSync } = require('node:fs');// highWaterMark
const stream = createReadStream('./demo.txt', { encoding: 'utf8' });let totalLength = 0;
stream.on('data', (chunk) => {totalLength += Buffer.byteLength(chunk);
});stream.on('end', () => {console.log(`数据读取完毕`);console.log(statSync('./demo.txt').size);console.log(totalLength);
});
2. 创建文件可写流
// 复制文件
const { createReadStream, createWriteStream, statSync } = require('node:fs');const readStream = createReadStream('./demo.txt', { encoding: 'utf8' });
const writeStream = createWriteStream('./demo-write.txt', { encoding: 'utf8' });// 方式1:手动写入
readStream.on('data', (chunk) => {writeStream.write(chunk);
});readStream.on('end', () => {console.log(`数据写入完毕`);console.log(statSync('./demo.txt').size === statSync('./demo-write.txt').size);
});// 方式2:通过管道完成写入
readStream.on('end', () => {console.log(`数据读取完毕`);}).pipe(writeStream).on('finish', () => {console.log(`写入完毕`);console.log(statSync('./demo.txt').size === statSync('./demo-write.txt').size);})
3. 转换流
// 压缩文件
const { createReadStream, createWriteStream } = require('node:fs');
const zlib = require('node:zlib');const readStream = createReadStream('./demo.txt');
const duplexStream = zlib.createGzip();
const writeStream = createWriteStream('./demo.tar.gz');readStream.pipe(duplexStream).pipe(writeStream);
4. 下载文件
const { createReadStream } = require('node:fs');
const zlib = require('node:zlib');
const http = require('node:http');http.createServer((req, res) => {res.writeHead(200, {'Content-Type': 'application/octet-stream','Content-Encoding': 'gzip','Content-Disposition': 'attachment; filename=demo.tar.gz',});const readStream = createReadStream('./demo.txt');const duplexStream = zlib.createGzip();readStream.pipe(duplexStream).pipe(res);
}).listen(8890);
5. readline模块
node:readline模块提供了一个接口,用于一次一行地从Readable流读取数据。
const readline = require('node:readline');
const { stdin: input, stdout: output } = require('node:process');const rl = readline.createInterface({ input, output });rl.question('What do you think of Node.js? ', (answer) => {console.log(answer);rl.close();
});
逐行读取文件内容
const { createInterface } = require('node:readline');
const { once } = require('node:events');
const { createReadStream } = require('node:fs');(async () => {const rl = createInterface({input: createReadStream('./demo.txt'),});let line = 0;rl.on('line', (lineContent) => {console.log(`${++line}:`, lineContent.toString());});await once(rl, 'close');console.log('File read complete.');
})();
11、 HTTP模块
http模块是一个非常常用的模块,我们可以用它来搭建一个服务器,或者使用它发送请求。
1. 搭建服务器
const http = require('node:http');
const { Buffer } = require('node:buffer');
const { URL } = require('node:url');
const querystring = require('node:querystring');// 程序支持的HTTP方法列表
// console.log(http.METHODS);// 所有标准HTTP响应状态代码的集合,以及每个状态代码的简短描述。
// console.log(http.STATUS_CODES);const server = http.createServer((req, res) => {/* --------- Request ---------- */// console.log(req.headers);// console.log(req.headers['cookie']);// console.log(req.httpVersion);// console.log(req.method);// console.log(req.url);// GET请求// console.log('query:', new URL(req.url, 'http://localhost:8888').searchParams);/* --------- Response ---------- */// 状态码和状态码描述// 获取状态码// console.log(res.statusCode);// console.log(res.statusMessage);// 设置状态码// res.statusCode = 404;// res.statusMessage = 'not found';// 设置响应头信息res.setHeader('Content-Type', 'text/html');res.setHeader('X-Foo', 'Bar');// res.writeHead(status, statusMessage, headers)// res.writeHead(200, 'Very OK', { 'Content-Type': 'text/plain' });// 获取响应头信息(header name自动转换成小写)// console.log(res.getHeaders());// console.log(res.getHeaderNames());// console.log(res.getHeader('X-Foo'));// console.log(res.hasHeader('X-Foo'));// 响应主体信息// res.write(chunk: string | buffer, encoding, callback)res.write(Buffer.from('hello world'))res.write('ok 1');res.write('ok 2');// res.end(data, encoding, callback); 相当于 res.write(data, encoding) + res.end(callback)res.end('ok end', 'utf8', () => {console.log('数据发送完成');});
});// 启动服务
server.listen(8888, 'localhost', () => {console.log('服务启动成功');
});// 关闭服务
// server.on('close', () => {
// console.log('The server is closed');
// });// server.close(() => {
// console.log('Close the server.');
// });
2. 获取POST请求的数据
const http = require('node:http');
const querystring = require('node:querystring');const server = http.createServer((req, res) => {if (req.method === 'POST') {let postData = '';req.on('data', (chunk) => {postData += chunk.toString();});req.on('end', () => {console.log('postData:', querystring.parse(postData));res.end('POST request end');});} else {res.end('Other request end');}
});server.listen(8888, 'localhost', () => {console.log('服务启动成功');
});
3. 客户端:发送GET网络请求
// http-server.js
const http = require('node:http');const server = http.createServer((req, res) => {if (req.method === 'GET') {console.log(req.headers['x-auth']);res.setHeader('Content-Type', 'text/html');res.end('This is a get request');} else if (req.method === 'POST') {// 接收client发送过来的数据let postData = '';req.on('data', (chunk) => {postData += chunk.toString();});req.on('end', () => {console.log('postData:', postData);res.setHeader('Content-Type', 'application/json');res.end(JSON.stringify({ a: 'b' }));});} else {res.statusCode = 404;res.end();}
});server.listen(8889, 'localhost', () => {console.log('服务启动成功');
});// 关闭服务
server.on('error', (error) => {console.log('The server has error', error.message);
})server.on('close', () => {console.log('The server is closed');
});
// http-get.js
const http = require('node:http');// http.get(url, options, callback)
const client = http.get('http://localhost:8889/?a=b', {headers: {'X-AUTH': '1234',},
}, (res) => {console.log(res.statusCode);console.log(res.headers);res.setEncoding('utf8');// 获取返回的内容let rawData = '';res.on('data', (chunk) => {rawData += chunk;});res.on('end', () => {console.log('data:', rawData);});
});// 请求服务出现错误
client.on('error', (e) => {console.log('client error info', e.message);
});// 关闭客户端
client.end();
4. 客户端:发送POST网络请求
// http-post.js
const http = require('node:http');
const { Buffer } = require('node:buffer');const postData = JSON.stringify({'msg': 'Hello World!',
});// http.request(options, callback)
const client = http.request({protocol: 'http:',hostname: 'localhost',port: '8889',pathname: '/',method: 'POST',headers: {'Content-Type': 'application/json','Content-Length': Buffer.byteLength(postData),},}, (res) => {console.log(res.statusCode);console.log(res.headers);res.setEncoding('utf8');// 获取返回的内容let rawData = '';res.on('data', (chunk) => {rawData += chunk;});res.on('end', () => {console.log('data:', rawData);});
});// 请求服务出现错误
client.on('error', (e) => {console.log('client error info', e.message);
});client.write(postData);// 关闭客户端
client.end();
