【node】见面会之常用基础模块

198 阅读9分钟

了解内置模块

node.js的内置模块就是node本身提供的模块,不用下载,直接使用require引入就可以使用这些模块的方法了。而这些模块在浏览器环境中是不能执行的。以下与大家见面的是我们在node中常用的一些模块,如有记录不完善或者想查看更多的,请前往 nodejs.cn/api/ 了解更多。

path模块

path用于处理文件和目录的路径。

__dirname 返回当前文件所在目录的绝对路径

const path = require("path");
console.log(__dirname);

path.join([...paths]) 用于路径的拼接

const path = require("path");
//路径的拼接
console.log(path.join("/node","index.json"));

path.resolve([...paths]) 将路径或路径片段的序列解析为绝对路径

const path = require("path");
console.log(path.resolve("."));
console.log(path.resolve(".."));
console.log(path.resolve("test1","test2/test3"));
//如果没有传入 path 片段,则 path.resolve() 会返回当前工作目录的绝对路径

path.basename(path[, ext]) 返回 path 的最后一部分

const path = require("path");
console.log(path.basename("/user/test/index.html"));
console.log(path.basename("/user/test/index.html",".html"));

fs模块

fs用于文件或文件夹的读写操作,较常用的是以下几种:

fs.mkdir(path[,options],callback) 异步 新建文件夹

//有三个参数,第一个参数是地址,就是你创建的文件夹存放的地址
//第二个参数不是必填的参数, options 参数可以是一个整数,用于指定文件的模式(权限与粘滞位)。 也可以是一个对象,其中 recursive 属性指定是否创建父目录。
//第三个参数是回调函数,参数就是err错误
 
fs.mkdir('/tmp/test/web', { recursive: true }, (err) => {
  if (err) throw err;
});

fs.mkdirSync(path[, options]) 同步 新建文件夹

//同步和异步的区别就不多说了,在写法上就是同步的方法没有回调函数
//同步调用若是出错,都会终止执行,必须用try catch来捕获错误
拿同步新建文件夹举例:
try{
    const result = mkdirSync("./test");
}catch(e){
    console.log(new Error(e));
}

fs.rmdir(path,callback) 异步 删除文件夹

fs.rmdir("./test",(err)=>{
    console.log(err);
})

fs.rmdirSync(path) 同步 删除文件夹

fs.rmdirSync("./test");

fs.readdir(path[,options],callback) 异步 读取文件夹

//读取文件夹的时候,回调函数中会有两个参数,一个是错误,另一个就是读取的文件
//如果 options 是一个字符串,则指定字符编码。 如果 encoding 设为 'buffer',则返回的文件名是 Buffer,可以定义为 utf8。
 
fs.readdir("./test",(err,files)=>{
    console.log(err);
    console.log(files); // 文件夹中的文件,以数组的形式存放
})

fs.readdirSync(path[, options]) 同步 读取文件夹

//如果 options 是一个字符串,则指定字符编码。 如果 encoding 设为 'buffer',则返回的文件名是 Buffer,可以定义为 utf8。

const a = fs.readdirSync('./');
console.log(a); // 文件夹中的文件,以数组的形式存放

fs.readFile(path[, options],callback); 异步 读文件

fs.readFile("../test/test.json",(err,data)=>{
  if(err){
    console.log(err);
  }else{
    console.log(JSON.parse(data));
  }
});

//callback回调函数中有两个参数,一个是err,另一个是data,读取的数据,一般data的格式都要改变,两种方法:
1.可以写成data.toString;
2.可以调用方法的时候多加一个参数,改变数值类型的 fs.readFile(file,"utf8",callback);

fs.readFileSync(path[, options]) 同步 读文件

const data = fs.readFileSync('../test/test.txt','utf8');

fs.writeFile(file, data[, options], callback) 异步 写文件

fs.writeFile('./test/index.json',JSON.stringify(data),(err)=>{
  if(err){
  	console.log(err);
  }
});
//如果没有该文件,会先创建该文件,若存在,会覆盖文件内容

fs.writeFileSync(file, data[, options]) 同步 写文件

try{
  fs.writeFileSync('./test/index.json',JSON.stringify(data));
}catch(err){
  console.log(err);
}

//如果没有该文件,会先创建该文件,若存在,会覆盖文件内容

