node 提供在服务端执行JS的环境,我们基于 node 提供的模块和功能来开发应用
学习列表
- global
- http、https、http2
- v8
- child_process
- cluster
- fs
- events
- util
global 全局对象
global 全局对象可以在任何地方访问到,所有其他的全局变量都是 global 的属性。这和浏览器中的window 对象一样。 global 最根本的作用是作为全局变量的宿主。在最外层定义的变量、global对象的属性、未定义直接赋值的变量都属于全局变量。 node 执行的 js 文件属于一个模块,模块不是最外层的,所以文件中声明的变量不会是全局变量。
// $ node test.js
var X = 123
console.log(global.X) // -> undefined
Y = 3333
console.log(global.Y) // -> 3333
global 对象的内置属性
- __dirname
- __filename
- setImmediate(callback[, ...args])
- setInterval(callback, delay[, ...args])
- setTimeout(callback, delay[, ...args])
- clearImmediate(immediateObject)
- clearInterval(intervalObject)
- clearTimeout(timeoutObject)
- console
- exports
- module
- require()
- global
以上几个大家都比较熟悉了
Buffer 类
用于处理二进制数据,Buffer类的实例大小是固定的、在V8堆内存外分配内存。 它和 typedArray 中的 Uint8Array 几乎一样,是由8位整数组成的数组。 node 中读取图片等文件时会返回 buffer 类型的数据
fs.readFile('logo.png',function (err, origin_buffer) {
console.log(Buffer.isBuffer(origin_buffer))
})
process
process 提供当前进程的信息和控制进程的方法,比如:
- exit([code]) 退出进程
- pid 进程号
- arch 当前 CPU 的架构
- 监听进程事件:
process.on('exit', function(code) {
console.log('退出码为:', code);
});
queueMicrotask(callback)
将 callback 放入当前微任务队列中执行。
const p = new Promise((resolve, reject)=>{
resolve()
})
p.then(()=>{
console.log('promise');
})
queueMicrotask(() => {
console.log('queueMicrotask');
});
process.nextTick(()=>{
console.log('nextTick');
})
console.log('task');
输出
task
nextTick
promise
queueMicrotask
TextEncoder
把 UTF-8 编码的值转为 uint8array
const encoder = new TextEncoder();
const uint8array = encoder.encode('这是一些数据');
TextDecoder
还原 TextEncoder 的结果
URL
url 模块提供了两套 API 来处理 URL:一个是旧版本遗留的 API,一个是实现了 WHATWG标准的新 API。
const url = require('url');
const pastUrl =
url.parse('https://user:pass@sub.host.com:8080/p/a/t/h?query=string#hash');
const newUrl =
new URL('https://user:pass@sub.host.com:8080/p/a/t/h?query=string#hash');
返回 url 中 origin、pathname、search 等信息。
URLSearchParams
URL实例中的 searchParams 就是 URLSearchParams 的实例,searchParams 是解析 search 得来的键值对,URLSearchParams提供了 set、append、delete 方法处理键值对
const myURL = new URL('https://example.org/?abc=123');
console.log(myURL.searchParams.get('abc'));
// 打印 123
myURL.searchParams.append('abc', 'xyz');
console.log(myURL.href);
// 打印 https://example.org/?abc=123&abc=xyz
WebAssembly
把 C/C++ 等其他语言的代码编译成 .wasm 文件并执行! 这太酷了 中文文档 MDN教程
http、https、http2
这些模块实现了 http、https、http2 协议,也是服务端开发最常用的模块。 常用的方法有:
- http.request(url[, options][, callback]) 发起请求
- http.createServer([options][, requestListener]) 创建一个服务器实例
常用内置类:
- Server 类,listen 方法启动一个服务:
const http = require('http');
const server = http.createServer((req, res) => {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain');
res.end('Hello, World!\n');
});
server.listen(3000, '127.0.0.1');
express 中的 app.listen() :
app.listen = function listen() {
var server = http.createServer(this);
return server.listen.apply(server, arguments);
};
- ClientRequest 类,http.request() 会返回一个 ClientRequest 的实例,通过监听它的事件、调用 write、end 等方法来控制请求。
- ServerResponse 类,包含响应的信息和操作响应数据的方法
下面是完整请求流程的例子:
const postData = querystring.stringify({
'msg': '你好世界'
});
const options = {
hostname: 'nodejs.cn',
port: 80,
path: '/upload',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': Buffer.byteLength(postData)
}
};
const req = http.request(options, (res) => {
console.log(`状态码: ${res.statusCode}`);
console.log(`响应头: ${JSON.stringify(res.headers)}`);
res.setEncoding('utf8');
res.on('data', (chunk) => {
console.log(`响应主体: ${chunk}`);
});
res.on('end', () => {
console.log('响应中已无数据');
});
});
req.on('error', (e) => {
console.error(`请求遇到问题: ${e.message}`);
});
// 将数据写入请求主体。
req.write(postData);
req.end();
扩展阅读
v8
v8 模块主要提供查询堆内存信息的方法和序列化 buffer 的方法
getHeapStatistics()
返回有关 V8 堆空间的统计信息
{
total_heap_size: 7326976,
total_heap_size_executable: 4194304,
total_physical_size: 7326976,
total_available_size: 1152656,
used_heap_size: 3476208,
heap_size_limit: 1535115264,
malloced_memory: 16384,
peak_malloced_memory: 1127496,
does_zap_garbage: 0,
number_of_native_contexts: 1,
number_of_detached_contexts: 0
}
可以通过观察 number_of_native_contexts 和 number_of_detached_contexts 的值来判断内存泄漏的情况:
-
number_of_native_contexts native_context 的值是当前活动的顶层上下文的数量。 随着时间的推移,此数字的增加表示内存泄漏。
-
number_of_detached_contexts detached_context 的值是已分离但尚未回收垃圾的上下文数。 该数字不为零表示潜在的内存泄漏。
serialize(value)、deserialize(buffer)
serialize(value) 将 value 转换成 buffer,deserialize 将 buffer 转换回来。
child_process 子进程
child_process 允许我们创建一个子进程来执行命令或者 js 文件
exec、execFile、fork、spawn 方法都会返回 ChildProcess 实例。
- exec 执行一个命令行语句
- execFile 执行文件
- fork 生成的子进程可以与父进程通信
- spawn 是上面三个方法的基础
下面代码创建了一个执行 node test.js 命令的子进程:
var workerProcess = child_process.exec('node test.js ', function (error, stdout, stderr) {
console.log('stdout: ' + stdout);
console.log('stderr: ' + stderr);
});
workerProcess.on('exit', function (code) {
console.log('子进程已退出,退出码 '+code);
});
cluster 集群
cluster 是为了能充分利用多核系统而提供的模块,它可以创建共享服务器端口的子进程。而 cluster 的底层是用 child_process 实现的。
根据 CPU 核的数量创建子进程
const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;
if (cluster.isMaster) {
console.log(`主进程 ${process.pid} 正在运行`);
// 衍生工作进程。
for (let i = 0; i < numCPUs; i++) {
cluster.fork();
}
cluster.on('exit', (worker, code, signal) => {
console.log(`工作进程 ${worker.process.pid} 已退出`);
});
} else {
// 工作进程可以共享任何 TCP 连接。
// 在本例子中,共享的是 HTTP 服务器。
http.createServer((req, res) => {
res.writeHead(200);
res.end('Hello, World\n');
}).listen(8000);
console.log(`工作进程 ${process.pid} 已启动`);
}
这段代码会带来一个疑惑,为什么所有子进程可以重复监听 8000 端口。这是因为 cluster 模块修改了 listen 方法,端口仅由 master 进程监听了一次。
在实践中,部分业务不需要也不能够由多个子进程一起做(比如每日的定时处理脚本),egg 框架里提出了 Agent 机制。在一个 Master、多个 Worker 的下新加了一个 Agent。Agent 好比是 Master 给其他 Worker 请的一个『秘书』,它不对外提供服务,只给 App Worker 打工,专门处理一些公共事务。
扩展阅读
通过源码解析 Node.js 中 cluster 模块的主要功能实现
fs
fs 模块提供与文件系统进行交互的 API。
写入文件:
const data = new Uint8Array(Buffer.from('Node.js中文网'));
fs.writeFile('文件.txt', data, (err) => {
if (err) throw err;
console.log('文件已被保存');
});
读取文件:
fs.readFile('/etc/passwd', (err, data) => {
if (err) throw err;
console.log(data);
});
删除整个文件夹:
function delDir(path){
let files = [];
if(fs.existsSync(path)){
files = fs.readdirSync(path);
files.forEach((file, index) => {
let curPath = path + "/" + file;
if(fs.statSync(curPath).isDirectory()){
delDir(curPath); //递归删除文件夹
} else {
fs.unlinkSync(curPath); //删除文件
}
});
fs.rmdirSync(path);
}
}
util
util.callbackify(original)
将 async 函数或者返回 promise 的函数转成回调风格的函数
const util = require('util');
async function fn() {
return 'hello world';
}
const callbackFunction = util.callbackify(fn);
callbackFunction((err, ret) => {
if (err) throw err;
console.log(ret);
});
util.promisify(original)
将回调风格的函数转成返回 promise 的函数
util.deprecate(fn, msg[, code])
废弃一个函数,调用被废弃的函数会触发警告
util.isDeepStrictEqual(val1, val2)
计算 val1 和 val2 是否深度相等
util.types
提供对内置对象进行类型检查的方法,比如:
- util.types.isDate(value)
- util.types.isPromise(value)
- util.types.isProxy(value)
- util.types.isRegExp(value)
- util.types.isMap(value)
- util.types.isSet(value)
总结
随着 node 版本的不断更新,带来了很多激动人心的功能和更强大的性能。充分了解 node 提供的模块可以让我们在开发时得心应手、游刃有余。