使用 JavaScript 构建待办事项列表应用

引言

在 Web 开发的漫长历史中,“待办事项列表”一直被视为程序员的“Hello World”。但正如我们在 2026 年所看到的,即使是简单的项目,也是实践现代工程理念、探索 AI 辅助工作流以及理解底层交互机制的绝佳契机。

在这篇文章中,我们将不仅构建一个功能完备的 Todo 应用,更会带你深入探讨如何利用最新的技术趋势来优化这一过程。我们将把目光从单纯的代码编写转移到“氛围编程”的体验上,利用 AI 作为我们的结对编程伙伴,共同打造一个既美观又具备生产级潜力的应用。

基础架构:HTML 与 CSS

构建视觉与结构

让我们从基础开始。为了给用户提供一种现代、流畅的体验,我们摒弃了默认的样式,转而使用 CSS 变量和 Flexbox 布局来创建一个响应式的界面。在 2026 年,我们不仅要考虑外观,还要考虑可访问性和性能。

以下是我们的 HTML 结构,它语义化且简洁:




    
    
    Modern Todo App 2026
    
        /* 定义 CSS 变量以便于主题切换和维护 */
        :root {
            --primary-color: #4a90e2;
            --secondary-color: #f06;
            --bg-gradient: linear-gradient(135deg, #1a1a2e, #16213e);
            --glass-bg: rgba(255, 255, 255, 0.1);
            --glass-border: rgba(255, 255, 255, 0.2);
            --text-color: #ffffff;
            --danger-color: #ff4757;
        }

        body {
            font-family: ‘Inter‘, system-ui, -apple-system, sans-serif;
            background: var(--bg-gradient);
            display: flex;
            justify-content: center;
            align-items: center;
            min-height: 100vh;
            margin: 0;
            color: var(--text-color);
        }

        /* 现代化的玻璃拟态容器 */
        .container {
            background: var(--glass-bg);
            backdrop-filter: blur(10px); /* 磨砂玻璃效果 */
            -webkit-backdrop-filter: blur(10px);
            border: 1px solid var(--glass-border);
            padding: 2.5rem;
            border-radius: 20px;
            box-shadow: 0 15px 35px rgba(0, 0, 0, 0.2);
            width: 100%;
            max-width: 450px;
            transition: transform 0.3s ease;
        }

        .container:hover {
            transform: translateY(-5px);
        }

        h2 {
            margin-top: 0;
            margin-bottom: 1.5rem;
            font-weight: 700;
            letter-spacing: 1px;
            text-align: center;
        }

        .input-group {
            display: flex;
            gap: 10px;
            margin-bottom: 1.5rem;
        }

        input[type="text"] {
            flex: 1;
            padding: 12px 15px;
            border-radius: 12px;
            border: 1px solid var(--glass-border);
            background: rgba(0, 0, 0, 0.2);
            color: #fff;
            outline: none;
            transition: all 0.3s;
        }

        input[type="text"]:focus {
            border-color: var(--primary-color);
            background: rgba(0, 0, 0, 0.4);
            box-shadow: 0 0 0 3px rgba(74, 144, 226, 0.3);
        }

        button {
            padding: 12px 20px;
            border-radius: 12px;
            border: none;
            background: var(--primary-color);
            color: #fff;
            font-weight: 600;
            cursor: pointer;
            transition: transform 0.2s, background 0.3s;
        }

        button:hover {
            transform: scale(1.05);
            background: #357abd;
        }

        button:active {
            transform: scale(0.95);
        }

        /* 任务列表样式 */
        .todo-list {
            list-style: none;
            padding: 0;
        }

        .todo-item {
            background: rgba(255, 255, 255, 0.05);
            border-bottom: 1px solid var(--glass-border);
            padding: 15px;
            border-radius: 12px;
            margin-bottom: 10px;
            display: flex;
            justify-content: space-between;
            align-items: center;
            animation: slideIn 0.3s ease forwards;
        }

        .todo-item.completed .task-text {
            text-decoration: line-through;
            opacity: 0.6;
        }

        .actions {
            display: flex;
            gap: 8px;
        }

        .btn-icon {
            background: transparent;
            padding: 5px;
            color: #ddd;
            font-size: 1.2rem;
        }

        .btn-icon:hover {
            background: rgba(255,255,255,0.1);
        }

        .btn-delete:hover { color: var(--danger-color); }

        @keyframes slideIn {
            from { opacity: 0; transform: translateY(10px); }
            to { opacity: 1; transform: translateY(0); }
        }
    


    

My Focus List 2026

设计理念:玻璃拟态与微交互

在这个设计中,我们采用了 玻璃拟态 风格。为什么?因为在 2026 年,用户界面越来越追求层次感和深度。通过 backdrop-filter: blur(10px),我们创建了一个磨砂玻璃效果,这不仅美观,还能让背景内容隐约透出,增强了上下文感知。

此外,请注意我们在 CSS 中定义的 @keyframes slideIn 动画。我们在 2026 年的核心理念之一是:每一个交互都应提供即时反馈。当用户添加任务时,任务不是生硬地出现,而是平滑地滑入。这种微交互极大地提升了用户体验的质感。

JavaScript 逻辑:从基础到工程化

核心功能实现

让我们深入探讨 JavaScript。在这个版本中,我们不再仅仅是“让代码跑起来”,而是要构建一个健壮的状态管理系统。我们将使用模块化的思维来组织代码,即使是在一个简单的 HTML 文件中。

// --- 状态管理 ---
class TodoStore {
    constructor() {
        // 使用一个更清晰的键名,避免冲突
        this.storageKey = ‘geeksforgeeks_todo_app_v1‘;
        this.todos = this._loadFromStorage() || [];
    }

    // 从 localStorage 加载数据,包含错误处理
    _loadFromStorage() {
        try {
            const data = localStorage.getItem(this.storageKey);
            return data ? JSON.parse(data) : null;
        } catch (error) {
            console.error("数据读取失败,可能是存储损坏", error);
            return null;
        }
    }

    // 持久化保存
    _saveToStorage() {
        try {
            localStorage.setItem(this.storageKey, JSON.stringify(this.todos));
        } catch (error) {
            console.error("无法保存到 localStorage,可能是存储空间已满", error);
            alert("警告:您的任务无法保存,请检查浏览器存储空间。");
        }
    }

    addTodo(text) {
        const newTodo = {
            id: Date.now(), // 简单生成唯一ID,生产环境建议使用 uuid
            text: text,
            completed: false,
            createdAt: new Date().toISOString()
        };
        this.todos.push(newTodo);
        this._saveToStorage();
        return newTodo;
    }

    deleteTodo(id) {
        this.todos = this.todos.filter(todo => todo.id !== id);
        this._saveToStorage();
    }

    toggleTodo(id) {
        const todo = this.todos.find(t => t.id === id);
        if (todo) {
            todo.completed = !todo.completed;
            this._saveToStorage();
        }
    }

    editTodo(id, newText) {
        const todo = this.todos.find(t => t.id === id);
        if (todo) {
            todo.text = newText;
            this._saveToStorage();
        }
    }
}

// --- UI 控制器 ---
const store = new TodoStore();
const taskInput = document.getElementById(‘taskInput‘);
const addBtn = document.getElementById(‘addBtn‘);
const taskList = document.getElementById(‘taskList‘);

// 渲染函数:根据状态生成 DOM
function render() {
    taskList.innerHTML = ‘‘;
    store.todos.forEach(todo => {
        const li = document.createElement(‘li‘);
        li.className = `todo-item ${todo.completed ? ‘completed‘ : ‘‘}`;
        
        // 为了安全性,使用 textContent 而不是 innerHTML 来显示用户输入
        li.innerHTML = `
            ${escapeHtml(todo.text)}
            
`; taskList.appendChild(li); }); } // 安全处理:防止 XSS 攻击 function escapeHtml(text) { const div = document.createElement(‘div‘); div.textContent = text; return div.innerHTML; } // 事件处理 function handleAdd() { const text = taskInput.value.trim(); if (!text) { // 使用更友好的方式提示,而不是 alert taskInput.placeholder = "请输入内容..."; taskInput.focus(); return; } store.addTodo(text); taskInput.value = ‘‘; render(); } // 将函数挂载到 window 对象以便 HTML 中的 onclick 可以访问 window.handleDelete = (id) => { if(confirm(‘确定要删除这个任务吗?‘)) { store.deleteTodo(id); render(); } }; window.handleToggle = (id) => { store.toggleTodo(id); render(); }; // 初始化 addBtn.addEventListener(‘click‘, handleAdd); taskInput.addEventListener(‘keypress‘, (e) => { if (e.key === ‘Enter‘) handleAdd(); }); // 首次渲染 render();

深度解析:为什么这样写?

你可能已经注意到,我们没有直接操作 DOM 数组,而是引入了 TodoStore 类。这就是我们在 2026 年倡导的关注点分离

  • 数据与视图解耦:INLINECODE51f8582f 只关心数据,INLINECODE4c94514c 只关心显示。当应用变得复杂时(例如需要添加撤销功能或云同步),这种分离能让我们轻松更换 UI 框架(如 React 或 Vue)而无需重写逻辑代码。
  • 安全性:请看 INLINECODEf40f792f 函数。在早期的教程中,直接使用 INLINECODE8bf5dfbd 插入用户输入是非常普遍的,但这会导致 XSS(跨站脚本攻击) 漏洞。作为专业的工程师,我们必须考虑到用户可能会输入恶意的 INLINECODE5bac0328 标签。通过 INLINECODEae6c6813 转义,我们确保了应用的安全性。

2026 年开发趋势:AI 辅助与 Vibe Coding

现在,让我们跳出具体的代码,谈谈在 2026 年我们是如何编写这段代码的。

Vibe Coding:与 AI 结对编程

如果你正在使用 CursorWindsurf 这样的现代 IDE,你会发现编写这个应用的过程发生了根本性的变化。这就是我们所说的 Vibe Coding(氛围编程)

我们不再逐字符敲击代码。相反,我们像是在指挥一个极其聪明的实习生:

  • Prompt: “创建一个待办事项列表,使用深色主题和玻璃拟态效果,确保包含 LocalStorage 持久化。”
  • AI Agent Action: AI 不仅生成了 HTML 和 CSS,甚至还预测到了我们需要添加 XSS 防护,并自动为我们生成了 escapeHtml 函数。

Agentic AI 的介入

在开发过程中,你可能会遇到这样一个 Bug:“LocalStorage 在隐身模式下无法保存”。在 2025 年,你可能需要去 Stack Overflow 搜索。但在 2026 年,你的 IDE 集成了 Agentic AI

当你运行代码并报错时,Agent 会主动分析报错堆栈,阅读你的上下文代码,然后直接弹出一个修复建议:“检测到 QuotaExceededError,建议添加 try-catch 块来优雅地处理存储满的情况。” 它甚至会直接为你写出这段错误处理代码。

多模态调试体验

想象一下,你想要调整按钮的阴影效果。你不需要反复猜测 box-shadow 的数值。你可以直接在代码编辑器中生成一个图片预览,或者通过语音告诉 AI:“让这个按钮看起来更悬浮一点”。AI 会自动调整 CSS 参数,并实时渲染出结果供你确认。这种多模态的开发体验极大地释放了我们的创造力,让我们专注于逻辑和设计,而不是语法细节。

生产级考量:性能与可维护性

虽然这是一个简单的项目,但如果我们将其视为一个大型产品的一部分,必须考虑以下工程化问题。

性能优化策略

在 INLINECODE885ab19f 函数中,我们采用了最简单的“全量刷新”策略:每次数据变动,清空 INLINECODE56002184 并重建 DOM。这在任务少于 100 条时是完全没问题的,响应速度极快。

但是,如果我们的 Todo List 需要支持成千上万条任务(例如是一个庞大的系统日志查看器),这种策略就会导致严重的性能问题(DOM 操作昂贵)。在生产环境中,我们会采用 Virtual DOM(虚拟 DOM)的思想:

  • 对比新旧数据树。
  • 计算出最小变更集。
  • 只更新发生变化的那个具体的 DOM 节点。

这就是为什么 React 或 Vue 这类框架在复杂应用中更受欢迎的原因。但在我们的这个场景下,原生 JS 的简单直接才是最有效的“优化”——避免引入 200kb 的库文件所带来的加载时间损耗。

技术债务与决策

我们在项目中直接使用了 INLINECODE164d72d9 作为 ID。这在一个单机单用户的应用中是可以接受的。但是,如果我们计划后续添加“多端同步”功能,这就是一个巨大的 技术债务。INLINECODE05af409f 在极快的操作下可能会产生重复 ID,且在不同的设备上无法保持一致。

我们的决策建议:如果你预见这个应用会生长,请尽早引入 UUID 库(如 uuid)来生成唯一标识符。这种前瞻性的思考能避免未来的大规模重构。

结语

通过这个简单的待办事项列表,我们回顾了从 DOM 操作、状态管理到安全性处理的完整开发流程。更重要的是,我们展望了 2026 年的开发图景:AI 不仅是辅助工具,更是我们的合作者;Vibe Coding 让我们更专注于创造价值而非琐碎的语法。

无论你是初学者还是资深开发者,保持对底层原理的理解(如 DOM 事件流、存储机制),同时拥抱新的开发工具(如 Agentic IDEs),都是在这个快速变化的时代中立于不败之地的关键。

现在,打开你的编辑器,开始构建属于你自己的下一个伟大应用吧!

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。如需转载,请注明文章出处豆丁博客和来源网址。https://shluqu.cn/37137.html
点赞
0.00 平均评分 (0% 分数) - 0