fs.appendFile(file, data[, options], callback) 异步 不覆盖写文件

fs.appendFile("./file.txt",data,(err)=>{
    console.log(err);
});
// 就是writeFile换成了appendFile,就可以不覆盖的写入内容了

fs.appendFileSync(file, data[, options]) 同步 不覆盖写文件

fs.appendFileSync("./file.txt",data);

fs.unlink(path,callback) 异步 删除文件

fs.unlink("./file.txt",(err)=>{
    console.log(err);
});

fs.unlinkSync(path) 同步 删除文件

fs.unlink("./file.txt");

url模块

使用传统的 API 解析 URL 字符串

const url = require("url");
const myURL = url.parse('https://user:pass@sub.host.com:8080/p/a/t/h?query=string#hash');

console.log(myURL);


/* 打印结果为解析后的URL对象
Url {
  protocol: 'https:',
  slashes: true,
  auth: 'user:pass',
  host: 'sub.host.com:8080',
  port: '8080',
  hostname: 'sub.host.com',
  hash: '#hash',
  //查询字符串,这里,如果我们想要以对象形式返回,可以让url.parse的第二个参数为true
  search: '?query=string',
  query: 'query=string',
  pathname: '/p/a/t/h',
  path: '/p/a/t/h?query=string',
  href: 'https://user:pass@sub.host.com:8080/p/a/t/h?query=string#hash'
}
*/

使用 WHATWG 的 API 解析 URL 字符串

const {URL} = require("url");
const myURL = new URL('https://user:pass@sub.host.com:8080/p/a/t/h?query=string#hash');

console.log(myURL);

/* 打印结果为解析后的URL对象
Url {
  protocol: 'https:',
  slashes: true,
  auth: 'user:pass',
  host: 'sub.host.com:8080',
  port: '8080',
  hostname: 'sub.host.com',
  hash: '#hash',
  search: '?query=string',
  query: 'query=string',
  pathname: '/p/a/t/h',
  path: '/p/a/t/h?query=string',
  href: 'https://user:pass@sub.host.com:8080/p/a/t/h?query=string#hash'
}
*/

querystring模块

queryString就是查询字符串,模块提供了一些实用函数,用于解析与格式化 URL 查询字符串

querystring.parse(str[, sep[, eq[, options]]]) 解析URL的查询字符串

- str <string> 要解析的 URL 查询字符串。
- sep <string> 用于在查询字符串中分隔键值对的子字符串。默认值: '&'。
- eq <string> 用于在查询字符串中分隔键和值的子字符串。默认值: '='。
- options <Object>:
  decodeURIComponent <Function> 当解码查询字符串中的百分比编码字符时使用的函数。默认  值: querystring.unescape()。  
  maxKeys <number> 指定要解析的键的最大数量。指定 0 可移除键的计数限制。默认值: 1000const qs = require('querystring');
const b = qs.parse("foo=bar&abc=xyz&abc=123");
console.log(b);

//{ foo: 'bar', abc: [ 'xyz', '123' ] }

//默认情况下,会假定查询字符串中的百分比编码字符使用 UTF-8 编码。 如果使用其他的字符编码,则需要指定其他的 decodeURIComponent 选项:

querystring.parse('w=%D6%D0%CE%C4&foo=bar', null, null, { decodeURIComponent: gbkDecodeURIComponent });

querystring.stringify(obj[, sep[, eq[, options]]]) 序列化字符串为url查询字符串

- obj <Object> 要序列化为 URL 查询字符串的对象。
- sep <string> 用于在查询字符串中分隔键值对的子字符串。默认值: '&'。
- eq <string> 用于在查询字符串中分隔键和值的子字符串。默认值: '='。
- options:
encodeURIComponent <Function> 当将查询字符串中不安全的 URL 字符转换为百分比编码时使用的函数。默认值: querystring.escape()。

querystring.stringify({ foo: 'bar', baz: ['qux', 'quux'], corge: '' });
// 返回 'foo=bar&baz=qux&baz=quux&corge='

querystring.stringify({ foo: 'bar', baz: 'qux' }, ';', ':');
// 返回 'foo:bar;baz:qux'

//默认情况下,查询字符串中需要进行百分比编码的字符将会被编码为 UTF-8。 如果需要其他的编码,则需要指定其他的 encodeURIComponent 选项:


