Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

学习 node 内置模块 #18

Open
ZhenHe17 opened this issue Jan 16, 2020 · 0 comments
Open

学习 node 内置模块 #18

ZhenHe17 opened this issue Jan 16, 2020 · 0 comments

Comments

@ZhenHe17
Copy link
Owner

ZhenHe17 commented Jan 16, 2020

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:[email protected]:8080/p/a/t/h?query=string#hash');
const newUrl =
  new URL('https://user:[email protected]: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();

扩展阅读

NodeJs 中的 http、https 和 http2

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 提供的模块可以让我们在开发时得心应手、游刃有余。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant