path

path模块用于对路径和文件进行处理,提供了很多便捷的API方法。另外路径在不同系统的电脑上有着一些区别,比如路径分隔符在windowsmac就不一样。而path这个模块考虑到这个问题内部已经做了适配,可以根据不同的系统来对路径做出处理。

这里可以去了解下可移植操作系统接口(Portable Operating System Interface,缩写为POSIX)。

LinuxMac OS都实现了POSIX,而windows部分实现了。

1. 获取路径信息

dirname:获取文件的父文件夹路径

//传入一个路径
const path = require("path");
console.log(path.dirname("nihao/hello/index.js")); //获取父文件夹

输出如下:

nihao/hello

basename:获取文件名

const path = require("path");
//传入一个路径
console.log(path.basename("index.js"));
console.log(path.basename("hello/nihao"));

输出如下:

index.js
nihao

extname:获取文件扩展名

const path = require("path");
//传入一个路径
console.log(path.extname("index.js"));
console.log(path.extname("path.sj.js"));

输出如下:

.js
.js

2.路径的拼接

对于路径的拼接我们可以自己进行字符串的拼接,但是对于不同的操作系统就需要做不同的处理,使用path中提供的方法就不用考虑这些问题了。

join:用于将多个路径进行拼接

js数组中这个函数是将一个数组(或一个类数组对象)的所有元素连接成一个字符串并返回这个字符串。如果数组只有一个项目,那么将返回该项目而不使用分隔符。在path模块中使用方法如下:


console.log(path.join("/one", "tow", "three", "."));
console.log(path.join("/one", "tow", "three", ".."));
console.log(path.join("/one", "/tow", "../three"));

输出结果:

\one\tow\three
\one\tow
\one\three

resolve:将路径或路径片段的序列解析为绝对路径。

这个方法也是拼接路径:

console.log(path.resolve("/one", "tow", "three", "."));
console.log(path.resolve("/one", "tow", "three", ".."));
console.log(path.resolve("/one", "/tow", "../three"));
console.log(path.resolve("/one", "/tow", "/three"));

输出结果如下:

e:\one\tow\three
e:\one\tow
e:\three
e:\three

该方法与join不同的一点在于,resolve会判断字符串开头的./, ../ ,/

从右向左进行拼接,如果能够组成绝对路径就停止拼接,并返回路径。

如果拼接完之后还不能组成就会将工作目录拼接进去形成绝对路径:

console.log(path.resolve("./one", "tow", "three"));

输出如下:

e:\code\代码笔记\Microsoft VS Code\study\深入node技术栈\one\tow\three

fs

fs表示文件系统,node也是为我们封装了这个文件操作系统,使得我们可以在任何操作系统下去操作文件。

Node的文件系统API很多,这里主要讲解一些比较常用的。

首先明白这些API的三种主要操作方式:

  • 方式一:同步操作文件,代码会被阻塞,不会继续执行
  • 方式二:异步回调操作文件,代码不会被阻塞,需要传入回调函数,但是嵌套层级多了会造成回调地狱。
  • 方式三:异步Promise操作文件,代码不会被回调,通过fs.promises或者导入fs的时候以fs/promises形式。

以下展示一个案例:获取文件状态。

方式一,同步获取文件:

const fs = require('fs');

const filePath = "./saber.txt";
const info  = fs.statSync(filePath);
console.log(info);
console.log("同步后续执行操作");

方式二,异步回调形式获取文件:

const fs = require('fs');

const filePath = "./saber.txt";
fs.stat(filePath, (err, info) => {
  if(err) {
    console.log(err)
  } else {
    console.log(info);
  }
});
console.log("异步后续执行操作")

方式三,promise方式操作:

const fs = require("fs/promises");//或者正常导入fs,但是使用的时候需要改为fs.promises.xxx
const filePath = "./saber.txt";

fs.stat(filePath)
  .then((res) => {
    console.log(res);
  })
  .catch((err) => {
    console.log(err);
  });
console.log("promise后续执行操作");

文件描述符

在POSIX系统上,对于每一个进程,内核都维护着一张当前打开的文件和资源的表格,每个打开的文件都有分配了一个简单的数字标识,称之为文件描述符。在系统级,所有的文件系统操作都使用这个文件描述符来识别和跟踪每个特定文件。