querystring.stringify({ w: '中文', foo: 'bar' }, null, null, { encodeURIComponent: gbkEncodeURIComponent });

events模块

EventEmitter

events 模块提供了一个对象: events.EventEmitter

EventEmitter 的核心就是事件触发与事件监听器功能的封装

EventEmitter 的每个事件由一个事件名和若干个参 数组成,事件名是一个字符串,通常表达一定的语义。对于每个事件,EventEmitter 支持 若干个事件监听器。当事件触发时,注册到这个事件的事件监听器被依次调用,事件参数作 为回调函数参数传递

const EventEmitter = require('events'); 
class MyEmitter extends EventEmitter {}

const myEmitter = new MyEmitter();

//eventEmitter.on() 用于注册监听器, eventEmitter.emit() 用于触发事件
myEmitter.on('eventName', () => {
  console.log('事件被触发了');
});
myEmitter.emit('eventName'); 

eventEmitter.emit(eventName,callback) callback中this

eventEmitter.emit() 方法可以传任意数量的参数到监听器函数。 当监听器函数被调用时, this 关键词会被指向监听器所绑定的 EventEmitter 实例

const myEmitter = new MyEmitter();
myEmitter.on('event', function(a, b) {
  console.log(a, b, this, this === myEmitter);
  // 打印:
  //   aa bb MyEmitter {
  //     domain: null,
  //     _events: { event: [Function] },
  //     _eventsCount: 1,
  //     _maxListeners: undefined } true
});
myEmitter.emit('event', 'aa', 'bb');

也可以使用 ES6 的箭头函数作为监听器。但 this 关键词不会指向 EventEmitter 实例

const myEmitter = new MyEmitter();
myEmitter.on('event', (a, b) => {
  console.log(a, b, this);
  // 打印: aa bb {}
});
myEmitter.emit('event', 'aa', 'bb');

eventEmitter.once()

使用 eventEmitter.once() 可以注册最多可调用一次的监听器。 当事件被触发时,监听器会被注销

const myEmitter = new MyEmitter();
let m = 0;
myEmitter.once('event', () => {
  console.log(++m);
});
myEmitter.emit('event');
// 打印: 1
myEmitter.emit('event');
// 不触发

emitter.removeListener(eventName, listener)

emitter.removeListener(eventName, listener) 从名为 eventName 的事件的监听器数组中移除指定的 listener

const callback = (stream) => {
  console.log('已连接');
};
server.on('connection', callback);
// ...
server.removeListener('connection', callback);

emitter.removeAllListeners([eventName])

emitter.removeAllListeners([eventName]) 移除全部的事件侦听

server.removeAllListeners(['connection']);

stream(流)

流(stream)是 Node.js 中处理流式数据的抽象接口

Node.js 中有四种基本的流类型:

Writable - 可写入数据的流(例如 fs.createWriteStream())。 Readable - 可读取数据的流(例如 fs.createReadStream())。 Duplex - 可读又可写的流(例如 net.Socket)。 Transform - 在读写过程中可以修改或转换数据的 Duplex 流(例如 zlib.createDeflate())。 此外,该模块还包括实用函数 stream.pipeline()、stream.finished() 和 stream.Readable.from()。

几乎所有的 Node.js 应用都在某种程度上使用了流。 下面是一个例子,使用流实现了一个 HTTP 服务器:

const http = require('http');

const server = http.createServer((req, res) => {
  // req 是一个 http.IncomingMessage 实例,它是可读流。
  // res 是一个 http.ServerResponse 实例,它是可写流。

  let body = '';
  // 接收数据为 utf8 字符串,
  // 如果没有设置字符编码,则会接收到 Buffer 对象。
  req.setEncoding('utf8');

  // 如果添加了监听器,则可读流会触发 'data' 事件。
  req.on('data', (chunk) => {
    body += chunk;
  });

  // 'end' 事件表明整个请求体已被接收。 
  req.on('end', () => {
    try {
      const data = JSON.parse(body);
      // 响应信息给用户。
      res.write(typeof data);
      res.end();
    } catch (er) {
      // json 解析失败。
      res.statusCode = 400;
      return res.end(`错误: ${er.message}`);
    }
  });
});

