F16第 1 / 1 课时未开始内容版本 1.0.0 · 核验于 2026-10-26

前端基础综合练习

前端基础综合练习 · 阅读后完成下方实验与检查题,再用「标记完成」记录进度。

学习目标

这是前端基础路线的综合练习:从空目录开始,独立完成一个可筛选的学习资源页,把语义结构、响应式、请求状态、类型和本地偏好五条线拧成一件作品。读完你应该能独立搭出一个「像样」的静态前端页面,并带着一份自查过的验收清单离开。

问题场景

单课知识点都练过了,拼起来却是另一回事:筛选栏写完发现键盘焦点顺序乱;请求处理了失败却漏了空状态;localStorage 存了偏好但没防坏数据。综合练习的价值就在暴露这些「接缝处的洞」——这一课给你完整做一遍的机会,错了有清单兜底。

核心解释

用固定骨架把五条线组织起来。数据层(类型 + 请求 + 校验):

interface Resource {
  id: number;
  title: string;
  track: 'foundation' | 'react' | 'nextjs';
  hours: number;
  url: string;
}

type LoadState =
  | { status: 'loading' }
  | { status: 'error'; message: string }
  | { status: 'ready'; items: Resource[] };

async function loadResources(): Promise<Resource[]> {
  const res = await fetch('https://jsonplaceholder.typicode.com/posts?_limit=12');
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
  const raw: unknown = await res.json();
  if (!Array.isArray(raw)) throw new Error('响应格式异常');
  return raw.map((x, i) => ({
    id: i,
    title: String(x.title),
    track: 'react' as const,
    hours: 8,
    url: '#',
  }));
}

展示层(语义 + 响应式):

<main>
  <h1>学习资源</h1>
  <form aria-label="筛选">
    <label for="track">方向</label>
    <select id="track" name="track">
      <option value="all">全部</option>
    </select>
  </form>
  <ul class="grid" id="list"></ul>
</main>
.grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 12px;
  list-style: none;
  padding: 0;
}

@media (max-width: 600px) {
  body { padding: 12px; }
}

偏好保存沿用 F13 的防线:读写都包 try/catch,带版本号,读回的字段做校验。

示例拆解

验收时按「用户旅程」过一遍,每一站都有明确标准:

1. 打开页面:加载态可见(不是白屏)→ 数据到达 → 列表渲染
2. 改错接口地址重试:错误态有文案和「重试」按钮,不是白屏
3. 筛选「全部 → react」:结果变化;切换回「全部」不报错
4. 只用键盘:Tab 顺序合理,焦点框清晰,Enter 能操作
5. 拖窄到手机宽度:无页面级横向滚动,列表单列可读
6. 刷新页面:上次选的方向和主题还在(localStorage)
7. 手动改坏存储再刷新:页面照常打开(回退默认值)

这份清单就是交付标准。实现顺序建议:先骨架(HTML 结构 + 硬编码假数据)→ 再请求与状态 → 再筛选与偏好 → 最后响应式与可访问性走查。每一步都可运行、可演示,小步提交(参考 F14),不要憋大招。

动手任务

本课没有在线实验,独立完成整页(纯 HTML/CSS/JS 或加上 TS 都可以,起本地服务预览):

  1. 完成页面本体:语义结构 + fetch 请求 + 加载/错误/空三种状态 + 按 track 筛选。
  2. 接上偏好:记住用户上次选的方向(localStorage,按 F13 的套路),刷新后自动选中。
  3. 按示例拆解的 7 条验收清单逐条自查,把每条结果(通过/不通过加原因)记在笔记里;不通过的全部修掉。

常见错误

  • 只写「成功路径」:加载中、失败、空列表三个状态缺任何一个,真实使用里都是白屏或假死。
  • 筛选状态与存储脱节:刷新后筛选丢失;基础线里至少要用 localStorage 接住刷新。
  • 综合练习写成各课代码拼接:类型定义了却到处 any,媒体查询写了却没验收;判断标准是清单,不是「代码里出现过」。
  • 一步做完才第一次运行:错误堆到一起无从下手;小步运行、小步提交,每步可演示。

检查与复盘

完成下方检查题确认理解。自查清单:

  • 三种状态(加载/错误/空)在页面上都能真实演示
  • 纯键盘与手机宽度两条路径完整走通过一遍
  • 偏好保存经过坏数据考验,页面不会因此白屏

参考资料

检查题共 2 题,提交后立即显示解析
答案由服务端判定;答错的题会自动进入复习计划。
Q1故障定位较难

bug 现场:课程详情页需求——进入页面请求详情并显示;点「收藏」调 POST /api/favorite,成功后按钮变「已收藏」。实际现象:点收藏后按钮闪一下又变回「未收藏」,刷新页面收藏状态仍是未收藏。最可能的根因是?

// 页面组件(React 18+)
const [fav, setFav] = useState(false);

useEffect(() => {
  fetch('/api/course/' + id)
    .then((r) => r.json())
    .then((d) => setFav(d.favored));
}, []);

async function onFav() {
  const res = await fetch('/api/favorite', {
    method: 'POST',
    body: JSON.stringify({ courseId: id }),
  });
  if (res.ok) setFav(true);
}
Q2读代码简单

阅读代码(React 18+)。这是综合实践里「课程卡片列表」的筛选逻辑:页面初始为「全部」,随后依次点击「前端」「全部」按钮,列表先后显示什么?

const courses = [
  { id: 1, title: 'Web 基础', track: 'foundation' },
  { id: 2, title: 'React 入门', track: 'react' },
  { id: 3, title: 'Go 后端', track: 'go' },
];
const [active, setActive] = useState('all');

const visible =
  active === 'all' ? courses : courses.filter((c) => c.track === active);

// 筛选按钮:
// <button onClick={() => setActive('react')}>前端</button>
// <button onClick={() => setActive('all')}>全部</button>

读完这一课了?

标记完成后,工作台会安排接下来的学习。

Command Palette

Search for a command to run...