2Node.js文件模块学习开发命令行Todo工具(中)

53 阅读2分钟

一. 优化之前的代码

  • 我们理清楚整个逻辑之后,期望实现的api
module.exports.add = (title) => {
  // 读取之前代码
  const list = db.read()
  // 往里面添加一个 title 任务
  list.push({title, done: false})
  // 存储任务到文件
  db.write(list)
}

  • 创建db.js,并使用promise实现read
// 这是直接获取系统的home目录
const homedir = require('os').homedir();
// 这是用户设置的home目录环境变量
const home = process.env.HOME || homedir
// 拼接储存文件路径
const p = require('path')
const dbPath = p.join(home, '.todo')
const fs = require('fs')

const db = {
  read(path = dbPath) {
    return new Promise((resolve, reject)=>{
      fs.readFile(path, { flag: 'a+' }, (error, data) => {
        if(error) {
          console.log('error', error)
          reject(error)
        } else{
          let list
          try {
            list = JSON.parse(data.toString())
          } catch (error2) {
            list = []
          }
          resolve(list)
        }
      })
    })
  }
  write() {}
}

modules.exports = db
  • 写好write
write(list, path = dbPath) {
    return new Promise((resolve, reject) => {
      const string = JSON.stringify(list)
      fs.writeFile(dbPath, string, (error)=>{
        if(error) return reject(error)
        resolve()
      })
    })
  }
  • 最终优化后的代码
// index.js
const db = require('./db.js')

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


// db.js
// 这是直接获取系统的home目录
const homedir = require('os').homedir();
// 这是用户设置的home目录环境变量
const home = process.env.HOME || homedir
// 拼接储存文件路径
const p = require('path')
const dbPath = p.join(home, '.todo')
const fs = require('fs')


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, (error)=>{
        if(error) return reject(error)
        resolve()
      })
    })
  }
}

module.exports = db
  • 总结: 我们通过先理清楚思路,设计好接口,再把之前的代码一点点挪过去来优化代码,我们称这种方法叫做面向接口编程

二. 消除webstorm警告

  • 需要打开webstorm的配置,见下图,如果没有找到需要下载一下,下载需要开代理

三. 实现剩下的代码

  • 实现clear
// cli.js
program
  .command('clear')
  .description('clear all tasks')
  .action(() => {
    api.clear()
  });

// index.js
module.exports.clear = async (title) => {
  await db.write([])
}
  • 实现展示全部
module.exports.showAll = async () => {
  // 读取之前的任务,
  const list = await db.read()
  // 打印之前的任务
  list.forEach((task, index)=>{
    console.log(`${task.done ? '[x]' : '[_]'} ${index + 1} - ${task.title}`)
  })
}

四. 通过inquirer.js来实现选择勾选完成等功能

npm install inquirer
  • 主要的代码逻辑
// cli.js
if(process.argv.length === 2) {
  // 说明用户直接运行 node cli.js
  api.showAll().then(()=>{console.log('')}, ()=>{console.log('展示失败')})
}

// index.js
module.exports.showAll = async () => {
  // 读取之前的任务,
  const list = await db.read()
  // 打印之前的任务
  inquirer
    .prompt([
      {
        type: 'list',
        name: 'index',
        message: '请选择你想操作的任务?',
        choices: [{name: '退出', value: '-1'}, ...list.map((task, index) => {
          return {
            name: [`${task.done ? '[x]' : '[_]'} ${index + 1} - ${task.title}`],
            value: index.toString()
          }
        }), {name: '+ 创建任务', value: '-2'}]
      },
    ])
    .then((answer) => {
      const index = parseInt(answer.index)

      if (index >= 0) {
        // 选中了任务
        inquirer.prompt({
          type: 'list',
          name: 'action',
          message: '请选择操作?',
          choices: [
            {name: '退出', value: 'quit'},
            {name: '已完成', value: 'markAsDone'},
            {name: '未完成', value: 'markAsUndone'},
            {name: '改标题', value: 'updateTitle'},
            {name: '删除', value: 'remove'},
          ]
        }).then(answer2 => {
          switch (answer2.action) {
            case 'markAsDone':
              list[index].done = true
              db.write(list)
              break;
            case 'markAsUndone':
              list[index].done = false
              db.write(list)
              break;
            case 'updateTitle':
              inquirer.prompt({
                type: 'input',
                name: 'title',
                message: '请输入新的标题:',
                default: list[index].title
              }).then(answers => {
                list[index].title = answers.title
                db.write(list)
              })
                break;
            case 'remove':
              list.splice(index, 1)
              db.write(list)
              break;
          }
        })
      } else if (index === -2) {
        inquirer.prompt({
          type: 'input',
          name: 'title',
          message: '请输入任务标题:',
        }).then(answer => {
          list.push({
            title: answer.title,
            done: false
          })
          db.write(list)
        })
      }
    });
}