当前位置: 首页 > news >正文

JavaScript 从零基础到精通系列:打造“我的任务管家”应用

摘要: 这是整个系列的终极实战。我们将使用 HTML5、CSS3 和纯 JavaScript (ES6+) 构建一个功能完备的待办事项应用:添加、完成、删除、筛选任务,数据通过 localStorage 持久化存储,并引入一个名言 API 每日激励。过程中还将使用 ES6 模块化组织代码,并回顾原型与类。完成这个项目,你将真切感受到自己已经从零基础走向了 JavaScript 开发者的行列。


一、项目需求与功能设计

功能列表

  • 添加新任务(文本输入框 + 添加按钮)

  • 标记任务为完成/未完成(带线划过效果)

  • 删除单个任务

  • 筛选:全部 / 未完成 / 已完成

  • 搜索过滤任务

  • 数据持久化:使用localStorage,刷新不丢失

  • 获取每日一句名言,显示在顶部

  • 美观的 UI(使用纯 CSS 变量)

二、项目结构搭建

我们将使用 ES6 模块来拆分代码,但需要注意,模块要在服务器环境下运行(Live Server 插件可解决)。也可以将<script type="module">内联,但为了清晰,我们拆分文件。

项目文件结构:

task-manager/ ├─ index.html # 主页面 ├─ css/ │ └─ style.css # 完整美化样式 └─ js/ ├─ main.js # 控制器 ├─ taskModel.js # 数据模型 ├─ taskView.js # 视图渲染 └─ api.js # 名言API

三、HTML 结构 (index.html)

