前端宝典十二:node基础模块和常用API

Node.js是开源、跨平台的JavaScript运行时环境,构建在Chromev8引擎之上。
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代码以供重复使用的官方标准格式。模块是使用importexport 相关的语句定义的。

ECMAScript和CommonJs区别

  1. ESM没有 require 、 exports 、 module.exports ,使用的是import、export
  2. ESM没有 __filename 、 __dirname 。
  3. ESM没有 require.resolve ,不过可以使用 import.meta.resolve 。
  4. 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');
 // 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());

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'); 2
 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, ';', ':'));

6、Path模块

Path模块提供了用于处理文件和目录路径的实用程序。

1. path.basename(path, suffix)返回路径的最后一部分。

const path = require('node:path'); 2
 // index.html
 path.basename('/foo/bar/baz/asdf/index.html');

 // index
 path.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 error
  console.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 error
 	console.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 error
    console.log(err);
}

9. unlinkSync删除文件

const { unlinkSync } = require('node:fs');
try {
	unlinkSync('./temp');
} catch (err) {
// handle the error
    console.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();
  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值