十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Web-Dev-For-Beginners 银行应用实战:app.js 代码重构、常量提取与 JSDoc 文档化完整指南

Web-Dev-For-Beginners 银行应用实战:app.js 代码重构、常量提取与 JSDoc 文档化完整指南 Web-Dev-For-Beginners 银行应用实战app.js 代码重构、常量提取与 JSDoc 文档化完整指南【免费下载链接】Web-Dev-For-Beginners24 Lessons, 12 Weeks, Get Started as a Web Developer项目地址: https://gitcode.com/GitHub_Trending/we/Web-Dev-For-Beginners导读在微软《Web-Dev-For-Beginners》课程的银行项目中前端app.js随着登录、注册、Dashboard 功能的不断叠加而迅速膨胀出现了硬编码 URL 散落、请求逻辑重复、缺乏注释等典型技术债症状。本文以本仓库 7-bank-project/3-data/assignment.md 任务为骨架系统讲解三大专业重构技术提取配置常量、创建统一的sendRequest()请求函数、编写 JSDoc 风格代码文档并结合 最终解决方案 与 API 服务端源码 逐行印证。学完本文你将掌握一套可直接复用的前端代码重构方法论理解 DRY 原则、魔法字符串消除与模块化分组在真实 Web 项目中的落地方式。一、任务背景当一个 app.js 开始失控1.1 代码为什么会膨胀在第 3 课 Methods of Fetching and Using Data 中我们一步步为银行应用加入了login()登录函数从表单读取用户名调用getAccount()获取账户数据register()注册函数将表单数据 POST 到服务器创建账户updateDashboard()把账户余额、币种、交易明细渲染到页面基于template的交易行工厂函数与DocumentFragment批量插入逻辑。功能越多app.js就越臃肿API 基础 URL//localhost:5000/api/accounts/...在多个函数中被硬编码getAccount()与createAccount()之间存在着高度相似的 fetch/错误处理样板代码而函数之间缺乏统一的分组与说明。这正是任务开篇所说的app.js has grown significantly with login, registration, and dashboard functionality。1.2 本任务要解决的三类问题症状根因重构手段同一 URL 出现在多处魔法字符串magic strings提取配置常量createAccount()与getAccount()代码雷同违反 DRY 原则抽取统一sendRequest()函数用途、参数、返回值无人能懂缺少文档JSDoc 区块头注释这三个问题在真实项目中分别对应可维护性、重复性、可读性三大维度也是代码评审code review最常考察的要点。二、重构技术一提取配置常量2.1 任务要求在文件顶部创建一段配置区集中存放可复用常量提取服务器 API 基础 URL当前在多个位置被硬编码为出现在多个函数中的错误提示创建常量考虑提取反复使用的路由路径与 DOM 元素 ID。任务给出的示例结构// Configuration constants const API_BASE_URL http://localhost:5000/api; const ROUTES { LOGIN: /login, DASHBOARD: /dashboard };2.2 仓库中的落地范例打开 解决方案 app.js可以看到真实重构结果——文件顶部即是一个整齐的常量区// --------------------------------------------------------------------------- // Constants // --------------------------------------------------------------------------- const serverUrl http://localhost:5000/api; // reserved for future server swap const storageKey savedAccount; const accountsKey accounts; const schemaKey schemaVersion; const schemaVersion 1;这里的每一项都有明确价值serverUrl把 API 基址收拢到一处注释还特意标注为未来更换服务器预留reserved for future server swap这正是提取常量的经典动机——未来改一个值而不是改十处storageKey/accountsKey/schemaKey/schemaVersion将 localStorage 的键名与版本号集中管理避免拼写不一致引发的隐蔽 bug。再看路由声明solution/app.js路径也被结构化收纳const routes { /dashboard: { title: My Account, templateId: dashboard, init: refresh }, /login: { title: Login, templateId: login, init: attachAuthHandlers } };2.3 扩展还有哪些值值得提取结合 3-data 课程 与解决方案源码以下类别都适合进入常量区类别示例提取理由服务器地址http://localhost:5000/api环境切换本地/生产只改一处API 端点/accounts、/accounts/:user/transactions与后端路由表一一对应便于同步维护localStorage 键名savedAccount、accounts、schemaVersion杜绝魔法字符串拼写错误错误消息Account not found、User already exists统一文案、便于国际化i18nDOM 元素 IDloginForm、loginError、transactions与 index.html 模板保持一致为什么值得做从源码结构看解决方案把常量区、Intl辅助函数区、存储与状态区、DOM 辅助区、路由区、认证区、Dashboard 区用// ---分隔线明确划分见 solution/app.js 全文的分区注释这正是任务分组相关函数要求的完整实现。三、重构技术二创建统一的sendRequest()请求函数3.1 任务要求与函数签名任务明确指出createAccount()与getAccount()之间存在重复代码需要合并为一个可复用的请求函数。要求包括同时处理 GET 与 POST 请求包含正确的错误处理支持不同的 URL 端点接受可选的请求体数据。任务给出的签名模板async function sendRequest(endpoint, method GET, data null) { // Your implementation here }3.2 重构前的痛点对照课程原始代码在未重构的 3-data 课程代码 中数据获取长这样async function getAccount(user) { try { const response await fetch(//localhost:5000/api/accounts/ encodeURIComponent(user)); return await response.json(); } catch (error) { return { error: error.message || Unknown error }; } }而注册逻辑中又有另一段独立的 fetch POST 代码。两个函数各自处理fetch → 解析 JSON → try/catch样板代码重复且 URL 是硬编码拼接。sendRequest()的意义就是把这段管道抽出来让调用方只关心端点、方法、数据三件事。3.3 一个可直接落地的完整实现结合任务签名与课程中encodeURIComponent()的安全性要求可以这样实现/** * Unified HTTP request helper for the Bank API. * param {string} endpoint - API path, e.g. /accounts/test * param {string} [methodGET] - HTTP method (GET | POST | ...) * param {object|null} [datanull] - Optional request body (serialized to JSON) * returns {Promiseobject} - Parsed JSON response; error object on failure */ async function sendRequest(endpoint, method GET, data null) { const url API_BASE_URL endpoint; const options { method, headers: { Content-Type: application/json } }; // Only attach a body when data is provided (GET requests must not carry one) if (data) { options.body JSON.stringify(data); } try { const response await fetch(url, options); // Surface non-2xx responses as explicit errors instead of silent failures if (!response.ok) { const errBody await response.json().catch(() ({})); throw new Error(errBody.error || Request failed with status ${response.status}); } return await response.json(); } catch (error) { return { error: error.message || Unknown error }; } }实现要点默认参数method GET让只取数据的调用零配置可选 body仅当传入data时才设置options.body避免 GET 请求携带无效请求体状态码校验显式检查response.ok把 404/409 等非 2xx 响应转化为可读错误而不是当作成功数据返回错误兜底任何异常都归一化为{ error: message }与课程中getAccount()的错误返回格式保持一致调用方无需区分异常类型。3.4 重构后调用方变得极简重构后getAccount()与createAccount()都退化为一行式调用// Retrieve account data const data await sendRequest(/accounts/${encodeURIComponent(user)}); // Create a new account (POST with body) const result await sendRequest(/accounts, POST, accountData);而登录流程对照 solution/app.js 的逻辑变为async function login() { const form document.getElementById(loginForm); if (!form.checkValidity()) { form.reportValidity(); return; } const user String(form.user.value || ).trim(); const data await getAccount(user); if (data.error) return updateElement(loginError, data.error); updateState(account, data); navigate(/dashboard); }3.5 与后端 API 的对应关系统一请求函数的价值要在后端路由的支撑下才能体现。本仓库 api/README.md 列出了sendRequest()需要覆盖的全部端点方法路由说明GET/api/获取服务器信息返回Bank API v1.0.0POST/api/accounts/创建账户如{ user, description, currency, balance }GET/api/accounts/:user获取指定账户全部数据DELETE/api/accounts/:user删除指定账户POST/api/accounts/:user/transactions添加交易如{ date, object, amount }DELETE/api/accounts/:user/transactions/:id删除指定交易对照 server.js 可以看到POST /accounts缺少必填参数返回400、账户已存在返回409、GET /accounts/:user不存在返回404——这些状态码正是sendRequest()中response.ok检查要拦截的错误场景。参数校验失败400、资源冲突409、资源不存在404是重构后测试用例的重点目标。提示在本地验证时先启动 API 服务cd 7-bank-project/api npm install npm start默认监听 5000 端口再用curl http://localhost:5000/api确认返回 Bank API v1.0.0之后前端的所有请求才有可用后端。四、重构技术三添加专业的 JSDoc 代码文档4.1 任务要求文档化的目标是解释代码背后的**为什么why**而不仅是是什么what。任务给出的文档标准为函数添加说明用途、参数、返回值的文档注释为复杂逻辑或业务规则添加行内注释用区块标题section headers对相关函数分组解释任何不直观的代码模式或浏览器特例。任务示例风格/** * Authenticates user and redirects to dashboard * param {Event} event - Form submission event * returns {Promisevoid} - Resolves when login process completes */ async function login(event) { // Prevent default form submission to handle with JavaScript event.preventDefault(); // Your implementation... }4.2 仓库中的文档化范例从 solution/app.js 可以看到文档化的三个层次完整落地层次一区块头Section Headers——用统一的// ---分隔线组织文件结构// --------------------------------------------------------------------------- // Constants // --------------------------------------------------------------------------- // --------------------------------------------------------------------------- // Intl helpers // --------------------------------------------------------------------------- // --------------------------------------------------------------------------- // Storage and state // --------------------------------------------------------------------------- // --------------------------------------------------------------------------- // DOM helpers // --------------------------------------------------------------------------- // --------------------------------------------------------------------------- // Router // --------------------------------------------------------------------------- // --------------------------------------------------------------------------- // Auth // --------------------------------------------------------------------------- // --------------------------------------------------------------------------- // Dashboard // --------------------------------------------------------------------------- // --------------------------------------------------------------------------- // Global listeners // --------------------------------------------------------------------------- // --------------------------------------------------------------------------- // Init // ---------------------------------------------------------------------------这种目录式分组让读者 10 秒内就能定位任意功能正是任务logically grouped with section headers的教科书示范。层次二行内注释解释为什么——看 solution/app.js 中冻结状态对象的注释// Keep a frozen state object to avoid accidental mutations let state Object.freeze({ account: null }); function updateState(property, newData) { state Object.freeze({ ...state, [property]: newData }); // Persist active account only localStorage.setItem(storageKey, JSON.stringify(state.account)); }Object.freeze的目的防止意外修改与只持久化当前账户的取舍都是典型的why 注释。层次三跨标签页同步注释solution/app.js// Cross-tab sync: refresh when accounts or active account change in another tab window.addEventListener(storage, (e) { if (e.key accountsKey || e.key storageKey) { if (state.account?.user) { refresh().catch(() {}); } } });4.3 JSDoc 文档化的实用清单结合任务要求与仓库实践为每个函数补充文档时可覆盖以下字段JSDoc 标记作用示例首行描述说明函数用途Authenticates user and redirects to dashboardparam {type} name参数类型与含义param {Event} event - Form submission eventreturns {type}返回值说明returns {Promisevoid} - Resolves when login completesthrows可能抛出的异常throws {Error} - When API returns non-2xx status同时记住任务的隐藏要求为浏览器特例browser-specific workarounds写注释。例如crypto.randomUUID在非 HTTPS/localhost 环境下不可用solution/app.js 中就有对应的降级实现与注释function uuid() { // Works on HTTPS and localhost; fallback otherwise if (globalThis.crypto typeof crypto.randomUUID function) { return crypto.randomUUID(); } return tx- Date.now().toString(36) - Math.random().toString(36).slice(2, 8); }五、成功标准三档评分对照表任务给出了非常实用的自评标准重构后请逐条核对5.1 优秀实现Exemplary✅常量所有魔法字符串与 URL 都已提取为命名清晰的常量✅DRY 原则公共请求逻辑合并进可复用的sendRequest()函数✅文档函数均有清晰的 JSDoc 注释说明用途与参数✅组织代码按区块标题逻辑分组格式统一✅错误处理借助新的请求函数改善了错误处理。5.2 合格实现Adequate✅常量多数重复值已提取残留少量硬编码✅函数化已创建基础sendRequest()但可能未覆盖所有边界情况✅注释关键函数有文档但部分解释可以更完整✅可读性整体组织良好仍有可改进之处。5.3 需要改进Needs Improvement❌常量大量魔法字符串与 URL 仍散落全文件❌重复相似函数之间仍有显著代码重复❌文档注释缺失或不足无法说明代码用途❌组织代码缺乏清晰结构与逻辑分组。建议以优秀实现为基准做自测在app.js中全局搜索localhost与直接拼写的 URL 字符串若结果为零说明常量提取到位再检查是否存在第二个fetch调用未经过sendRequest()以此验证 DRY。六、测试重构后的代码回归清单重构的黄金法则是**行为不变**。任务要求重构后完整回归以下场景测试所有用户流程注册、登录、Dashboard 展示与错误处理验证 API 调用确认sendRequest()对账户创建POST与账户获取GET都工作正常检查错误场景使用无效凭据与网络错误进行测试查看控制台输出确认重构过程中未引入新错误。可以对照 3-data 课程的测试流程图 设计的冒烟用例用例操作预期结果注册提交新用户名 币种跳转 Dashboard余额/描述正确显示登录成功使用已存在账户如test进入 Dashboard交易列表渲染登录失败输入不存在的用户名loginError区域显示可见错误消息网络异常关闭 API 服务后登录错误被sendRequest()捕获页面不崩溃页面标题切换登录/Dashboard浏览器标签标题随之更新依赖 第 1 课路由任务 的 title 功能其中可见错误消息依赖 3-data 课程 中的updateElement(id, text)辅助函数与rolealert无障碍声明区域见 solution/index.html 中的#loginError、#registerError、#transactionError元素。七、提交指南与附加挑战CODE_STRUCTURE.md7.1 提交要求任务规定提交重构后的app.js必须包含清晰的组织不同功能的区块标题一致的代码格式与缩进所有函数的完整 JSDoc 文档文件开头的简要注释说明重构思路。7.2 附加挑战Bonus编写 CODE_STRUCTURE.md任务要求编写一个简单的代码文档文件解释应用的架构以及各函数如何协同工作。以仓库现状为参考CODE_STRUCTURE.md至少应覆盖数据层getAccounts()/saveAccounts()负责 localStorage 读写getAccount()/createAccount()/createTransaction()提供业务数据访问solution/app.js状态层updateState()维护冻结的全局状态对象并持久化当前账户solution/app.js视图层updateElement()/createTransactionRow模板克隆 /DocumentFragment批量渲染构成数据到 UI 的管道solution/app.js 与 index.html 中的template路由层routes表 navigate()/updateRoute()init生命周期钩子控制页面切换solution/app.js。这份文档本质上就是架构全景图也是后续新人接手、代码评审时最有价值的交付物。八、现实世界关联为什么这套技能是饭碗技能任务结尾点明了这套练习与工业界的直接映射值得作为重构动力的长期视角代码评审Code Reviews评审者评估的就是可读性与可维护性——正如本任务的三档评分标准技术债务Technical Debt当代码不定期重构与文档化债务会像利息一样累积最终拖慢整个迭代节奏团队协作Team Collaboration清晰、文档良好的代码让新成员能快速理解并安全修改Bug 修复Bug Fixes在组织良好、具备恰当抽象的代码库中修 bug 要容易得多。结语本文以 3-data 课程任务 为骨架完整走通了提取常量 → 统一请求函数 → JSDoc 文档化三步重构路径并逐一对照 解决方案源码 与 API 实现 验证了每一步的真实落地形态。这三项技能——消除魔法字符串、消灭重复、书写清晰文档——是专业前端开发的基本功也是任何规模项目健康演进的基础。完成重构后请务必按第六节的回归清单验证行为不变再以第五节的优秀标准做最终自评。【免费下载链接】Web-Dev-For-Beginners24 Lessons, 12 Weeks, Get Started as a Web Developer项目地址: https://gitcode.com/GitHub_Trending/we/Web-Dev-For-Beginners创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表