<!DOCTYPE html> <html lang="zh"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>📋 我的任务管家</title> <link rel="stylesheet" href="css/style.css"> </head> <body> <div class="app"> <header> <h1>📋 我的任务管家</h1> <div id="quote" class="quote">加载中...</div> </header> <div class="controls"> <input type="text" id="taskInput" placeholder="添加一个新任务..." autocomplete="off"> <button id="addBtn">➕ 添加</button> </div> <div class="filter-bar"> <input type="text" id="searchInput" placeholder="🔍 搜索任务..."> <div class="filter-buttons"> <button>四、CSS 美化(css/style.css

使用 CSS 变量统一风格,列表项动画等。

:root { --primary: #4a6fa5; --primary-light: #5a7fbb; --success: #2ecc71; --danger: #e74c3c; --gray: #95a5a6; --bg: #f8f9fc; --card: #ffffff; --text: #2c3e50; --border: #e3e6f0; --shadow: 0 4px 8px rgba(0,0,0,0.08); } * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: 'Segoe UI', Roboto, 'Helvetica Neue', sans-serif; background: var(--bg); display: flex; justify-content: center; padding: 2rem 1rem; color: var(--text); line-height: 1.6; } .app { width: 100%; max-width: 650px; } header { text-align: center; margin-bottom: 2rem; } header h1 { font-size: 2rem; margin-bottom: 0.8rem; color: var(--primary); } .quote { font-size: 0.95rem; color: var(--gray); font-style: italic; max-width: 500px; margin: 0 auto; } .controls { display: flex; gap: 0.5rem; margin-bottom: 1rem; } #taskInput { flex: 1; padding: 0.9rem 1rem; border: 1px solid var(--border); border-radius: 10px; font-size: 1rem; outline: none; transition: 0.2s; } #taskInput:focus { border-color: var(--primary); box-shadow: 0 0 0 2px rgba(74, 111, 165, 0.2); } #addBtn { padding: 0.9rem 1.4rem; background: var(--primary); color: white; border: none; border-radius: 10px; cursor: pointer; font-size: 1rem; transition: 0.2s; } #addBtn:hover { background: var(--primary-light); } .filter-bar { background: var(--card); padding: 1rem; border-radius: 10px; box-shadow: var(--shadow); margin-bottom: 1rem; display: flex; flex-direction: column; gap: 0.8rem; } #searchInput { padding: 0.7rem 1rem; border: 1px solid var(--border); border-radius: 8px; font-size: 0.95rem; outline: none; } .filter-buttons { display: flex; gap: 0.5rem; } .filter-buttons button { flex: 1; padding: 0.6rem; border: 1px solid var(--border); background: white; border-radius: 8px; cursor: pointer; transition: 0.2s; } .filter-buttons button.active { background: var(--primary); color: white; border-color: var(--primary); } #taskCount { font-size: 0.9rem; color: var(--gray); text-align: center; } .task-list { list-style: none; } .task-item { display: flex; align-items: center; background: var(--card); padding: 1rem; margin-bottom: 0.6rem; border-radius: 10px; box-shadow: var(--shadow); transition: all 0.25s ease; } .task-item:hover { transform: translateY(-2px); box-shadow: 0 6px 12px rgba(0,0,0,0.1); } .task-item.completed .task-text { text-decoration: line-through; opacity: 0.6; } .toggle-checkbox { transform: scale(1.3); margin-right: 1rem; accent-color: var(--success); cursor: pointer; } .task-text { flex: 1; font-size: 1rem; word-break: break-all; } .delete-btn { background: var(--danger); color: white; border: none; padding: 0.4rem 0.7rem; border-radius: 6px; cursor: pointer; font-size: 0.9rem; transition: 0.2s; } .delete-btn:hover { background: #c0392b; } .clear-btn { width: 100%; padding: 0.9rem; background: #fff; color: var(--danger); border: 1px solid var(--danger); border-radius: 10px; cursor: pointer; font-size: 1rem; margin-top: 1rem; transition: 0.2s; } .clear-btn:hover { background: #fef2f2; }

五、JavaScript 核心逻辑 (模块化)

我们将采用 MVC 模式的简化版,分离数据、视图和控制器。

5.1 模型层 (js/taskModel.js)

负责数据存储、业务逻辑,与 localStorage 交互。

const STORAGE_KEY = 'taskManagerTasks'; export default class TaskModel { constructor() { this.tasks = this.loadTasks(); } // 从本地存储读取任务 loadTasks() { const stored = localStorage.getItem(STORAGE_KEY); return stored ? JSON.parse(stored) : []; } // 保存到本地存储 saveTasks() { localStorage.setItem(STORAGE_KEY, JSON.stringify(this.tasks)); } // 添加任务 addTask(text) { const task = { id: Date.now(), text, completed: false, createdAt: new Date().toISOString() }; this.tasks.push(task); this.saveTasks(); return task; } // 删除任务 deleteTask(id) { this.tasks = this.tasks.filter(task => task.id !== id); this.saveTasks(); } // 切换完成状态 toggleTask(id) { const task = this.tasks.find(t => t.id === id); if (task) { task.completed = !task.completed; this.saveTasks(); } } // 清除已完成 clearCompleted() { this.tasks = this.tasks.filter(task => !task.completed); this.saveTasks(); } // 筛选 + 搜索 getFilteredTasks(filter = 'all', searchTerm = '') { let filtered = [...this.tasks]; if (filter === 'active') { filtered = filtered.filter(t => !t.completed); } else if (filter === 'completed') { filtered = filtered.filter(t => t.completed); } if (searchTerm.trim()) { const term = searchTerm.toLowerCase(); filtered = filtered.filter(t => t.text.toLowerCase().includes(term)); } return filtered; } }

5.2 视图层 (js/taskView.js)

负责 DOM 渲染与事件绑定监听的回调。

export default class TaskView { constructor() { this.taskList = document.getElementById('taskList'); this.taskCount = document.getElementById('taskCount'); this.filterButtons = document.querySelectorAll('.filter-buttons button'); } // 渲染任务列表 displayTasks(tasks) { this.taskList.innerHTML = ''; tasks.forEach(task => { const li = this.createTaskElement(task); this.taskList.appendChild(li); }); this.updateCount(tasks); } // 创建单个任务DOM createTaskElement(task) { const li = document.createElement('li'); li.className = `task-item ${task.completed ? 'completed' : ''}`; li.dataset.id = task.id; li.innerHTML = ` <input type="checkbox" class="toggle-checkbox" ${task.completed ? 'checked' : ''}> <span class="task-text">${task.text}</span> <button class="delete-btn">🗑️</button> `; return li; } // 更新任务数量 updateCount(tasks) { const left = tasks.filter(t => !t.completed).length; this.taskCount.textContent = `${tasks.length} 个任务 (${left} 未完成)`; } // 高亮当前筛选按钮 setActiveFilter(filterValue) { this.filterButtons.forEach(btn => { btn.classList.toggle('active', btn.dataset.filter === filterValue); }); } // 绑定添加任务 bindAddTask(handler) { document.getElementById('addBtn').addEventListener('click', handler); document.getElementById('taskInput').addEventListener('keypress', e => { if (e.key === 'Enter') handler(); }); } // 绑定删除任务 bindDeleteTask(handler) { this.taskList.addEventListener('click', e => { if (e.target.classList.contains('delete-btn')) { const id = Number(e.target.closest('.task-item').dataset.id); handler(id); } }); } // 绑定切换完成状态 bindToggleTask(handler) { this.taskList.addEventListener('change', e => { if (e.target.classList.contains('toggle-checkbox')) { const id = Number(e.target.closest('.task-item').dataset.id); handler(id); } }); } // 绑定筛选 bindFilter(handler) { this.filterButtons.forEach(btn => { btn.addEventListener('click', () => handler(btn.dataset.filter)); }); } // 绑定搜索 bindSearch(handler) { document.getElementById('searchInput').addEventListener('input', e => handler(e.target.value)); } // 绑定清除已完成 bindClearCompleted(handler) { document.getElementById('clearCompleted').addEventListener('click', handler); } // 获取输入框内容 getInputText() { return document.getElementById('taskInput').value.trim(); } // 清空输入框 clearInput() { document.getElementById('taskInput').value = ''; document.getElementById('taskInput').focus(); } }

5.3 API 模块 (js/api.js)

获取每日名言。

// 获取中文每日一句(接口:https://v.api.aa1.cn/api/yiyan/index.php) export async function fetchQuote() { // 本地兜底中文语录(接口失败时显示) const localQuotes = [ "道阻且长,行则将至;行而不辍,未来可期。", "星光不问赶路人,时光不负有心人。", "每一份坚持,都是成功的伏笔。", "脚踏实地,仰望星空,认真过好每一天。", "愿你历尽千帆,归来仍是少年。", "努力的意义,在于让自己拥有更多选择。", "不必急着抵达,沿途风景亦有深意。", "心有山海,静而无边,稳步向前。" ]; try { // 你指定的接口 const res = await fetch('https://v.api.aa1.cn/api/yiyan/index.php'); if (!res.ok) throw new Error('接口请求失败'); // 获取返回的带标签文本 const htmlText = await res.text(); // 自动去掉 <p> </p> 等所有 HTML 标签 const cleanText = htmlText.replace(/<[^>]+>/g, '').trim(); // 返回干净美观的句子 return `“${cleanText}”`; } catch (err) { // 接口异常时显示本地语录 const randomIdx = Math.floor(Math.random() * localQuotes.length); return `“${localQuotes[randomIdx]}” — 励志语录`; } }

5.4 主控制器 (js/main.js)

把模型、视图和 API 聚合起来。

import TaskModel from './taskModel.js'; import TaskView from './taskView.js'; import { fetchQuote } from './api.js'; class App { constructor(model, view) { this.model = model; this.view = view; this.currentFilter = 'all'; this.searchTerm = ''; // 绑定所有事件 this.view.bindAddTask(this.handleAddTask.bind(this)); this.view.bindDeleteTask(this.handleDeleteTask.bind(this)); this.view.bindToggleTask(this.handleToggleTask.bind(this)); this.view.bindFilter(this.handleFilter.bind(this)); this.view.bindSearch(this.handleSearch.bind(this)); this.view.bindClearCompleted(this.handleClearCompleted.bind(this)); // 初始化 this.loadQuote(); this.refreshDisplay(); } // 加载名言 async loadQuote() { const quote = await fetchQuote(); document.getElementById('quote').textContent = quote; } // 刷新页面显示 refreshDisplay() { const filtered = this.model.getFilteredTasks(this.currentFilter, this.searchTerm); this.view.displayTasks(filtered); } // 添加任务 handleAddTask() { const text = this.view.getInputText(); if (!text) return alert('任务内容不能为空'); this.model.addTask(text); this.view.clearInput(); this.refreshDisplay(); } // 删除任务 handleDeleteTask(id) { this.model.deleteTask(id); this.refreshDisplay(); } // 切换完成状态 handleToggleTask(id) { this.model.toggleTask(id); this.refreshDisplay(); } // 筛选 handleFilter(filter) { this.currentFilter = filter; this.view.setActiveFilter(filter); this.refreshDisplay(); } // 搜索 handleSearch(term) { this.searchTerm = term; this.refreshDisplay(); } // 清除已完成 handleClearCompleted() { this.model.clearCompleted(); this.refreshDisplay(); } } // 启动应用 new App(new TaskModel(), new TaskView());

六、运行与测试

将项目文件夹放在本地服务器(VS Code 的 Live Server 插件右键打开index.html即可,因为 ES6 模块要求 HTTP 协议)。

添加任务,尝试筛选、搜索、标记完成。

刷新页面,任务依然存在,因为保存在了localStorage,顶部的名言会在每次刷新时更换。

七、知识回顾与未来之路

在这个项目里,我们几乎用到了整个系列的所有重要知识点:

  • 基础语法:变量、模板字符串、箭头函数、展开运算符。

  • 流程控制:条件判断、循环在逻辑中的体现。

  • 对象与数组:任务对象、数组方法(filter、map、find),JSON 序列化。

  • DOM 与事件:事件委托处理动态元素,类名切换样式。

  • 异步编程async/await获取 API 数据。

  • 模块化:ES6 模块拆分项目,数据模型与视图分离。

  • 原型与类:这里使用了类语法(class),本质仍是原型继承。面向对象思想让代码结构清晰。

你已经不再是小白了。从 1995 年那个 10 天诞生的脚本语言,到如今构建出功能完备的单页应用,你亲身体验了 JavaScript 的强大与魅力。

下一步学习方向

  • 深入语言:原型链、this 绑定规则、生成器、迭代器、Proxy/Reflect。

  • 前端框架:React、Vue 或 Angular,它们让大规模应用开发更高效。

  • Node.js 后端:用同一门语言开发服务器,学习 Express、数据库(MongoDB/MySQL)。

  • 工程化:Webpack、Vite 打包工具,ESLint 代码规范,TypeScript 类型系统。


总结: 恭喜你完成了整个 JavaScript 从零到精通的旅程!我们追溯了历史,夯实了语法,征服了异步,最终整合出一个完整的任务管家项目。这篇文章不仅仅是一个结尾,更是你编程生涯的新起点。代码世界无限广阔,愿 JavaScript 伴你一往无前。现在,去用你学到的技能创造一些酷炫的东西吧!


如果这篇文章帮你解决了实操上的困惑,别忘记点击点赞、分享,也可以留言告诉我你遇到的其它问题,我会尽快回复。动手练习是掌握编程最快的方法,请务必亲手敲一遍本文的所有示例代码,并截图保存你的成果。你的关注是我坚持原创和细节共享的力量来源,谢谢大家。

http://www.zskr.cn/news/1435905.html

相关文章:

  • ComfyUI-Impact-Pack V8:AI图像细节增强的智能解决方案
  • 谷歌内核(Chrome/Chromium)常用 CLI 运行参数
  • YimMenu完整指南:GTA5最强免费辅助工具全面解析
  • 人力资源管理系统如何提升员工入转调离效率“
  • 2026贵金属回收行业:五大标杆机构解读 - 资讯纵览
  • 如何3步解决B站缓存视频无法播放问题:BilibiliCacheVideoMerge完整操作指南
  • 【仅限风控负责人查阅】:Gemini模型监管合规红线预警(银保监EAST 5.0新规适配 checklist + 8处审计高危字段标注)
  • 【Gemini对手全景图谱】:2024年全球7大AI大模型竞对实力对比与技术代差分析
  • 洛雪音乐无损音源终极指南:3步解锁全网高品质音乐
  • 跨平台漫画阅读新纪元:nhentai-cross如何重塑多设备阅读体验?
  • 洛雪音乐音源完整配置指南:3步实现全网无损音乐体验
  • 区块链+联邦学习:构建可信AI网络数据共享架构实战
  • 2026 玻璃钢罐厂家专业评测榜 、推荐排行 ! - 资讯纵览
  • 网络规划设计师案例要求
  • 基于Arduino与3D打印的六轮摇臂转向机器人平台设计与实现
  • 记忆压缩:高效管理 AI Agent 的记忆
  • 想写一个找车app,但是汽油车,没有电,如何跟app连接通讯呢?
  • 3步掌握X-Spider:告别繁琐的推特媒体收集工作
  • Arduino NANO程序上传失败?CH340G驱动与硬件时钟问题全解析
  • 2026武汉黄金回收多少钱一克?靠谱商家推荐TOP3,13区全域免费上门当场到账 - 资讯纵览
  • OpenCV 是什么?能做什么?一张图看懂计算机视觉入门
  • 长期记忆存储:构建持久的 AI 记忆系统
  • 用Reflex全栈Python框架快速构建AI文生图Web应用
  • 洛雪音乐音源全攻略:从新手到高手的完整配置方案
  • CentOS 7.9物理机IPMI环境搭建保姆级教程(含OpenIPMI和ipmitool安装配置)
  • 操作系统-day05-p是地址变量,p是地址变量,p是地址变量
  • Arduino超声波避障机器人:从传感器原理到电机驱动的完整实践
  • 终极指南:三步掌握X-Spider高效下载推特媒体内容
  • 从超级碗广告看机器人未来:六大趋势揭示人机共融新范式
  • 从零构建自平衡机器人:Arduino、MPU6050与PID控制实战