Node.js-Phase 1 学习总结:CLI 文件管理系统
文章目录目录一、学到的 Node.js 知识点1.1 Node.js 模块系统CommonJS1.2 fs 文件系统模块1.3 path 路径模块1.4 process 进程模块1.5 readline 交互模块1.6 错误处理模式1.7 async/await 与 Promise1.8 安全实践二、我能做什么2.1 已完成的功能清单2.2 扩展能力基于已学知识三、能应付的面试题3.1 基础概念题3.2 fs 模块题3.3 path 模块题3.4 process 模块题3.5 事件循环与异步题3.6 安全题3.7 架构设计题四、扩展方向4.1 Phase 2 预告Web API 版本4.2 Phase 3 进阶方向4.3 面试冲刺建议目录学到的 Node.js 知识点我能做什么能应付的面试题扩展方向一、学到的 Node.js 知识点1.1 Node.js 模块系统CommonJS知识点说明代码位置require加载机制Node.js 按同步方式加载模块会缓存已加载的模块cli/index.js:15-22module.exports导出模块接口可以是对象、函数、类各命令文件末尾目录作为模块require(./cli)自动查找cli/index.js项目入口__dirname当前模块文件所在目录的绝对路径core/pathValidator.js:8require路径解析相对路径./、../的查找规则各文件头部面试相关require和import的区别—— CommonJS 是同步加载、运行时加载、值拷贝ESM 是异步加载、编译时加载、值引用Node.js 模块查找策略—— 内置模块 →node_modules→ 逐级向上查找 → NODE_PATH循环引用如何处理—— CommonJS 返回未完成的module.exports的当前状态1.2 fs 文件系统模块Phase 1 用到的 APIAPI同步/异步用途fs.promises.readdir(path, { withFileTypes: true })Promise读取目录返回fs.Dirent对象可直接判断类型fs.promises.readFile(path, utf-8)Promise读取文件内容fs.promises.writeFile(path, content, utf-8)Promise写入文件内容fs.promises.mkdir(path, { recursive: true })Promise创建目录递归创建fs.promises.copyFile(src, dst)Promise复制文件fs.promises.rm(path, { recursive: true, force: true })Promise删除文件或目录递归删除fs.promises.unlink(path)Promise删除单个文件fs.promises.stat(path)Promise获取文件/目录的 Stats 信息fs.existsSync(path)同步检查路径是否存在没有 Promise 版本核心知识点三种 API 风格回调风格fs.readFile(path, cb)— 回调地狱风险同步风格fs.readFileSync(path)— 阻塞事件循环Promise 风格fs.promises.readFile(path)— 可 await推荐// 回调风格旧fs.readFile(a.txt,(err,data){if(err)throwerr;});// 同步风格阻塞constdatafs.readFileSync(a.txt);// Promise 风格推荐constdataawaitfs.promises.readFile(a.txt,utf-8);fs.Direntreaddir的withFileTypes: true选项返回fs.Dirent对象提供isDirectory()、isFile()、isSymbolicLink()等方法避免对每个条目额外调用statrecursive: truemkdir和rm的递归选项允许创建/删除多级嵌套目录文件元数据fs.Stats提供size、birthtime、mtime、atime、mode权限等信息面试相关三种 API 的应用场景—— CLI 工具可用同步服务器用 Promise/回调withFileTypes: true的作用—— 避免 N1 次stat调用如何实现递归删除目录—— Node 14 用fs.rm(path, { recursive: true })之前用rimraf库1.3 path 路径模块API作用示例path.resolve([...paths])解析为绝对路径从右往左遇到绝对路径停止resolve(/a, b, c)→/a/b/cpath.join([...paths])拼接路径片段使用平台分隔符join(/a, /b)→/a/bpath.basename(path)获取文件名basename(/a/b/c.js)→c.jspath.dirname(path)获取目录名dirname(/a/b/c.js)→/a/bpath.relative(from, to)获取相对路径relative(/a/b, /a/c)→../c关键区别resolvevsjoin// resolve处理绝对路径从右向左构建path.resolve(/a,b)→/a/bpath.resolve(/a,/b)→/b// 遇到绝对路径就停path.resolve(a,b)→/cwd/a/b// join只是拼接用平台分隔符连接path.join(/a,/b)→/a/b// 不会重置path.join(a,..,b)→b// 处理 ..安全应用path.resolve(PROJECT_ROOT, userInput)后检查是否以PROJECT_ROOT开头防止路径穿越攻击。面试相关path.resolve和path.join的底层区别—— resolve 会处理绝对路径并基于cwd计算join 单纯拼接跨平台路径问题—— Windows 用\POSIX 用/path模块自动处理1.4 process 进程模块API用途process.argv获取命令行参数数组前两个是 node 路径和脚本路径process.exit(code)退出进程0 成功非 0 失败process.cwd()获取当前工作目录process.stdout标准输出流process.stdin标准输入流process.argv结构// node cli/index.js list test// process.argv [// /usr/local/bin/node, // Node.js 执行路径// /path/to/cli/index.js, // 脚本路径// list, // 第一个参数// test // 第二个参数// ]面试相关如何解析复杂命令行参数—— 简单用process.argv.slice(2)复杂用commander/yargs库process.cwd()和__dirname的区别——cwd()是执行命令时所在的目录__dirname是代码文件所在的目录1.5 readline 交互模块API用途readline.createInterface({ input, output })创建交互接口rl.question(query, callback)向用户提问rl.close()关闭接口constreadlinerequire(readline);constrlreadline.createInterface({input:process.stdin,output:process.stdout,});constanswerawaitnewPromise((resolve){rl.question(确认删除? (y/N): ,(ans)resolve(ans));});rl.close();面试相关如何让 Node.js CLI 实现交互输入—— 用readline模块readline的事件驱动模型—— 基于EventEmitter监听line、close事件1.6 错误处理模式Phase 1 的分层错误处理命令层 (list.js) → 无 try-catch错误向上抛 核心层 (fileService.js) → 检查型错误直接 throw 入口层 (cli/index.js) → 统一 try-catch 捕获并输出// 入口层统一处理try{awaitcommandFn(args.slice(1),logger);}catch(err){logger.error(${command}操作失败:${err.message});process.exit(1);}Node.js 错误类型错误类型示例处理方式操作错误文件不存在、权限不足可预期的错误用 try-catch程序员错误参数为 undefined、类型错误应该修复代码系统错误ENOENT、EACCES、EISDIR通过err.code区分处理面试相关Node.js 中如何优雅处理错误—— 区分操作错误和程序员错误使用 Error 对象的 code 属性分类处理未捕获的异常如何处理——process.on(uncaughtException)和process.on(unhandledRejection)1.7 async/await 与 Promise// Promise 链 vs async/await// Promise 链fs.promises.readFile(a.txt).then(datafs.promises.writeFile(b.txt,data)).then(()console.log(done)).catch(errconsole.error(err));// async/await更直观asyncfunctioncopy(){try{constdataawaitfs.promises.readFile(a.txt);awaitfs.promises.writeFile(b.txt,data);console.log(done);}catch(err){console.error(err);}}1.8 安全实践路径穿越防护Path Traversal PreventionconstPROJECT_ROOTpath.resolve(__dirname,..);functionresolveSafePath(inputPath){constresolvedpath.resolve(PROJECT_ROOT,inputPath);// 关键的检查用户输入解析后必须在项目目录内if(!resolved.startsWith(PROJECT_ROOT)){thrownewError(Invalid path: directory traversal detected);}returnresolved;}攻击示例../../etc/passwd→resolve后超出项目根目录 → 被拦截二、我能做什么2.1 已完成的功能清单命令功能使用场景node cli list [dir]浏览目录区分文件和文件夹显示文件大小查看项目结构node cli read file读取并显示文件内容快速查看文件node cli info path查看文件/目录详细信息大小、权限、时间等诊断文件属性node cli mkdir dir创建目录支持多级递归快速建目录结构node cli write file text写入文件内容快速创建文件node cli copy src dst复制文件文件备份node cli remove path删除文件或目录带确认提示防误删清理文件2.2 扩展能力基于已学知识批量重命名工具利用readdirrename实现文件搜索工具递归遍历目录按名称/内容/正则匹配目录对比工具对比两个目录的文件差异文件监控使用fs.watch监听文件变化简单的构建脚本文件复制、合并、压缩等脚手架生成器根据模板生成项目目录结构三、能应付的面试题3.1 基础概念题Q1require和import的区别CommonJS 是运行时同步加载输出值的拷贝ESM 是编译时异步加载输出值的引用CommonJS 使用require()/module.exportsESM 使用import/exportCommonJS 可以在条件语句中动态加载ESM 的import必须是静态的顶层Q2Node.js 模块查找策略内置模块fs、path 等优先级最高如果路径以./或../开头按相对路径查找如果是裸名称如lodash逐级向上查找node_modules查找时会尝试添加.js、.json、.node扩展名如果目录有package.json的main字段指向入口文件Q3Node.js 的全局对象有哪些global浏览器中的window对应__dirname、__filename模块级非全局但普遍使用process、Buffer、consolesetTimeout、setInterval、setImmediateexports、require、module3.2 fs 模块题Q4fs.readFile、fs.readFileSync、fs.promises.readFile的区别readFile回调不阻塞但嵌套多时回调地狱readFileSync同步简单直观但阻塞事件循环不适合生产服务器promises.readFilePromise不阻塞可用 async/await推荐方式Q5如何处理大文件读取readFile会一次性加载到内存不适合大文件大文件应使用fs.createReadStreamStream边读边处理控制内存占用Q6fs.stat能获取哪些信息size字节数、mode权限birthtime创建时间、mtime修改时间、atime访问时间isFile()、isDirectory()、isSymbolicLink()3.3 path 模块题Q7path.resolve和path.join的区别resolve从右到左处理遇到绝对路径就返回否则基于cwd拼接join只是简单地用平台分隔符连接所有片段不做绝对路径解析例子resolve(/a, /b)→/bjoin(/a, /b)→/a/bQ8跨平台路径要注意什么Windows 用反斜杠\Linux/macOS 用正斜杠/使用path模块自动处理不要手动拼接路径字符串path.sep获取平台路径分隔符3.4 process 模块题Q9process.argv的结构是怎样的[0]Node.js 可执行文件路径[1]当前执行的 JS 文件路径[2]起用户传入的命令行参数Q10process.cwd()和__dirname的区别cwd()用户执行node命令时所在的目录可能变化__dirname当前代码文件所在的目录固定不变3.5 事件循环与异步题Q11什么是事件循环Event LoopNode.js 的事件循环分为多个阶段timers → I/O callbacks → idle/prepare → poll → check → closesetTimeout(fn, 0)在 timers 阶段执行setImmediate(fn)在 check 阶段执行process.nextTick(fn)在当前阶段结束后立即执行优先级最高Q12为什么readFile的回调晚于setTimeoutreadFile属于 I/O 操作回调在 poll 阶段处理setTimeout在 timers 阶段处理执行顺序取决于文件读取完成的时间点3.6 安全题Q13什么是路径穿越攻击Path Traversal如何防护攻击者用../../etc/passwd等方式访问受限目录外的文件防护方法使用path.resolve()解析为绝对路径检查解析后的路径是否以项目根目录开头startsWith避免直接拼接用户输入到路径中3.7 架构设计题Q14这个项目为什么分成 cli / core / lib 三层入口层cli负责参数解析、命令分发、用户交互业务层core封装核心文件操作逻辑与输入输出解耦工具层lib提供通用的日志、配置等基础设施好处职责单一、便于测试、Core 层可被 CLI 和未来的 Web API 共享四、扩展方向4.1 Phase 2 预告Web API 版本基于原生http模块构建 RESTful 服务// 大致结构consthttprequire(http);constserverhttp.createServer(async(req,res){const{method,url}req;// 路由分发// GET /api/files → 浏览目录// GET /api/files/info → 文件详情// POST /api/files/upload → 上传文件// GET /api/files/download → 下载文件// DELETE /api/files → 删除文件});将学到的新知识点http模块创建服务器、请求/响应处理url模块URL 解析、查询参数提取请求体解析处理 JSON、multipart/form-dataCORS 跨域处理MIME 类型设置文件上传下载的流式处理4.2 Phase 3 进阶方向方向知识点面试价值Stream 流处理fs.createReadStream、fs.createWriteStream、pipe、背压控制⭐⭐⭐⭐⭐Buffer 详解Buffer 创建、编码转换、二进制操作⭐⭐⭐⭐事件系统EventEmitter自定义事件、发布订阅模式⭐⭐⭐⭐⭐加密与校验crypto模块MD5/SHA1 哈希、文件完整性校验⭐⭐⭐⭐Cluster 多进程利用多核 CPU、进程间通信⭐⭐⭐⭐Stream 实战大文件分片上传、断点续传⭐⭐⭐⭐⭐4.3 面试冲刺建议完成 Phase 1 后你已经可以回答大部分 Node.js 基础面试题。按优先级准备事件循环机制最高频— 结合项目中的 async/await 理解模块系统— CommonJS vs ESM循环依赖Stream 与 Buffer— 大文件处理场景错误处理— 分层设计、错误分类安全— 路径穿越、XSS、CSRF 的基本防护一句话总结 Phase 1通过一个 CLI 文件管理工具掌握了 Node.js 最核心的fs、path、process、readline模块理解了 CommonJS 模块化、async/await 异步编程、分层架构和安全实践覆盖了 Node.js 后端面试的 60% 基础知识。