Skip to content

Latest commit

 

History

History
522 lines (379 loc) · 15.2 KB

File metadata and controls

522 lines (379 loc) · 15.2 KB

NetLearner 项目开发日志

记录从 v1 到 v5 的全部问题、根因分析、修复方案。 作为后续维护的参考手册。


目录

  1. 架构决策
  2. 问题清单
  3. 关键修复详解
  4. 经验教训
  5. 后续维护指南

1. 架构决策

技术栈

前端: 原生 HTML/CSS/JS (Vanilla) — 零依赖,无构建工具
路由: Hash-based SPA — 所有页面内嵌 index.html,hash 切换
存储: localStorage (L1) + JSON 导入/导出 (L2)
服务器: Node.js 内建 http 模块 — 零 npm 依赖,零第三方包
题库: JSON 文件 — AI 生成,直接 fetch 加载

为什么是 SPA 而不是多页面

  • 多页面方案需要每个页面重复加载 CSS/JS
  • 需要处理 CORS(file:// 协议下 fetch 不能用)
  • GitHub Pages 部署时需要额外配置 404.html 做 fallback
  • SPA 一个 index.html 解决所有问题

为什么自写 server.js 而不是 http-server

http-server 的坑:
  - 默认 Cache: 3600s(1小时缓存),导致跨浏览器不一致
  - -c-1 参数在部分版本失效
  - 与 Python http.server 同时监听同一端口时无报错,静默冲突

自写 server.js 的优势:
  - 精确控制缓存头:Cache-Control: no-store + Pragma: no-cache + Expires: 0
  - 单一进程、唯一启动方式
  - 零外部依赖

2. 问题清单

P0 — 功能阻断

# 问题 首次出现 根因 修复版本
P0-1 CCNA 模拟考显示 32 题(应为 60 题) v2 浏览器缓存了旧版 JSON 文件 v4
P0-2 CCNP 水平测试在 Edge 显示"待推出" v2 Edge 缓存了文件不存在时的 404 响应 v4
P0-3 两个服务器同时监听 8080 端口 v3 http-server 未彻底关闭,与 server.js 冲突 v4
P0-4 Chrome 和 Edge 显示不同版本内容 v3 不同浏览器在不同时间点各自缓存了不同版本 v4
P0-5 模拟考返回后列表空白,刷新才恢复 v4 renderMockList() 未重新显示 mock-list 容器 v4

P1 — 用户体验

# 问题 首次出现 根因 修复版本
P1-1 水平测试选完不能返回重新选择 v2 缺少重置 UI 的机制 v4
P1-2 考试字体太小,页面空白多 v3 CSS 字体 14-15px,卡片 padding 过大 v4
P1-3 启动方式混乱(python/node/http-server) v2 文档同时列出多种启动方式 v4
P1-4 首页标题文案不准确 v3 "零版权风险"未体现"考纲驱动"核心 v4
P1-5 CCNP 模拟考只有 31 题 v3 出题脚本未按官方权重配比 v4

P2 — 代码质量

# 问题 首次出现 根因 修复版本
P2-1 项目根目录堆积构建脚本和中间文件 v3 边开发边打补丁,未清理 v4
P2-2 缓存爆破用 Date.now() v3 同一页面内多次请求时间戳可能相同 v4
P2-3 JS 模块初始化顺序依赖隐式全局 v2 script 标签顺序即依赖顺序 v4
P2-4 多个 page-change 事件监听器 v3 app.js 末尾添加了第二个 listener v4

3. 关键修复详解

3.1 缓存问题(P0-1, P0-2, P0-3, P0-4)

现象

Chrome: CCNA 模拟考 60 题 ✓    CCNP 水平测试 ✓
Edge:   CCNA 模拟考 60 题 ✓    CCNP 水平测试 ✗(待推出)

反过来:
Chrome: CCNA 模拟考 32 题 ✗    CCNP 水平测试 ✓
Edge:   CCNA 模拟考 60 题 ✓    CCNP 水平测试 ✗

根因分析

浏览器 A                 浏览器 B
     │                       │
     ├─ 首次请求             ├─ 首次请求(时间晚一些)
     │  GET ccna-mock-1.json │  GET ccna-mock-1.json
     │  ← 200 (32题版本)     │  ← 200 (60题版本,已更新)
     │  缓存: max-age=3600   │  缓存: max-age=3600
     │                       │
     ├─ 后续请求(1小时内)   ├─ 后续请求(1小时内)
     │  读缓存 → 32题        │  读缓存 → 60题
     │  ✗ 显示错误           │  ✓ 显示正确

关键发现:不同浏览器在不同时间打开页面,从 http-server 获取了不同版本的文件,并被各自缓存。服务器端的 Cache-Control: max-age=3600 让浏览器在 1 小时内不重新请求。

同时,旧版 http-server 进程未被杀死,与新版 server.js 同时监听 8080 端口。部分请求打到旧服务器(带缓存),部分打到新服务器(不带缓存)。

修复方案(三层防御)

第一层 — 服务器(server.js):
  Cache-Control: no-store, no-cache, must-revalidate, proxy-revalidate
  Pragma: no-cache
  Expires: 0

第二层 — HTML 页面:
  <meta http-equiv="Cache-Control" content="no-store">
  <meta http-equiv="Pragma" content="no-cache">
  <meta http-equiv="Expires" content="0">

第三层 — fetch 请求:
  fetch(url + '?v=' + APP_VER)
  APP_VER 硬编码在 app.js 顶部,版本变更时所有 URL 自动刷新

验证方式

curl -sI http://localhost:8080/any-file | grep -i "cache"
# 输出: Cache-Control: no-store, no-cache, must-revalidate

3.2 题目数量不匹配(P0-1, P1-5)

现象

CCNA 模拟考: 文件显示 60 题,浏览器显示 32 题(缓存导致)
CCNP 模拟考: 31 题,未按官方考纲配比

官方考纲数据

CCNA 200-301 v1.1:

知识域 权重 配题
Network Fundamentals 20% 12
Network Access 20% 12
IP Connectivity 25% 15
IP Services 10% 6
Security Fundamentals 15% 9
Automation 10% 6
合计 100% 60

CCNP ENCOR 350-401 v1.2:

知识域 权重 配题
Architecture 15% 9
Virtualization 10% 6
Infrastructure 30% 18
Network Assurance 10% 6
Security 20% 12
Automation & AI 15% 9
合计 100% 60

数据来源

  • Cisco 官网 Exam Topics 页面
  • Cisco Live BRKCRT-2008 会议资料
  • Cisco Press 官方认证指南

3.3 模拟考返回空白(P0-5)

现象

1. 进入模拟考试列表 → 看到 CCNA / CCNP 两个选项 ✓
2. 点击 CCNA → 进入考试页面 ✓
3. 点击浏览器返回 → 页面空白 ✗
4. 刷新 → 恢复正常 ✓

根因

loadExam()mock-list 设置为 display: none。导航回模拟考页面时触发 page-change → 调用 renderMockList()。该方法重新渲染列表内容,但没有将 mock-list 重新设置回 display: block

loadExam():
  mock-list.style.display = 'none'     ← 隐藏列表
  mock-exam.style.display = 'block'    ← 显示考试

renderMockList():
  mock-list.innerHTML = '...'          ← 重新渲染
  mock-exam.style.display = 'none'     ← 隐藏考试
  // 漏了: mock-list.style.display = 'block'  ← 没有重新显示!

修复

function renderMockList() {
  // ... 渲染代码 ...
  document.getElementById('mock-exam').style.display = 'none';
  document.getElementById('mock-list').style.display = 'block';  // ← 加这一行
}

同时新增「⬅ 返回试卷列表」按钮和 backMock() 函数。


3.4 水平测试不能返回(P1-1)

现象

选择 CCNA 或 CCNP 进入答题后,没有按钮可以回到选择界面。

修复

// 页面切换时自动重置
window.addEventListener('page-change', (e) => {
  if (e.detail.page === 'page-level-test') resetLT();
});

function resetLT() {
  document.getElementById('lt-setup').style.display = 'block';
  document.getElementById('lt-exam').style.display = 'none';
  engine = null;
}

同时在 HTML 中添加「⬅ 重新选择」按钮。


3.5 字体和布局(P1-2)

原始值 vs 优化值

元素 原始 优化后
题干字体 15px 17px
选项字体 14px 16px
选项内边距 10px 14px 14px 18px
选项行高 1.6 1.5
题干 margin-bottom 16px 20px

4. 经验教训

4.1 服务器选择

❌ npx http-server        → 缓存不可控,多进程冲突
❌ python -m http.server  → 非 Windows 默认,显式退出
✅ node server.js         → 零依赖,缓存精确控制,唯一进程

4.2 缓存策略

❌ 依赖服务器默认缓存行为
❌ Date.now() 做缓存爆破(同页多次请求可能相同)
✅ Cache-Control: no-store(服务器端)
✅ HTML meta 头防缓存
✅ fetch URL 带版本号参数

4.3 状态管理

❌ 不同页面间共享全局变量,没有重置机制
✅ 每次 page-change 事件触发时重置页面状态
✅ 每个页面有独立的 reset 函数

4.4 浏览器兼容性

❌ 只在 Chrome 测试,认为 Edge/Firefox 会自动一致
✅ 关键行为需在 Chrome + Edge + Firefox 各验证一次
✅ 服务器端缓存头是所有浏览器统一遵循的标准

4.5 移动端支持(重大优化)

问题

项目初期只在 PC 端 Chrome 上测试,未考虑手机端使用场景。
实际上,考生在通勤、午休时的碎片化刷题需求非常强烈。

解决方案

支持同一 WiFi 下手机访问,具体步骤:

1. 电脑启动 node server.js(监听 0.0.0.0:8080)
2. 手机连接同一 WiFi
3. 获取电脑局域网 IP(ipconfig 查 192.168.x.x)
4. 手机浏览器访问 http://192.168.x.x:8080

关键技术点

服务器绑定 0.0.0.0(默认行为)→ 局域网内所有设备可访问
无需部署到公网服务器
无需 HTTPS(局域网内足够安全)
数据仍存在各自设备的 localStorage 中

文档产出

新增 使用教程.md

  • PC 端完整使用指南
  • 手机端连接教程(含防火墙设置)
  • 功能详解(水平测试、模拟考、错题本、学习计划)
  • 数据管理说明
  • 常见问题(FAQ)
  • 命令速查表

4.6 考试规格验证

❌ 凭印象写题目数量
✅ 从官方文档核实考试时长、题量、域名、权重
✅ 数据来源明确记录在日志中

5. 后续维护指南

启动项目

# 唯一方式
node server.js

# 浏览器打开
http://localhost:8080

新增题库

# 1. 用 AI 生成 JSON 题库
# 2. 放入 questions/generated/
# 3. 更新 questions/index.json 元数据
# 4. 验证
node scripts/build-all-questions.js
# 5. 重新加载页面即可(无缓存,立刻生效)

更新版本号

修改 app.js 顶部的 APP_VER 常量,所有 ?v= 参数自动刷新。

跨浏览器验证清单

每次修改后在以下浏览器测试:

□ Chrome 最新版
□ Edge 最新版
□ Firefox 最新版

检查项:
  □ 水平测试可选 CCNA / CCNP 并答题
  □ 模拟考 60 题完整显示
  □ 错题本收录 / 筛选 / 重练
  □ 导入 / 导出数据
  □ 深色 / 浅色主题切换
  □ 移动端 375px 宽度布局

附录

文件结构(v4 最终版)

NetLearner/
├── server.js                 # 服务器(唯一入口)
├── index.html                # SPA 主页面
├── assets/
│   ├── css/main.css          # 设计系统
│   └── js/
│       ├── app.js            # 应用主逻辑(版本号在此定义)
│       ├── router.js         # 哈希路由
│       ├── storage.js        # 存储层
│       ├── exam-engine.js    # 考试状态机
│       ├── wrong-answer-service.js  # 错题本
│       └── planner-generator.js     # 学习计划
├── questions/
│   ├── index.json            # 题库元数据
│   └── generated/            # 4 个题库文件
│       ├── ccna-level-demo.json
│       ├── ccna-mock-1.json
│       ├── ccnp-encor-level-demo.json
│       └── ccnp-encor-mock-1.json
├── scripts/
│   └── build-all-questions.js  # 题库生成脚本
├── plugin-forge/               # 发布用 skill
├── 项目说明.md
├── 项目计划书.md
└── 项目开发日志.md

题库规格速查

文件 目标 题量 时长 大纲版本
ccna-level-demo.json CCNA 10 200301 v1.1
ccna-mock-1.json CCNA 60 120min 200301 v1.1
ccnp-encor-level-demo.json CCNP ENCOR 10 350401 v1.2
ccnp-encor-mock-1.json CCNP ENCOR 60 120min 350401 v1.2

c## v5.0 - 2026-05-26 双厂商架构 / 大池加权 / 多题型引擎

版本里程碑

从 v4.0 的 Cisco 单厂商升级为 Cisco+Huawei 双厂商。核心从固定试卷升级为大池加权随机。

新特性

1. Huawei 认证全系接入 HCIA-Datacom H12-811 V2.0 (8域大池加权) / HCIP-Datacom H12-821 V1.0 (13域)

2. 多题型引擎 single(单选) / multiple(多选) / fill(填空) / drag(拖拽) 四种评分逻辑

3. 域加权随机抽题系统 ExamEngine.pickFromPool() 按官方配比从各域加权抽取

4. 厂商筛选 + 双品牌主题 模考页: [全部] [Cisco] [Huawei] 三级筛选 / Cisco蓝 #4fc3f7 / 华为红 #e53935

5. 跨厂商共享概念池 questions/shared-concepts.json - 一个概念生成Cisco英文+Huawei双版本。已收录21个共享概念。

题库清单 v5.0

概念 变体 题型
CCNA 543 543 single 6
CCNP ENCOR 67 67 single+multi+drag+fill 6
HCIA V2.0 119 229 single+variants 8
HCIP H12-821 107 207 single+multi+drag+fill 13

CCNA池: 9套模考去重524题+共享概念19题=543题

新增文件

tools/question-importer.html / tools/build-pools.js / questions/shared-concepts.json / questions/generated/ccna-pool.json / ccnp-pool.json / hcia-pool.json / hcip-pool.json / hcia-level-demo.json / hcip-level-demo.json


v5.0 Patch 1 — 水平测试重构 · 考试命名规范化

水平测试全面大池化

废除所有水平测试的固定 10 题模式,全部改为从对应大池加权随机抽取:

考试 原题量 现题量 覆盖域 模式
CCNA (200-301) 10 题 15 题 6 域加权 从 543 池随机
CCNP ENCOR (350-401) 10 题 20 题 6 域加权 从 67 池随机
HCIA-Datacom (H12-811) 10 题 15 题 8 域加权 从 119 池随机
HCIP-Datacom (H12-821) 10 题 20 题 13 域加权 从 107 池随机

考量: 10 题对 HCIP 的 13 个域完全不够,每个域分不到 1 题。CCNP/HCIP 提至 20 题,每域至少 1-2 题,测出来才有参考价值。CCNA/HCIA 入门级提至 15 题,适中。

考试命名规范化

所有界面使用官方考试代码,与 Cisco/Huawei 官方命名一致:

旧: CCNA 模拟考试         → 新: CCNA (200-301) 模拟考试
旧: CCNP ENCOR 水平测试   → 新: CCNP ENCOR (350-401) 水平测试
旧: HCIA 水平测试         → 新: HCIA-Datacom (H12-811) 水平测试
旧: HCIP 水平测试         → 新: HCIP-Datacom (H12-821) 水平测试

涉及文件:questions/index.jsonindex.html(标题/描述/关于)、app.js(fallback 元数据)


后续规划

  • CCNP池扩充(当前67概念偏小)
  • HCIP选考H12-831支持
  • 答题卡支持回顾