server.listen(1337);

fs.createReadStream(path[, options]) 读取文件流

可读流的事件请具体参考 nodejs.cn/api/stream.… 文档

let fs = require("fs");
let times = 0;
let stream = fs.createReadStream('../test.txt', { start: 10, end: 99 }});
stream.on("data", chunk => {
    console.log("第"+(times++)+"次读取的块内容:");
});
stream.on("end", () => {
    console.log("END");
});
stream.on("error", err => {
    console.log(err);
});

fs.createWriteStream(path[, options]) 写入文件流

可写流的事件请具体参考 nodejs.cn/api/stream.… 文档

let fs = require('fs');
let stream = fs.createWriteStream('../test.txt', { encoding: 'utf8' });
stream.write("要写入的文本1");
stream.write("要写入的文本2");
// ...
//二进制
//stream.write(new Buffer("文本3", 'utf8'));
//stream.write(new Buffer("文本4", 'utf8'));
// ...
stream.end(); // 终止写入

readable.pipe(writable[,options]) 把一个Readable流和一个Writeable流串起来

两个流是可以串起来的,比如把一个Readable流和一个Writeable流串起来,此时,数据就会自动从Readable流进入Writeable流,此操作成为pipe

let fs = require('fs');
let rs = fs.createReadStream("A.txt");
let ws = fs.createWriteStream("B.txt");

rs.pipe(ws);

上述代码就可以实现A.txt中的内容写入到B.txt中,默认情况下,当Readable流中的数据读取完毕后,就会触发end事件,然后自动关闭Writeable流,我们可以通过以下设定来取消自动关闭Writeable流

rs.pipe(ws, { end: false });

http模块

发起http请求

http.request(options[, callback]) / http.request(url[, options][, callback])

url 可以是字符串或 URL 对象。 如果 url 是一个字符串,则会自动使用 [url.URL()] 解析它。 如果它是一个 URL 对象,则会自动转换为普通的 options 对象。

如果同时指定了 url 和 options,则对象会被合并,其中 options 属性优先。

可选的 callback 参数会作为单次监听器被添加到 'response' 事件。

官方事例:

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();

在示例中调用了 req.end()。 使用 http.request() 时,必须始终调用 req.end() 来表示请求的结束,即使没有数据被写入请求主体

http.get(options[, callback]) / http.get(url[, options][, callback])

http
    .get(options, response => {
      response.setEncoding('utf8');
      let data = "";
      response.on("data", (chunk) => {
        data += chunk;
      });
      response.on("end", () => {
        console.log("ok");
      });
    })
    .on("error", (err) => {
      console.log("TopicList Request Error: " + err.message);
    });

options 接受与 http.request() 相同的 options,但是 method 始终被设置为 GET。 从原型继承的属性会被忽略

使用http模块搭建服务器 http.createServer([options][, requestListener])

Node.js的http模块,已经帮助我们隐藏了处理TCP连接的具体细节,使得我们可以在更高的层次上直接处理,主要是使用http模块提供的request和response对象

request对象封装了http请求 response对象封装了http相应

1、创建一个http服务器(假设文件在server.js):

const http = require('http');

const server = http.createServer((request, response) => {
    response.writeHead(200, {'Content-Type': 'text/html'});
    response.end("This is a server by Node.js");
});

server.listen(1234);

这样子我们就完成了一个http服务器的创建了,使用node server.js开启,然后只要访问http://localhost:1234,就会看到页面上输出:This is a server by Node.js

2、我们可以看到http.createServer中有两个参数request和response,这两个参数,request是用来获得客户端发送的HTTP请求的(request对象中包含了许多的请求信息),而response是用来让服务器做出回应的

process

process.nextTick(callback[, ...args])

关于process.nextTick后面我们会着重去讲解

crypto模块

crypto 模块提供了加密功能,包括对 OpenSSL 的哈希、HMAC、加密、解密、签名、以及验证功能的一整套封装

这块内容,也会单独写一篇文章来深度分析一下

util实用工具

具体内容请参考 nodejs.cn/api/util.ht…

以上这些都是平时在使用node过程中比较常用的一些内容,当然如果说的不全,也希望各位多多包涵,没写全的内容请各位看官到官网去深度挖掘和吸收哦,谢谢~