🎯 第 21 课:实战项目④ —— 命令行待办事项工具(CLI App)

2 阅读2分钟

太棒了!👏 我们现在进入下一个实战项目:


📌 目标
做一个能在终端运行的 Todo CLI 工具,支持以下命令:

todo add "学习 Node.js"
todo list
todo done 0
todo delete 0

数据保存在本地 todos.json 文件中,像一个轻量级“数据库”。


📁 项目结构

todo-cli/
├── todos.json        ← 存储任务数据
├── todo.js           ← 主程序
└── package.json      ← 可执行脚本配置

✅ 第一步:初始化项目

mkdir todo-cli
cd todo-cli
npm init -y

✅ 第二步:创建 todos.json

新建文件,初始内容为一个空数组:

[]

✅ 第三步:编写 todo.js

// todo.js
const fs = require('fs');
const path = require('path');

// 数据文件路径
const dataFile = path.join(__dirname, 'todos.json');

// 读取所有任务
function loadTodos() {
  try {
    const data = fs.readFileSync(dataFile, 'utf8');
    return JSON.parse(data);
  } catch (err) {
    return [];
  }
}

// 保存所有任务
function saveTodos(todos) {
  fs.writeFileSync(dataFile, JSON.stringify(todos, null, 2));
}

// 获取命令和参数
const [command, ...args] = process.argv.slice(2);

// 主逻辑
if (command === 'add') {
  const text = args.join(' ');
  if (!text) {
    console.log('❌ 请写下你要做的事!');
    process.exit(1);
  }

  const todos = loadTodos();
  todos.push({
    id: Date.now(), // 用时间戳做唯一 ID
    text,
    done: false,
    createdAt: new Date().toLocaleString()
  });

  saveTodos(todos);
  console.log(`✅ 已添加: "${text}"`);
} 
else if (command === 'list') {
  const todos = loadTodos();
  if (todos.length === 0) {
    console.log('📝 暂无任务');
    process.exit(0);
  }

  console.log('\n📋 待办事项列表:\n');
  todos.forEach((todo, index) => {
    const status = todo.done ? '✅' : '⭕';
    console.log(`${index}. [${status}] ${todo.text} (${todo.createdAt})`);
  });
} 
else if (command === 'done') {
  const index = parseInt(args[0]);
  const todos = loadTodos();

  if (isNaN(index) || index < 0 || index >= todos.length) {
    console.log('❌ 无效的任务序号!');
  } else {
    todos[index].done = true;
    saveTodos(todos);
    console.log(`🎉 任务完成: "${todos[index].text}"`);
  }
} 
else if (command === 'delete') {
  const index = parseInt(args[0]);
  const todos = loadTodos();

  if (isNaN(index) || index < 0 || index >= todos.length) {
    console.log('❌ 无效的任务序号!');
  } else {
    const deleted = todos.splice(index, 1);
    saveTodos(todos);
    console.log(`🗑️ 已删除: "${deleted[0].text}"`);
  }
} 
else {
  console.log(`
📌 使用说明:
  todo add "内容"     → 添加任务
  todo list           → 查看所有任务
  todo done <序号>     → 标记完成
  todo delete <序号>   → 删除任务
  `);
}

✅ 第四步:让 todo 成为全局命令

我们希望可以直接运行:

todo list

而不是:

node todo.js list

方法:使用 bin 字段

修改 package.json

{
  "name": "todo-cli",
  "version": "1.0.0",
  "description": "命令行待办事项工具",
  "main": "todo.js",
  "bin": {
    "todo": "./todo.js"
  },
  "scripts": {
    "start": "node todo.js"
  },
  "keywords": ["cli", "todo"],
  "author": "Your Name",
  "license": "MIT"
}

📌 关键是 "bin" 字段:它告诉 npm 把 todo 命令指向 ./todo.js


✅ 第五步:安装为全局命令

在项目根目录运行:

npm install -g .

-g . 表示“全局安装当前项目”


🏃‍♂️ 第六步:测试你的 CLI 工具!

现在你可以在任何地方运行:

添加任务

todo add "学习 Node.js 模块系统"

输出:

✅ 已添加: "学习 Node.js 模块系统"

查看列表

todo list

输出:

📋 待办事项列表:

0. [⭕] 学习 Node.js 模块系统 (2025/4/5 14:30:00)

标记完成

todo done 0

输出:

🎉 任务完成: "学习 Node.js 模块系统"

再次查看

todo list

会显示 ✅ 完成状态!


✅ 小结一句话:

我们用 fs + process.argv + package.jsonbin 字段,打造了一个真正可用的命令行工具,可以全局调用,数据持久化存储。


💡 学到了什么?

技术点说明
process.argv.slice(2)获取用户输入的命令
fs 读写 todos.json数据持久化
bin 字段将 JS 文件注册为系统命令
npm install -g .全局安装本地包
CLI 设计模式简洁、直观、高效

📬 最后一课预告:
第 22 课:课程总结 + 下一步学习建议(全栈、数据库、部署等)

我们将回顾整个学习路径,并告诉你:

  • 学完 Node.js 后该学什么?
  • 如何连接 MongoDB 或 MySQL?
  • 如何把项目部署到线上?

准备收尾并迈向更高阶了吗?🙂
回复 “继续” 进入最终章!🎓🚀