第 10 章 · 综合实战:个人主页与待办清单
本章目标:综合运用 HTML、CSS、JavaScript,在 academy-demo 仓库中完成两个可展示、可验收的小项目:个人主页(Portfolio) 与 待办清单(Todo),为后续 ES6+ 与工程化章节打基础。
前置:第 1~9 章;建议用时 6~8 小时。
10.1 项目总览
| 项目 | 路径 | 核心能力 | 验收权重 |
|---|
| 个人主页 | academy-demo/portfolio/ | 语义 HTML、响应式、CSS 变量 | 40% |
| 待办清单 | academy-demo/todo/ | DOM、事件委托、localStorage | 60% |
academy-demo/
├── portfolio/
│ ├── index.html
│ ├── css/
│ │ ├── theme.css # 颜色、字体、CSS 变量
│ │ └── layout.css # Flex / Grid 布局
│ ├── js/
│ │ └── main.js # 平滑滚动、主题(可选)
│ └── assets/
│ └── avatar.jpg
└── todo/
├── index.html
├── css/
│ └── app.css
└── js/
└── app.js # 增删改查 + 持久化
10.2 项目一:个人主页
10.2.1 功能清单
| 功能 | 技术要求 | 优先级 |
|---|
| 响应式单页 | 头图、简介、技能、项目卡、联系方式 | P0 |
| 语义结构 | header / nav / main / section / footer | P0 |
| 设计令牌 | CSS 变量统一主色、间距、圆角 | P0 |
| 布局 | Flex 导航 + Grid 项目卡片 | P0 |
| 锚点导航 | scroll-behavior: smooth 或 JS 平滑滚动 | P1 |
| 深色模式 | prefers-color-scheme 或手动切换 | P2 |
10.2.2 关键 HTML 结构
<header class="hero" id="top">
<img class="avatar" src="assets/avatar.jpg" alt="张三的头像" width="120" height="120">
<h1>张三</h1>
<p class="tagline">前端学习者 · 贤紫技术学院 academy-demo</p>
<nav aria-label="页内导航"><a href="#about">关于</a><a href="#projects">项目</a></nav>
</header>
<main>
<section id="about"><h2>关于我</h2><div class="skills"><span class="tag">HTML</span>...</div></section>
<section id="projects"><h2>项目展示</h2><article class="card"><a href="../todo/">Todo 演示</a></article></section>
</main>
10.2.3 CSS 变量与响应式
/* theme.css */
:root {
--color-primary: #c2185b;
--color-bg: #fafafa;
--color-text: #212121;
--space-md: 1rem;
--radius: 8px;
}
@media (prefers-color-scheme: dark) {
:root {
--color-bg: #121212;
--color-text: #e0e0e0;
}
}
/* layout.css */
.project-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: var(--space-md);
}
.hero nav {
display: flex;
gap: 1rem;
flex-wrap: wrap;
justify-content: center;
}
10.2.4 可选 JS 增强
// portfolio/js/main.js
document.querySelectorAll('a[href^="#"]').forEach(link => {
link.addEventListener('click', (e) => {
const id = link.getAttribute('href');
if (id === '#') return;
const target = document.querySelector(id);
if (!target) return;
e.preventDefault();
target.scrollIntoView({ behavior: 'smooth', block: 'start' });
});
});
10.3 项目二:待办清单(Todo)
10.3.1 功能清单
| 功能 | 说明 | 状态字段 |
|---|
| 添加任务 | 输入框 + 按钮或 Enter | text, done: false |
| 勾选完成 | 切换 done | |
| 删除单项 | 每行删除按钮 | |
| 统计 | 显示未完成 / 已完成数量 | |
| 持久化 | localStorage 刷新不丢 | |
| 筛选(P1) | 全部 / 进行中 / 已完成 | filter 状态 |
10.3.2 数据模型
/**
* @typedef {{ id: number, text: string, done: boolean }} Todo
*/
const STORAGE_KEY = 'academy-demo:todos';
let todos = /** @type {Todo[]} */ ([]);
let filter = 'all'; // 'all' | 'active' | 'completed'
10.3.3 核心逻辑