你的第一个Todo List项目藏着这些坑:HTML+CSS+JS新手避雷指南

张开发
2026/4/6 19:41:11 15 分钟阅读

分享文章

你的第一个Todo List项目藏着这些坑:HTML+CSS+JS新手避雷指南
你的第一个Todo List项目藏着这些坑HTMLCSSJS新手避雷指南刚学完前端三件套的新手第一个综合练习项目往往选择Todo List——它看似简单却暗藏无数陷阱。我曾见过太多初学者在实现基础功能后代码变成难以维护的意大利面条或是遭遇性能瓶颈、安全漏洞却不自知。本文将揭示那些教程里不会告诉你的10个典型错误从事件绑定到数据持久化帮你避开这些坑。1. 事件监听的性能陷阱新手最常见的错误是在循环内绑定事件监听器。比如动态生成待办事项时为每个删除按钮单独绑定点击事件// 错误示范每次添加都重新绑定所有按钮 function addTodo() { const deleteButtons document.querySelectorAll(.delete-btn); deleteButtons.forEach(btn { btn.addEventListener(click, handleDelete); }); }这种写法会导致内存泄漏每次添加新事项都会创建新的事件监听器性能下降列表较长时重复绑定消耗资源正确做法使用事件委托Event Delegation// 最佳实践在父元素上统一监听 document.getElementById(todo-list).addEventListener(click, (e) { if (e.target.classList.contains(delete-btn)) { handleDelete(e); } });提示事件委托利用了事件冒泡机制无论新增多少子元素都无需重新绑定2. XSS安全漏洞直接使用innerHTML插入用户输入内容极其危险// 危险代码用户输入可能包含恶意脚本 todoList.innerHTML li${userInput}/li;攻击者可输入scriptalert(XSS攻击)/script img srcx onerror恶意代码防御方案使用textContent代替innerHTML对特殊字符进行转义使用DOMPurify等库过滤// 安全写法 const li document.createElement(li); li.textContent userInput; todoList.appendChild(li);3. CSS选择器权重混乱新手常犯的选择器错误/* 问题代码选择器过于具体且重复 */ div#container ul.todo-list li.item span.text { color: blue; } /* 后面想修改却失效 */ .todo-list .text { color: red !important; /* 被迫使用!important */ }CSS权重管理原则避免过度限定选择器如div ul li优先使用class选择器保持选择器简单可覆盖/* 优化后 */ .todo-item { color: blue; } /* 需要修改时 */ .todo-list .todo-item { color: red; /* 自然覆盖 */ }4. 状态管理缺失许多新手Todo项目存在状态同步问题问题表现后果解决方案DOM直接存储状态操作后状态丢失使用数据模型分散的状态变量难以维护集中状态管理无唯一标识无法精准操作为每个todo添加id推荐状态结构const state { todos: [ { id: 1, text: 学习React, completed: false }, { id: 2, text: 部署项目, completed: true } ], filter: all };5. 缺乏数据持久化页面刷新后数据消失你需要本地存储方案对比方案容量有效期适用场景localStorage5MB永久Todo列表数据sessionStorage5MB会话期间临时数据IndexedDB大永久复杂结构化数据实现示例// 保存到localStorage function saveTodos() { localStorage.setItem(todos, JSON.stringify(state.todos)); } // 初始化时读取 function loadTodos() { const saved localStorage.getItem(todos); if (saved) state.todos JSON.parse(saved); }6. 无防抖处理的用户输入连续快速点击添加按钮会导致重复提交// 问题代码快速点击会创建多个todo addButton.addEventListener(click, addTodo);防抖(debounce)实现function debounce(fn, delay) { let timer; return function() { clearTimeout(timer); timer setTimeout(() fn.apply(this, arguments), delay); }; } addButton.addEventListener(click, debounce(addTodo, 300));7. 可访问性(A11Y)忽视合格的Todo List应该支持键盘操作Enter添加空格标记完成为视觉障碍者提供ARIA标签足够的颜色对比度改进示例input typetext aria-label新增待办事项 placeholder输入后按Enter添加 keyup.enteraddTodo button aria-label标记完成 clicktoggleComplete span classvisually-hidden完成/span svg.../svg /button8. 移动端适配缺失常见移动端问题及解决方案触摸目标太小按钮至少48x48px输入法遮挡滚动到可视区域滑动删除添加touch事件支持/* 触摸友好设计 */ .todo-item { padding: 12px; min-height: 48px; } .delete-btn { width: 48px; height: 48px; }9. 无错误边界处理健壮的代码应该处理// 数据加载失败 try { const data JSON.parse(localStorage.getItem(todos)); } catch (e) { console.error(解析失败, e); state.todos []; } // 网络请求失败 fetch(/api/todos) .then(response { if (!response.ok) throw new Error(请求失败); return response.json(); }) .catch(error { showToast(error.message); });10. 项目结构混乱典型的新手文件结构project/ index.html script.js style.css images/优化后的结构todo-app/ src/ components/ TodoItem/ index.js style.css stores/ todos.js utils/ storage.js api.js public/ index.html assets/ package.json实现第一个Todo List只是起点真正的价值在于理解这些避坑原则后你能写出更健壮、可维护的前端代码。当我在团队代码审查中看到类似问题时总会想起自己当年踩过的这些坑——现在你可以轻松跨过它们了。

更多文章