Node.js 使用 CommonJS 模块系统(v12以上也支持ESM),而在浏览器使用 ESM 标准。
这意味着在 Node.js(包括webpack) 中使用 require(),而在浏览器中则使用 import。
NodeJs和Chrome中的JS均基于V8引擎,NodeJs甚至可以开发桌面应用(如electron开发的VSCode和WhatsApp)。
环境变量
process 模块提供了 env 属性,该属性承载了在启动进程时设置的所有环境变量。
process.env.NODE_ENV // 默认为"development"
从命令行获取参数
node app.js A B
//app.js
process.argv.forEach((val, index) => {
console.log(`${index}: ${val}`)
})
//log
0: C:\Program Files\nodejs\node.exe
1: C:\Users\62406\Desktop\other\node\node.js
2: A
3: B
当文件发生改动时自动重启应用
node --watch app.js
命令行模式
在命令行中输入node进入node环境REPL模式。
-
.editor:允许输入多行 -
.load:加载 JavaScript 文件 -
.save:将你在 REPL 会话中输入的所有内容保存到文件(指定文件名)
接受来自命令行的输入
const readline = require('node:readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
rl.question(`What's your name?`, name => {
console.log(`Hi ${name}!`);
rl.close();
});
util.promisify
内置方法,将基于回调的函数转换为 Promise 函数。
class MyClass {
myCallbackFn(cb) {
cb(null, this)
}
}
const obj = new MyClass()
// 为保留上下文(使 this 指向不变),因为 promisified 是 obj 的属性
obj.promisified = require('util').promisify(obj.myCallbackFn)
const context = await obj.promisified()
console.log(context === obj) // true
事件
通过events模块构建了类似浏览器中的事件系统
const EventEmitter = require('events')
const eventEmitter = new EventEmitter()
eventEmitter.on('start', number => {
console.log(`开始 ${number}`)
})
eventEmitter.emit('start', 23)
- once(): 添加单次监听器。
- removeListener() / off(): 从事件中移除事件监听器。
- removeAllListeners(): 移除事件的所有监听器。
路径
使用path模块管理路径
const path = require('node:path');
const notes = '/users/joe/notes.txt';
path.dirname(notes); // /users/joe
path.basename(notes); // notes.txt
path.extname(notes); // .txt
- 使用
path.join()连接路径的两个或多个部分:
const name = 'joe';
path.join('/', 'users', name, 'notes.txt'); // '/users/joe/notes.txt'
- 使用
path.resolve()将相对路径加入到当前工作目录:
path.resolve('tmp', 'joe.txt'); // '/Users/joe/tmp/joe.txt' if run from my home folder
文件
文件读写:
- fs.open + fs.read(write),手动操作读写,优点是可以控制每次读取的位置和量:
const fs = require('fs');
fs.open('file.txt', 'r', (err, fd) => {
if (err) throw err;
const buffer = Buffer.alloc(1024);
fs.read(fd, buffer, 0, buffer.length, 0, (err, bytesRead) => {
if (err) throw err;
console.log(buffer.toString('utf8', 0, bytesRead));
fs.close(fd, (err) => { if (err) throw err; });
});
});
- fs.readFile(writeFile),一次性读取整个文件,但对大文件内存占用较多:
const fs = require('fs');
fs.readFile('file.txt', 'utf8', (err, data) => {
if (err) throw err;
console.log(data);
});
- (推荐)fs.createReadStream(createWriteStream),流式处理,内存占用极少(分块读取),还方便管道传输到其他流(HTTP响应、压缩流等):
// 现代推荐组合:流 + 异步迭代
const fs = require('fs');
async function processLargeFile() {
const stream = fs.createReadStream('massive.csv');
let lineCount = 0;
for await (const chunk of stream) {
lineCount += chunk.toString().split('\n').length - 1;
}
console.log(`Total lines: ${lineCount}`);
}
以上方法都有其同步版本,如fs.openSync、fs.readFileSync。
在Node14+版本还可以使用fs/promises模块下的Promise版本:
const fs = require('node:fs/promises');
// Or const fs = require('fs').promises before v14.
async function example() {
let filehandle;
try {
filehandle = await fs.open('/Users/joe/test.txt', 'r');
console.log(filehandle.fd);
console.log(await filehandle.readFile({ encoding: 'utf8' }));
} finally {
if (filehandle) await filehandle.close();
}
}
example();
老版本可以用util.promisify转化出 Promise 版本:
const fs = require('node:fs');
const util = require('node:util');
async function example() {
const open = util.promisify(fs.open);
const fd = await open('/Users/joe/test.txt', 'r');
}
example();
其他文件操作方法
- fs.stat 获取文件/目录信息
- fs.access 判断文件/目录是否存在
- fs.copyFile 复制文件
- fs.mkdir 创建目录
- fs.readdir 读取目录内容
- fs.unlink 删除文件/目录
- fs.rename 重命名文件/目录
fs.open的几种模式
模式 描述
r 读取(默认),文件不存在则报错,指针在开头
r+ 读写,文件不存在则报错,指针在开头
w 写入,文件不存在则创建,存在则清空,指针在开头
w+ 读写,文件不存在则创建,存在则清空,指针在开头
a 追加写入,文件不存在则创建,指针在末尾
a+ 读取和追加写入,文件不存在则创建,指针在末尾
测试
在项目中创建.test.js后缀文件,并通过node --test即可自动搜索并运行
const { test, describe } = require('node:test');
const assert = require('node:assert/strict');
// 简单测试用例
test('同步测试示例', (t) => {
assert.equal(1, 1);
});
// 异步测试
test('异步测试示例', async (t) => {
const result = await someAsyncFunction();
assert.ok(result);
});
- 检查测试代码覆盖率
node --experimental-test-coverage --test main.test.js