Node.js fs模块学习

通过一个简单的 node 命令行的 todo 项目学习了 nodejs 文件模块,收获颇多。

Github 源码

项目介绍:

  • 功能
    可以展示所有 todo 任务
    可以新创建一个任务
    可以编辑 todo 任务,修改任务名、完成状态
    可以删除 todo 任务
  • 命令

    d 展示所有任务
    d add xxx 添加新任务
    d clear 清除所有任务

  • 展示

引入 node fs模块

nodejs fs 中文文档

  1. fs.readFile(path[, options], callback)

path (被读取文件路径);
options (文件以什么形式被读取,flag: a+ 打开文件进行读取和追加,如果文件不存在,则创建该文件);
cllback (回调函数,读取文件之后的操作,接受两个参数,Error 读取文件失败结果,data 读取成功结果)

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

file (被写入的文件路径);
data (文件被写入的内容);
cllback (回调函数,读取文件之后的操作,只接受一个参数,Error 写入文件失败结果)

  1. 读文件与写文件都是异步操作,所以需要返回一个 promise 对象,调用 read()write() 时需要使用 awaitasync,如果读取或者写入文件失败,则 callback() 传入的 error 需要使用 reject(error) 返回。
  2. 导入文件时使用 require('xxx') ,导出时使用 module.exports
// db.js

// 配置 .todo 文件到 home 目录
const homedir = require('os').homedir()
const home = process.env.HOME || homedir
const p = require('path')
const fs = require('fs')
const dbPath = p.join(home, '.todo')

const db = {
  // 读取之前的任务
  read(path = dbPath) {
    return new Promise((resolve, reject) => {
      fs.readFile(path, {flag: 'a+'}, (error, data) => {
        if (error) {return reject(error)}
        let list
        try {
          list = JSON.parse(data.toString())
        } catch (error2) {
          list = []
        }
        resolve(list)
      })
    })
  },
  // 存储任务到文件
  write(list, path = dbPath) {
    return new Promise((resolve, reject) => {
      const string = JSON.stringify(list)
      fs.writeFile(path, string + '\n', (error) => {
        if (error) {return reject(error)}
        resolve()
      })
    })
  }
}

module.exports = db

引入 commander.js 配置命令

commander.js 是完整的命令行解决方案;

安装并引入之后可以通过 .command() 来自定义命令;

需要通过独立的的可执行文件来实现命令,如index.js,自定义命令的执行部分主要放在这里;

.action((arg)=>{}) 调用可执行文件 index.js 中对应的函数;

.description() 用来描述命令干了什么。

//  cli.js  (add示例)
const program = require('commander');
const api = require('./index')

program
  .command('add')
  .description('add a task')
  .action((...args) => {
    const words = args.slice(0, -1).join(' ')
    api.add(words)
      .then(() => {console.log('添加成功')}, () => {console.log('添加失败')})
  });

program.parse(process.argv);
//  index.js (add示例)
const db = require('./db')

module.exports.add = async (title) => {
  // 读取之前的任务
  const list = await db.read()
  // 添加一个新任务
  list.push({title: title, done: false})
  // 存储任务到文件
  await db.write(list)
}

注意:添加和清空任务后都要将新的任务列表写入文件。

引入 inquirer.js

Inquirer.js 是一个命令行交互工具;

安装并引入后通过 inquirer.prompt({type, name, message, choices[fn]}).then((answer)=>{}) 来配置命令行操作界面;

type : prompt 的类型. 默认值: input 可选值: input, number, confirm, list, rawlist, expand, checkbox, password, editor
name : 后续在 .then() 时使用来操作answer 的 key;
message : 命令行提示信息,通常是一个询问;
choices : 数组或返回一个选择数组的函数,如果定义为函数,则第一个参数将是当前会话的 answer
answer : 由一个键值对组成,key 是该 prompt 的 name 属性,value 取决于 prompt 的类型,在这里是每一项任务。

//  index.js  (askForAction示例)
const inquirer = require('inquirer')

module.exports.show = async () => {
  // 读取之前的 list
  const list = await db.read()
  // 切换进行操作任务
  printTasks(list)
}

function printTasks(list) {
  inquirer
    .prompt({
      type: 'list',
      name: 'index',
      message: '请选择需要操作的任务',
      choices: [...list.map((item, index) => {
        return {
          name: `${item.done ? '[√]' : '[x]'}${index + 1}:${item.title}`, value: index.toString()
        }
      }), {name: ' + 添加', value: '-1'}, {name: ' x 取消', value: '-2'}]
    }).then((answer) => {
    const index = parseInt(answer.index)
    if (index >= 0) {
      askForAction(list, index)
    } else if (index === -1) {
      askForCreatTask(list)
    }
  })
}

通过嵌套 inquirer 可实现各种层级的命令行交互界面;
此时即可通过 node cli 可执行查看任务列表,并进行后续的交互操作;
通过 node cli add 添加任务,node clear 清除任务列表;
但是这样每次必须要都输入 node。

Node.js 使用 Shebang

当对 cli.js 文件设置了正确的 Shebang 时,只需输入 ./cli.js 即可

设置方法:

  • 命令行输入
 chmod u+x cli.js 
  • cli.js 文件首行输入
#!/usr/bin/env node

env 主要用于在修改后的环境中运行命令。写 /usr/bin/env node 告诉 OS 运行 env,而 env 将运行 node,最后 node 将依次执行脚本。

命令行工具配置

package.json 中添加 bin 属性,指定命令的名称

  "bin": {
    "d": "cli.js"
  },

发布至 npm

  1. package.json 添加 files 属性,确定要上传包的文件,上传所有 .js 文件
  "files": [
    "*.js"
  ],
  1. 保证 name 不会与现有的 npm 包重名;
  2. 输入 npm login 登录账户,注意使用 nrm use npm 切换为 npm 源然后登录;
  3. 登录之后使用 npm publish 发布。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,542评论 6 504
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,822评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,912评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,449评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,500评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,370评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,193评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,074评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,505评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,722评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,841评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,569评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,168评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,783评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,918评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,962评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,781评论 2 354