Node 基础
1、什么是Node.js
- Node.js是一个开源、跨平台的JavaScript运行时环境,构建在Chrome的V8引擎之上。
- Node.js是异步事件驱动的单线程模型。
- 由于Node.js异步非阻塞的特性,因此适用于 I/O 密集型的应用场景。
需要注意的是,Node.js是单线程模型,需要避免CPU的耗时操作。
2、CommonJS模块化
2.1 CommonJS规范
Node.js
应用由模块组成,默认采用的是CommonJS
规范。即每个文件是一个模块,有自己的作用域。在一个文件里面定义的变量、函数、类等都是私有的,对其它文件不可见。
不过我们可以通过在模块中通过exports
或者module.exports
命令将内容导出,其它文件就可以通过require
命令访问到这些被导出的内容。
-
module:module是对当前模块对象的引用。
module.exports
用于定义模块导出
的内容。 -
exports:exports是module.exports对应的引用。
-
require:
require加载
模块,访问模块导出的内容。 -
语法
// 模块导出 - user.js
const name = 'lucy';
const age = 28;
const printName = () => {
console.log(name);
};
module.exports = { name, age, printName };
// 或者
exports.name = name;
exports.age = age;
exports.printName = printName;
// 模块导入 - index.js
const { name, age, printName } = require('./user.js');
console.log(name, age, printName);
2.2 CommonJS示例
例子1 - 给exports直接赋值
// 模块导出 - user.js
const name = 'lucy';
const age = 28;
const printName = () => {
console.log(name);
};
exports = { name, age };
// 模块导入 - index.js
const { name, age, printName } = require('./user.js');
// undefined、undefined、undefined
console.log(name, age, printName);
// 入口文件
node ./index.js
例子2 - 同时使用exports和module.exports
// 模块导出 - user.js
const name = 'lucy';
const age = 28;
const printName = () => {
console.log(name);
};
module.exports = { name, age };
exports.printName = printName;
// 模块导入 - index.js
const { name, age, printName } = require('./user.js');
// lucy 28 undefined
console.log(name, age, printName);
// 入口文件
node ./index.js
例子3 - 访问包含随机语句的模块
// 模块导出 - user.js
const height = Math.random();
module.exports = { height };
// 模块导入 - index.js
const { height: firstHeight } = require('./user.js');
// 第一次访问: 0.14063734111371584
console.log('第一次访问:', firstHeight);
const { height: secondHeight } = require('./user.js');
// 第二次访问: 0.14063734111371584
console.log('第二次访问:', secondHeight);
// 入口文件
node ./index.js
例子4 - 循环引用
// moduleA.js
const name = 'moduleA';
const path = './moduleA.js';
const moduleB = require('./moduleB');
// moduleB.name moduleB
console.log('moduleB.name', moduleB.name);
module.exports = { name, path };
// moduleB.js
const name = 'moduleB';
const path = './moduleB.js';
const moduleA = require('./moduleA');
// moduleA.name undefined
console.log('moduleA.name', moduleA.name);
module.exports = { name, path };
// 入口文件
node ./moduleA.js
总结
- 模块导出的数据存放在module.exports对象中。
- exports是module.exports的别名。
- 模块加载过一次后会进行缓存,第二次加载时直接返回上一次的缓存结果。
- Node.js会检测出当前代码是否存在循环引用,并做相应的处理。
如何删除模块缓存
require.cache: 模块被加载时缓存在此对象中。
require.resolve: 定位到模块位置,返回模块的解析后的文件名。
// xxx: 文件标识符
delete require.cache[require.resolve('xxx')];
查看当前模块被包裹后的内容
/* 输出:
function (exports, require, module, __filename, __dirname) {
module file content
}
*/
console.log('' + arguments.callee);
查看主入口module对象
/* 输出:
{
"id": ".",
"path": "",
"exports": {},
"filename": "",
"loaded": false,
"children": [
{
"id": "",
"path": "",
"exports": {
"name": "lucy",
"age": 28
},
"filename": "",
"loaded": true,
"children": [],
"paths": [...]
}
],
"paths": [...]
}
*/
console.log(JSON.stringify(require.main, null, 2));
3、Node.js核心模块使用
3.1 HTTP模块(常用)
http模块是一个非常常用的模块,我们可以用它来搭建一个服务器,或者使用它发送请求。
- 搭建服务器
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.');
// });
- 获取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('服务启动成功');
});
- 客户端:发送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();
- 客户端:发送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();
3.2 URL模块(常用)
URL模块提供了一些非常实用的用于解析URL的方法。
- 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
console.log(myURL.toString());
- URLSearchParams对象
const myURL = new URL('https://example.org/?abc=123');
// 123
console.log(myURL.searchParams.get('abc'));
myURL.searchParams.append('abc', 'xyz');
// https://example.org/?abc=123&abc=xyz
console.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());
3.3 Query String模块(常用)
Query String模块提供了一些非常实用的用于解析查询字符串的方法。
- querystring.parse(str, sep, eq)
解析查询字符串。
/*
{ a: 'b', c: [ 'd', 'f' ] }
*/
const querystring = require('node:querystring');
const obj = querystring.parse('a=b&c=d&c=f');
console.log(obj);
- querystring.stringify(obj, sep, eq)
将对象序列化成查询字符串。
const querystring = require('node:querystring');
const obj = {
a: 'b',
c: ['d', 'f']
};
// a=b&c=d&c=f
console.log(querystring.stringify(obj));
// a:b;c:d;c:f
console.log(querystring.stringify(obj, ';', ':'));
3.4 Path模块(常用)
Path模块提供了用于处理文件和目录路径的实用程序。
- path.basename(path, suffix)
返回路径的最后一部分。
const path = require('node:path');
// index.html
path.basename('/foo/bar/baz/asdf/index.html');
// index
path.basename('/foo/bar/baz/asdf/index.html', '.html');
- path.delimiter
返回特定平台的路径之间分隔符。Windows使用的是;,POSIX使用的是:。
const path = require('node:path');
console.log(path.delimiter);
console.log(process.env.PATH);
- path.dirname(path)
返回路径的目录名。
const path = require('node:path');
// /foo/bar/baz/asdf
path.dirname('/foo/bar/baz/asdf/quux');
- path.extname(path)
返回路径的扩展名。根据最后一个.字符到路径结尾的字符串。
const path = require('node:path');
// .html
path.extname('index.html');
// .
path.extname('index.');
//
path.extname('index');
// .md
path.extname('.index.md');
- path.isAbsoute(path)
如果path是绝对路径则返回true。
const path = require('node:path');
ath.isAbsolute('/foo/bar'); // true
path.isAbsolute('/baz/..'); // true
path.isAbsolute('qux/'); // false
path.isAbsolute('.'); // false
- path.join([…paths])
将所有给定的路径片段连接在一起,然后对生成的路径进行规范化。
const path = require('node:path');
// /foo/bar/baz/asdf
path.join('/foo', 'bar', 'baz/asdf', 'quux', '..');
- path.normalize(path)
规范化给定的路径,解析…和.片段。
const path = require('node:path');
// /foo/bar/baz/asdf
path.normalize('/foo/bar//baz/asdf/quux/..');
- path.relative(from, to)
根据当前工作目录返回从from到to的相对路径。
const path = require('node:path');
// ../../impl/bbb
path.relative('/data/orandea/test/aaa', '/data/orandea/impl/bbb');
- path.resolve([…paths])
将一系列的路径片段解析为绝对路径。
const path = require('node:path');
// /foo/bar/baz
path.resolve('/foo/bar', './baz');
// /tmp/file
path.resolve('/foo/bar', '/tmp/file/');
// 假如当前命令执行的目录是/home/myself/node(和代码文件所在的目录没有关系)
// /home/myself/node/wwwroot/static_files/gif/image.gif
path.resolve('wwwroot', 'static_files/png/', '../gif/image.gif');
- path.sep
返回特定平台的路径中目录之间分隔符。Windows使用的是\,POSIX使用的是/。
const path = require('node:path');
console.log(path.sep);
path.join()
和path.resolve()
的区别
path.join是用来连接
路径片段,并返回规范化后的路径
;
path.resolve将路径片段解析为绝对路径
。
// 例子1
// /bar/foo
console.log(path.join('/bar', '/foo'));
// /foo
console.log(path.resolve('/bar', '/foo'));
// 例子2:假如当前命令执行的目录是/home/myself/node(和代码文件所在的目录没有关系)
// bar/foo/
console.log(path.join('bar/', 'foo/'));
// /home/myself/node/bar/foo
console.log(path.resolve('bar/', 'foo/'));
3.5 File System模块(常用)
node:fs模块与文件系统进行交互。
- 基本使用
// 使用基于Promise的API
const fs = require('node:fs/promises');
// 使用基于回调函数或者同步操作的API
const fs = require('node:fs');
- 基于Promise、回调函数、同步操作的使用方式
// promise
const { readFile } = require('node:fs/promises');
const path = require('node:path');
(async function(path) {
try {
const content = await readFile(path, 'utf8');
console.log(content);
} catch (error) {
console.error('there was an error:', error.message);
}
})(path.resolve(__dirname, './music.txt'));
// callback
const { readFile } = require('node:fs');
const path = require('node:path');
readFile(path.resolve(__dirname, './music.txt'), 'utf8', (err, data) => {
if (err) throw err;
console.log(data);
});
// sync handle
const { readFileSync } = require('node:fs');
const path = require('node:path');
try {
const content = readFileSync(path.resolve(__dirname, './music.txt'), 'utf8');
console.log(content);
} catch (err) {
// handle the error
}
接下来的案例代码都是基于同步操作的API编写。
3. 创建文件夹
const { mkdirSync } = require('node:fs');
try {
mkdirSync('./temp');
// 不可以同时创建不存在的多层级目录
mkdirSync('./logs/errorlog');
} catch (err) {
// handle the error
console.log(err);
}
- 写入文件(如果文件不存在,则创建文件)
const { writeFileSync } = require('node:fs');
const path = require('node:path');
try {
writeFileSync(path.resolve(__dirname, './music.txt'), '新的内容', 'utf8');
} catch (err) {
// handle the error
}
- 追加文件内容
const { appendFileSync } = require('node:fs');
const path = require('node:path');
const os = require('node:os');
try {
appendFileSync(path.resolve(__dirname, './music.txt'), `${os.EOL}新的内容`, 'utf8');
} catch (err) {
// handle the error
}
- 读取文件夹
const { readdirSync } = require('node:fs');
try {
const files = readdirSync('.');
console.log(files);
} catch (err) {
// handle the error
console.log(err);
}
- 读取文件
const { readFileSync } = require('node:fs');
const path = require('node:path');
try {
const content = readFileSync(path.resolve(__dirname, './music.txt'), 'utf8');
console.log(content);
} catch (err) {
// handle the error
}
- 区分文件夹还是文件、判断文件或者文件夹是否存在
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 && '文件不存在');
}
- 重命名文件
const { renameSync } = require('node:fs');
try {
renameSync('./music.txt', './new-music.txt');
} catch (err) {
// handle the error
}
- 删除文件夹
const { rmdirSync } = require('node:fs');
try {
// 目录不为空,则删除失败
rmdirSync('./temp');
} catch (err) {
// handle the error
console.log(err);
}
- 删除文件
const { unlinkSync } = require('node:fs');
try {
unlinkSync('./music1.txt');
} catch (err) {
// handle the error
console.log(err);
}
- 实战:递归创建文件夹
const { mkdirSync, statSync } = require('node:fs');
const path = require('node:path');
function mkdirs(dir) {
const dirs = dir.split(path.sep);
dirs.forEach((currentDir, index) => {
const dirPath = dirs.slice(0, index + 1).join(path.sep);
if (currentDir) {
let isExisted = false;
try {
isExisted = statSync(dirPath).isDirectory();
} catch (err) {
// 说明文件夹不存在
}
if (!isExisted) {
mkdirSync(dirPath);
}
}
});
}
mkdirs(path.resolve(__dirname, './temp/logs'));
3.6 Modules:CommonJS模块
使用CommonJS规范的文件模块对象。
特性:
- require:加载文件模块导出的对象。
- module.exports:存储文件模块导出的对象。
- __dirname: 当前模块的文件夹路径。
- __filename: 当前模块的文件路径。
- exports:模块导出对象,是module.exports的别名。
- require.cache:存储已加载的模块的缓存对象。
- require.main:主入口文件模块对象。
- require.resolve:返回模块解析后的文件路径。
console.log(__dirname);
console.log(__filename);
exports = module.exports = {a : 1, b : 2};
exports.c = 3;
console.log(require.main);
// xxx.js不存在,会报错
delete require.cache[require.resolve('./xxx.js')];
3.7 Modules:ECMAScript模块
ECMAScript Module(ESM)是打包JavaScript代码以供重复使用的官方标准格式。模块是使用import
和export
相关的语句定义的。
生效规则
- 如果文件扩展名为
.js
。只有最近的package.json
文件的type
属性值为module
时,Node.js
才会认为文件使用的是ESM
规范的模块,其它情况都会认为采用的是CommonJS
规范的模块。 - 无论
type
字段的值如何,.mjs
文件始终被视为ES
模块,.cjs
文件总是被视为CommonJS
模块。
注意事项
- 必须指定文件扩展名。
- 文件夹的索引文件也必须要完全指定。
示例
// user.mjs
const name = 'lily';
const age = 28;
export default { name, age };
// index.mjs
import user from './user.mjs';
console.log(user);
特性
- 导入断言
目前只支持json类型。
// animal.json
{
"name": "monkey"
}
// index.mjs
import animal from './animal.json' assert { type: 'json' };
console.log('animal', animal);
- 直接在内置的模块中导出API
import { readFile } from 'node:fs';
readFile(new URL('./foo.txt', import.meta.url), { encoding: 'utf8' }, (err, source) => {
if (err) {
console.error(err);
} else {
console.log(source);
}
});
- import.meta
包含url和resolve属性的对象。 - import.meta.url
返回当前文件的url路径,以file://协议开头。
// file:///app/index.mjs
console.log(import.meta.url);
- import.meta.resolve
返回需要被解析的文件的url路径,以file://协议开头。
// file:///app/user.mjs
console.log(import.meta.resolve('./user.mjs'));
总结:ESM和CommonJS的区别
- ESM没有
require
、exports
、module.exports
。 - ESM没有
__filename
、__dirname
。 - ESM没有
require.resolve
,不过可以使用import.meta.resolve
。 - ESM没有
require.cache
。
3.8 Modules:node:module API
提供了一些通用的与模块实例交互的方法。
- module.builtinModules
Node.js提供的所有模块的名称列表。可用于验证模块是否由第三方维护。
const builtin = require('node:module').builtinModules;
- module.createRequire(filename)
创建require
函数。filename
参数必须是URL对象、URL字符串,或者是绝对路径。
// sibling-module.js
module.exports = {
name: 'sibling-module',
};
// index.mjs
import { createRequire } from 'node:module';
const require = createRequire(import.meta.url);
const siblingModule = require('./sibling-module');
console.log(siblingModule);
- 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
3.9 OS模块
node:fs模块与文件系统进行交互。
- os.EOL
const os = require('node:os');
console.log(os.EOL);
3.10 Stream模块
Stream
是一个抽象接口,用于处理Node.js
中的流数据。node:stream
模块为实现流接口提供了API。
Stream
可以是可读的,也可以是可写的,或者两者都有。所有流都是EventEmitter的实例。
Node.js提供了很多流对象,比如操作文件系统、对HTTP服务的请求等。
通常在处理大体积文件的时候用到流,防止占用过多的内存。
- 创建文件只读流
// 创建只读流
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);
});
- 创建指定范围的只读流
const rangeStream = createReadStream('./demo.txt', { encoding: 'utf8', start: 3, end: 5 });
let content = '';
rangeStream.on('data', (chunk) => {
content += chunk;
});
rangeStream.on('end', () => {
// 一个字符
console.log(content);
});
- 创建文件可写流
// 复制文件
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);
})
- 转换流
// 压缩文件
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);
- 下载文件
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);
3.11 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.');
})();
- 轻量的CLI
const readline = require('node:readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
prompt: 'HAHA> ',
});
rl.prompt();
rl.on('line', (line) => {
switch (line.trim()) {
case 'hello':
console.log('world!');
break;
default:
console.log(`Say what? I might have heard '${line.trim()}'`);
break;
}
rl.prompt();
}).on('close', () => {
console.log('Have a great day!');
process.exit(0);
});
3.12 process模块
process
对象提供有关当前Node.js
进程的信息。
- process.argv 和 process.argv0
process.argv
属性返回一个数组,该数组包含Node.js
进程启动时传递的命令行参数。
第一个元素将是process.execPath
。如果需要访问argv[0]的原始值,请使用process.argv0
。第二个元素将是正在执行的JavaScript文件的路径。剩下的元素将是任何额外的命令行参数。
const process = require('node:process');
/*
[
'/xxx/node/v20.8.0/bin/node',
'/xxx/node-1/Node核心模块使用/10.process/index.js'
]
*/
console.log(process.argv);
// node
console.log(process.argv0);
- process.cwd()
返回Node.js
进程的当前工作目录。
const process = require('node:process');
// /xxxx/Node核心模块使用/10.process
console.log(process.cwd());
- process.chdir(directory)
更改Node.js进程的当前工作目录。如果目录不存在,则抛出异常。
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}`);
}
- process.env
返回一个包含用户环境的对象。可以手动添加一些环境信息到对象里面。
const process = require('node:process');
console.log(process.env);
process.env.mode = 'development';
console.log(process.env);
3.13 Buffer模块
Buffer对象表示固定长度的字节序列。许多Node.js API都支持Buffer。在日常工作开发中,我们基本不会用到这个模块。
虽然Buffer类在全局范围内可用,但仍然建议通过import或require语句显式引用它。
- Buffer.alloc(size, fill, encoding)
创建buffer对象。默认填充为0。
// 创建长度为5的缓冲区
const { Buffer } = require('node:buffer');
const buf1 = Buffer.alloc(5);
// <Buffer 00 00 00 00 00>
console.log(buf1);
// 创建长度为5的缓冲区,并将内容填充为a
const buf2 = Buffer.alloc(5, 'a');
// <Buffer 61 61 61 61 61>
console.log(buf2);
// 创建长度为11的缓冲区,并指定填充内容的编码格式(内容为:hello world)
const buf3 = Buffer.alloc(11, 'aGVsbG8gd29ybGQ=', 'base64');
// <Buffer 68 65 6c 6c 6f 20 77 6f 72 6c 64>
console.log(buf3);
- Buffer.from(string | buffer)
根据已有的buffer或者字符串创建Buffer对象。
const { Buffer } = require('node:buffer');
// 根据字符串创建
const buf = Buffer.from('hello world', 'utf8');
// <Buffer 68 65 6c 6c 6f 20 77 6f 72 6c 64>
console.log(buf);
console.log(buf.toString());
// 根据buffer对象创建
const buf1 = Buffer.from('buffer');
const buf2 = Buffer.from(buf1);
buf1[0] = 0x61;
// auffer
console.log(buf1.toString());
// buffer
console.log(buf2.toString());
- Buffer.byteLength(string, encoding)
返回字符串编码后的字节长度。
const { Buffer } = require('node:buffer');
const str = '你好,world';
// 8字符,12字节
console.log(`${str.length}字符,${Buffer.byteLength(str, 'utf8')}字节`);
- Buffer.concat(list, totalLength)
返回结合后最新的buffer对象。
const { Buffer } = require('node:buffer');
const buf1 = Buffer.alloc(10);
const buf2 = Buffer.alloc(14);
const buf3 = Buffer.alloc(18);
const totalLength = buf1.length + buf2.length + buf3.length;
const bufA = Buffer.concat([buf1, buf2, buf3], totalLength);
// 42
console.log(bufA.length);
- Buffer.isBuffer(obj)
如果是buffer对象,则返回true。
const { Buffer } = require('node:buffer');
Buffer.isBuffer(Buffer.alloc(10)); // true
Buffer.isBuffer([]); // false
- buf.fill(value, offset, end, encoding)
const { Buffer } = require('node:buffer');
const buf = Buffer.alloc(10);
buf.fill('h', 2, 8, 'utf8');
// <Buffer 00 00 68 68 68 68 68 68 00 00>
console.log(buf);
- buf.includes(value)
如果value在buf中被找到,则返回true。
const { Buffer } = require('node:buffer');
const buf = Buffer.from('this is a buffer');
// true
console.log(buf.includes('this'));
- buf.indexOf(value)
查找value在buffer中的索引,没有找到则返回-1。
const { Buffer } = require('node:buffer');
const buf = Buffer.from('this is a buffer');
// 0
console.log(buf.indexOf('this'));
- buf.subarray(start, end)
根据start和end截取出一个新的buffer对象,原对象不会受影响。
const { Buffer } = require('node:buffer');
const buf = Buffer.from('this is a buffer');
const subBuf = buf.subarray(0, 4);
console.log(buf.toString());
console.log(subBuf.toString());