使用文件描述符来查看状态:

const fs = require('fs');

//打开文件获取文件描述符
fs.open("./saber.txt","r", (err, fd) => {
  if(err) {
    console.log(err);
    return
  }
  console.log(fd);
  fs.fstat(fd, (err, info) => {
    if(err) {
      console.log(err);
    } else {
      console.log(info);
    }
  })
})

文件的读写

文件的读取
fs.readFile(path[, options], callback)

fs.readFile('./saber.txt', {encoding:'utf8'}, (err, data) => {
  if(err) {
    console.log(err);
  } else {
    console.log(data);
  }
})

文件的写入

fs.writeFile(file, data[, options], callback)

const content = "    这里是写入的内容" + "\n";
fs.writeFile("./saber.txt", content, { flag: "a", encoding: "utf8" }, (err) => {
  console.log(err);
});

文件夹操作

创建文件

const dirname = './zhangsan';
if(!fs.existsSync(dirname)) {
  fs.mkdir(dirname, err => {
    console.log(err);
  });
}

读取文件夹

fs.readdir(dirname, (err, info) => {
  if(err) {
    console.log(err);
    return
  }
  console.log(info);
})

这种方式只能读取第一层的所有问文件,如果遇到了文件夹是不会读取了,如果要读取文件夹中所有的文件,可以使用递归读取:

function getAllFilename(dirname) {
  fs.readdir(dirname, { withFileTypes: true }, (err, info) => {
    if (err) {
      console.log(err);
      return;
    }
    for (const item of info) {
      if (item.isDirectory()) {
        const filePath = path.resolve(dirname, item.name);
        getAllFilename(filePath);
      } else {
        console.log(item.name);
      }
    }
  });
}
getAllFilename(dirname);

events模块

Node.js 的大部分核心 API 都是围绕惯用的异步事件驱动架构构建的,在该架构中,某些类型的对象(称为"触发器")触发命名事件,使 Function 对象("监听器")被调用。

发出事件和监听事件都是通过EventEmitter类来完成的,它们都属于events对象。

常见方法:

  • emitter.on(eventName, listener):监听事件,也可以使用
    addListener
  • emitter.off(eventName, listener):移除事件监听,也可以使 用removeListener
  • emitter.emit(eventName[, ...args]):发出事件,可以携带一
    些参数。

常见属性:

  • emitter.eventNames():返回当前 EventEmitter对象注册的事件字符串数组。
  • emitter.getMaxListeners():返回当前EventEmitter的最大监听器数量,可以通过setMaxListeners()来修改,默认是10。
  • emitter.listenerCount(事件名称):返回当前 EventEmitter对象某一个事件名称,监听器的个数。
  • emitter.listeners(事件名称):返回当前 EventEmitter对象某个事件监听器上所有的监听器数组。

其他方法:

  • emitter.once(eventName, listener):事件监听一次。
  • emitter.once(eventName, listener):事件监听一次。
  • emitter.prependOnceListener():将监听事件添加到最前面,但是只监听一次。
  • emitter.removeAllListeners([eventName]):移除所有的监听器。
/*
 * @Date: 2021-06-20 14:26:23
 * @LastEditors: zhangheng
 * @LastEditTime: 2021-11-07 00:36:33
 */
/**
 * Create by zhangheng on 2021/5/1
 */

const EventEmitter = require("events");

//1.创建发射器
const eventBus = new EventEmitter();

//2.监听事件
/*
 *   两种方式
 *   on 和 addListener 都一样
 * */
//监听事件
const listener1 = (args) => {
  console.log("监听1到了click事件", args);
};
eventBus.on("click", listener1);
eventBus.on("click", (args) => {
  console.log("监听2到了click事件", args);
});
eventBus.prependListener("click", () => {
  console.log("我要第一个执行");
});

eventBus.emit("click", { a: 111, b: 1111222 });
eventBus.off("click", listener1);
eventBus.emit("click", { a: 111, b: 1111222 });
Last modification:November 7, 2021
如果觉得我的文章对你有用,请随意赞赏