最近用 AI 做了几个帮助学习的小网页,纯粹是个人兴趣,不打算卖课,也不搞知识付费。如果你也想知道怎么让 AI 帮你快速做出一个能用的学习工具,这篇文章可以完整还原我的思路和过程。
整篇文章会围绕一个核心:不依赖后端、不做复杂架构,用 AI 生成纯前端的单文件网页。每个案例都是可以直接双击打开的 HTML 文件,代码完整可复制,适合学生、教师、技术爱好者参考。你不需要先精通 JavaScript,只要能描述清楚需求,再跟着文章把代码跑起来就行。
1. 为什么用 AI 做学习小网页
1.1 这类网页解决了什么问题
学习过程中的很多需求,其实都很轻量。比如背单词需要闪卡,复习需要随机测验,做计划需要拆解任务。这些需求完全可以用一个简单网页实现,不需要安装 App,不需要注册账号,也不需要复杂的服务器。
传统写法的痛点在于:大多数人并不熟悉前端开发。即使熟悉,为一个几十行的功能去搭一套工程化项目,也是杀鸡用牛刀。AI 辅助开发的价值就在这里:你负责描述问题和验收结果,AI 负责把页面结构、样式、交互逻辑一次生成出来。
我做这类网页时,通常只用一个原则:能用单文件 HTML 完成的,绝不拆项目。这样维护成本低,文件也好分享,发到网盘、聊天窗口、或者群里,别人下载下来就能用。
1.2 为什么选择纯前端单文件
纯前端单文件的好处很明显:
- 不需要安装依赖,浏览器直接打开。
- 不依赖后端接口,数据放在页面内或本地存储里。
- 不会因为服务器到期、接口变更而失效。
- 代码量可控,方便阅读和修改。
对于学习工具来说,这个方案足够稳定。你背单词、做测验、生成计划,这些功能对实时数据同步没有要求。本地保存足够满足需求。
2. 环境准备与 AI 工具选择
2.1 开发环境
这类学习网页对环境要求非常低。我的开发环境如下:
- 操作系统:Windows / macOS / Linux 均可
- 浏览器:任意现代浏览器,推荐 Chrome 或 Edge
- 编辑器:VS Code,普通文本编辑器也可以
- 本地运行方式:双击 HTML 文件,或者在目录下执行:
python -m http.server 8080然后浏览器访问http://localhost:8080查看效果。
如果你不清楚版本对应关系,不需要担心。文章中用的都是 HTML5、CSS3、原生 JavaScript,没有任何第三方框架。版本需要根据你的项目实际情况调整,本文示例以常见环境为例,重点演示配置思路和实现方法。
2.2 AI 辅助工具的选型
目前市面上的主流 AI 对话工具,例如 DeepSeek、豆包、元宝、Kimi、通义千问等,都能胜任“生成学习网页”这种任务。选择哪一款完全看个人使用习惯。
我更建议选择支持联网检索且可以免费使用的工具,方便在生成代码时补充一些最新语法知识。不过对本文的案例而言,这些 AI 工具的基础文本生成能力已经足够,不需要额外配置任何插件或 API Key。
如果你想把 AI 能力集成到自己的项目里,就应该去查阅对应平台的开放平台文档,申请 API Key,并处理好费用和权限控制。本文不展开讲集成问题,只讲如何用对话式 AI 辅助开发。
2.3 把需求描述清楚的 Prompt 方法
AI 能不能一次生成符合预期的网页,很大程度上取决于 Prompt 是否清晰。我常用的模板是:
请帮我生成一个完整的 HTML 文件,功能如下: 1. 页面用途:... 2. 用户操作方式:... 3. 页面展示内容:... 4. 交互逻辑:... 5. 数据存储方式:本地存储 / 不存储 6. 样式要求:简洁、居中、适合手机和电脑 请使用 HTML + CSS + JavaScript,所有代码放在同一个 HTML 文件中。越具体的描述,生成结果越接近预期。比如你说“做一个背单词工具”,AI 只能自由发挥;但如果你说“做一个单词闪卡页面,正面显示英文单词,点击卡片翻面显示中文释义,下方有认识和不认识两个按钮,点击后自动切换下一张卡片”,效果就会好很多。
这就是用 AI 做学习网页的核心思路:把模糊想法拆成清晰需求,再让 AI 生成代码,最后自己运行验证并迭代。
3. 核心思路:从需求到网页的实现路径
3.1 拆解需求
每次动手之前,先回答三个问题:
| 问题 | 示例 |
|---|---|
| 用户是谁? | 学生,正在背英语单词 |
| 核心操作是什么? | 看英文,想中文,再点击卡片验证 |
| 结果如何展示? | 标记认识/不认识,到最后显示掌握率 |
把这些信息写下来,就可以转成 Prompt 给 AI。
3.2 生成与验证
AI 生成代码之后,不要只是“看看”。强烈建议立刻把代码保存为.html文件,然后在浏览器里打开。
首次运行经常会有小问题,例如按钮点击无效、样式错位、逻辑判断写反。这时候直接把浏览器控制台里的报错复制给 AI,让它根据报错信息修复。这一步就是“验证闭环”。
代码运行后点击按钮没有反应,控制台报错:Uncaught TypeError: Cannot read properties of null 请修复。AI 通常能顺着报错快速定位问题。
3.3 迭代优化
核心功能跑通之后,再优化细节。迭代方向一般有这几类:
- 视觉优化:调整颜色、字体、间距,让页面更舒服。
- 交互反馈:点击后增加动画、音效或文字提示。
- 数据持久化:用 localStorage 保存学习记录,刷新页面不丢失。
- 扩展内容:增加导入导出词库、切换学习模式等功能。
每一轮迭代都可以继续用 AI 完成。我把这种工作方式总结为:AI 负责实现,你负责验收和确定方向。
4. 完整实战:三个学习小网页
下面分享我实际做出来的三个小页面。它们之间没有依赖关系,你可以单独复制运行。
4.1 单词闪卡记忆页
第一个页面解决背单词的场景:卡片正面显示英文单词,点击卡片翻面显示中文释义。底部两个按钮分别标记“认识”和“不认识”,切换下一张卡片,最后统计本次学习掌握率。
Prompt 示例:
生成一个单词闪卡记忆页面,功能如下: 1. 页面居中显示一张卡片,卡片正面显示英文单词,背面显示中文释义。 2. 点击卡片后,卡片翻转显示背面。 3. 卡片下方有“认识”和“不认识”两个按钮。 4. 点击按钮后,自动切换到下一个单词。 5. 统计本轮学习正确率,并在全部单词结束后显示。 6. 内置一个简单的单词数组,至少包含 10 个单词。 7. 页面样式简洁、配色柔和,适合长时间学习使用。 用单个 HTML 文件实现。完整代码:
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>单词闪卡记忆页</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: "PingFang SC", "Microsoft YaHei", sans-serif; background: #f0f4f8; display: flex; justify-content: center; align-items: center; min-height: 100vh; } .container { width: 90%; max-width: 480px; text-align: center; } h1 { font-size: 24px; color: #333; margin-bottom: 20px; } .card-wrapper { perspective: 1000px; margin-bottom: 24px; cursor: pointer; } .card { position: relative; width: 100%; height: 260px; transform-style: preserve-3d; transition: transform 0.5s; } .card.flipped { transform: rotateY(180deg); } .card-face { position: absolute; width: 100%; height: 100%; backface-visibility: hidden; display: flex; justify-content: center; align-items: center; border-radius: 16px; box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1); font-size: 30px; font-weight: 600; } .card-front { background: #ffffff; color: #1e293b; } .card-back { background: #2563eb; color: #ffffff; transform: rotateY(180deg); } .buttons { display: flex; gap: 16px; justify-content: center; margin-bottom: 20px; } .btn { border: none; padding: 12px 24px; border-radius: 10px; font-size: 16px; color: #fff; cursor: pointer; transition: opacity 0.2s; } .btn-know { background: #16a34a; } .btn-unknown { background: #dc2626; } .btn:hover { opacity: 0.85; } .progress { color: #64748b; font-size: 14px; } .result { display: none; background: #ffffff; border-radius: 16px; padding: 32px 20px; box-shadow: 0 10px 20px rgba(0, 0, 0, 0.06); } .result h2 { font-size: 20px; color: #333; margin-bottom: 12px; } .result p { color: #64748b; font-size: 16px; } .btn-reset { margin-top: 20px; background: #2563eb; } </style> </head> <body> <div class="container"> <h1>单词闪卡</h1> <div class="card-wrapper" id="cardWrapper"> <div class="card" id="card"> <div class="card-face card-front" id="frontText">apple</div> <div class="card-face card-back" id="backText">苹果</div> </div> </div> <div class="buttons"> <button class="btn btn-unknown" id="unknownBtn">不认识</button> <button class="btn btn-know" id="knowBtn">认识</button> </div> <div class="progress" id="progressText">1 / 10</div> <div class="result" id="resultBox"> <h2>本次学习完成</h2> <p id="resultText">正确率:0%</p> <button class="btn btn-reset" id="resetBtn">再学一轮</button> </div> </div> <script> const wordList = [ { en: "apple", zh: "苹果" }, { en: "book", zh: "书本" }, { en: "computer", zh: "计算机" }, { en: "dog", zh: "狗" }, { en: "education", zh: "教育" }, { en: "family", zh: "家庭" }, { en: "garden", zh: "花园" }, { en: "health", zh: "健康" }, { en: "internet", zh: "互联网" }, { en: "journey", zh: "旅行" } ]; let currentIndex = 0; let knownCount = 0; const cardWrapper = document.getElementById('cardWrapper'); const card = document.getElementById('card'); const frontText = document.getElementById('frontText'); const backText = document.getElementById('backText'); const knowBtn = document.getElementById('knowBtn'); const unknownBtn = document.getElementById('unknownBtn'); const progressText = document.getElementById('progressText'); const resultBox = document.getElementById('resultBox'); const resultText = document.getElementById('resultText'); const resetBtn = document.getElementById('resetBtn'); function loadWord() { const word = wordList[currentIndex]; frontText.textContent = word.en; backText.textContent = word.zh; card.classList.remove('flipped'); progressText.textContent = (currentIndex + 1) + ' / ' + wordList.length; } function finishStudy() { document.querySelector('.card-wrapper').style.display = 'none'; document.querySelector('.buttons').style.display = 'none'; progressText.style.display = 'none'; resultBox.style.display = 'block'; const rate = Math.round((knownCount / wordList.length) * 100); resultText.textContent = '正确率:' + rate + '%'; } cardWrapper.addEventListener('click', function () { card.classList.toggle('flipped'); }); knowBtn.addEventListener('click', function () { knownCount++; currentIndex++; if (currentIndex >= wordList.length) { finishStudy(); return; } loadWord(); }); unknownBtn.addEventListener('click', function () { currentIndex++; if (currentIndex >= wordList.length) { finishStudy(); return; } loadWord(); }); resetBtn.addEventListener('click', function () { currentIndex = 0; knownCount = 0; document.querySelector('.card-wrapper').style.display = 'block'; document.querySelector('.buttons').style.display = 'flex'; progressText.style.display = 'block'; resultBox.style.display = 'none'; loadWord(); }); loadWord(); </script> </body> </html>运行后,点击卡片区域即可翻面。按钮切换单词,全部完成后显示正确率。
这个页面主要解释几个关键点:
backface-visibility: hidden和transform: rotateY(180deg)实现了翻卡片的 3D 效果。- JavaScript 维护
currentIndex和knownCount两个状态,控制展示顺序和最终统计。 - “认识”按钮会累加正确数,“不认识”按钮只切换下一张,不计入正确数。
后续可以让 AI 增加词库导入功能,比如从文本框中粘贴单词列表,或者接入本地存储。
4.2 随机测验工具
第二个页面用于日常测验。我从题库中随机抽题,用户选择答案后立刻判断对错,并记录总分。
Prompt 示例:
生成一个随机测验页面,功能如下: 1. 内置一个 JavaScript 数组作为选择题题库,每道题包含题目、四个选项、正确答案。 2. 每次从题库中随机抽取 5 道题。 3. 用户点击选项后,高亮显示正确或错误。 4. 答完所有题目后,显示得分和正确率。 5. 提供“重新测验”按钮。 6. 页面设计干净,适合学习使用。 用单个 HTML 文件实现。完整代码:
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>随机测验工具</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: "PingFang SC", "Microsoft YaHei", sans-serif; background: #f8fafc; display: flex; justify-content: center; align-items: center; min-height: 100vh; } .container { width: 90%; max-width: 560px; background: #fff; border-radius: 16px; box-shadow: 0 10px 20px rgba(0, 0, 0, 0.08); padding: 28px; } h1 { font-size: 22px; color: #1e293b; margin-bottom: 16px; } .question { font-size: 18px; color: #334155; margin-bottom: 20px; line-height: 1.6; } .option-list { list-style: none; margin-bottom: 20px; } .option-item { border: 2px solid #e2e8f0; border-radius: 10px; padding: 12px 16px; margin-bottom: 10px; cursor: pointer; transition: all 0.2s; font-size: 15px; color: #334155; } .option-item:hover { border-color: #2563eb; background: #eff6ff; } .option-item.correct { border-color: #16a34a; background: #f0fdf4; color: #166534; } .option-item.wrong { border-color: #dc2626; background: #fef2f2; color: #991b1b; } .info-bar { display: flex; justify-content: space-between; align-items: center; color: #64748b; font-size: 14px; margin-bottom: 16px; } .btn { border: none; background: #2563eb; color: #fff; padding: 10px 20px; border-radius: 8px; font-size: 14px; cursor: pointer; } .btn:hover { opacity: 0.9; } .result { display: none; text-align: center; padding: 24px 0; } .result h2 { font-size: 24px; color: #1e293b; } .result p { font-size: 16px; color: #64748b; margin: 12px 0 20px; } </style> </head> <body> <div class="container"> <h1>随机测验</h1> <div class="info-bar"> <span id="questionNum">第 1 题 / 共 5 题</span> <span id="scoreText">得分:0</span> </div> <div class="question" id="question"></div> <ul class="option-list" id="optionList"></ul> <button class="btn" id="nextBtn" style="display: none;">下一题</button> <div class="result" id="resultBox"> <h2>测验完成</h2> <p id="resultText"></p> <button class="btn" id="resetBtn">重新测验</button> </div> </div> <script> const quizBank = [ { question: "下列哪个不是编程语言?", options: ["Python", "JavaScript", "HTML", "C++"], answer: 2 }, { question: "HTTP 状态码 404 表示什么?", options: ["服务不可用", "找到页面", "授权失败", "页面不存在"], answer: 3 }, { question: "JavaScript 中,哪个关键字用于声明常量?", options: ["var", "let", "const", "static"], answer: 2 }, { question: "CSS 中,哪个属性用于改变文字颜色?", options: ["font-size", "text-color", "color", "background"], answer: 2 }, { question: "下列哪种数据结构适合保存键值对?", options: ["数组", "对象", "链表", "栈"], answer: 1 } ]; let selectedQuestions = []; let currentIndex = 0; let score = 0; let answered = false; const questionEl = document.getElementById('question'); const optionListEl = document.getElementById('optionList'); const questionNumEl = document.getElementById('questionNum'); const scoreTextEl = document.getElementById('scoreText'); const nextBtn = document.getElementById('nextBtn'); const resultBox = document.getElementById('resultBox'); const resultText = document.getElementById('resultText'); const resetBtn = document.getElementById('resetBtn'); function shuffle(arr) { const copy = [...arr]; for (let i = copy.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [copy[i], copy[j]] = [copy[j], copy[i]]; } return copy; } function startQuiz() { selectedQuestions = shuffle(quizBank).slice(0, 5); currentIndex = 0; score = 0; answered = false; resultBox.style.display = 'none'; nextBtn.style.display = 'none'; document.querySelector('.info-bar').style.display = 'flex'; document.querySelector('.question').style.display = 'block'; optionListEl.style.display = 'block'; renderQuestion(); } function renderQuestion() { const q = selectedQuestions[currentIndex]; questionEl.textContent = q.question; optionListEl.innerHTML = ''; questionNumEl.textContent = '第 ' + (currentIndex + 1) + ' 题 / 共 ' + selectedQuestions.length + ' 题'; scoreTextEl.textContent = '得分:' + score; answered = false; nextBtn.style.display = 'none'; q.options.forEach(function (text, index) { const li = document.createElement('li'); li.className = 'option-item'; li.textContent = text; li.addEventListener('click', function () { if (answered) { return; } answered = true; const options = optionListEl.children; Array.from(options).forEach(function (item, i) { item.classList.remove('correct', 'wrong'); if (i === q.answer) { item.classList.add('correct'); } else if (i === index) { item.classList.add('wrong'); } }); if (index === q.answer) { score++; } scoreTextEl.textContent = '得分:' + score; if (currentIndex < selectedQuestions.length - 1) { nextBtn.style.display = 'inline-block'; } else { setTimeout(showResult, 600); } }); optionListEl.appendChild(li); }); } function showResult() { document.querySelector('.info-bar').style.display = 'none'; document.querySelector('.question').style.display = 'none'; optionListEl.style.display = 'none'; nextBtn.style.display = 'none'; resultBox.style.display = 'block'; const rate = Math.round((score / selectedQuestions.length) * 100); resultText.textContent = '答对 ' + score + ' 题,正确率 ' + rate + '%'; } nextBtn.addEventListener('click', function () { currentIndex++; renderQuestion(); }); resetBtn.addEventListener('click', function () { startQuiz(); }); startQuiz(); </script> </body> </html>在这个页面里,AI 帮我处理了很多细节:
shuffle函数随机打乱题库,避免每次测验顺序一致。- 选项点击后会先高亮正确答案,再把用户选错的项标红,这种反馈比只提示“对或错”更直观。
- 所有状态用
answered标记管理,防止重复点击同一道题计分。
如果你想换一门科目,只需要改动quizBank数组中的题目内容即可。
4.3 学习计划生成器
第三个页面用来拆解学习计划。用户输入学习主题、每天可用时长、总天数,页面自动按天生成任务清单。
Prompt 示例:
生成一个学习计划生成器页面,包含以下功能: 1. 输入学习主题、每天可用学习时间(小时)、学习总天数。 2. 点击生成按钮后,按照时间拆分学习阶段,输出每一天的学习计划。 3. 计划采用列表展示,并标记第几天。 4. 页面不依赖后端。 5. 样式清晰,适合移动端阅读。 用单个 HTML 文件实现。完整代码:
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>学习计划生成器</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: "PingFang SC", "Microsoft YaHei", sans-serif; background: #f1f5f9; display: flex; justify-content: center; align-items: center; min-height: 100vh; padding: 20px; } .container { width: 100%; max-width: 640px; background: #ffffff; border-radius: 16px; box-shadow: 0 10px 20px rgba(0, 0, 0, 0.08); padding: 28px; } h1 { font-size: 22px; color: #1e293b; margin-bottom: 20px; } .form-item { margin-bottom: 16px; } label { display: block; font-size: 14px; color: #475569; margin-bottom: 6px; } input[type="text"], input[type="number"] { width: 100%; padding: 10px 12px; border: 2px solid #e2e8f0; border-radius: 8px; font-size: 14px; outline: none; transition: border-color 0.2s; } input:focus { border-color: #2563eb; } .btn { border: none; background: #2563eb; color: #fff; padding: 12px 24px; border-radius: 8px; font-size: 15px; cursor: pointer; width: 100%; transition: opacity 0.2s; } .btn:hover { opacity: 0.9; } .plan-list { margin-top: 24px; border-top: 1px solid #e2e8f0; padding-top: 16px; } .plan-day { background: #f8fafc; border-radius: 10px; padding: 14px 16px; margin-bottom: 12px; } .plan-day h3 { font-size: 15px; color: #2563eb; margin-bottom: 6px; } .plan-day p { font-size: 14px; color: #475569; line-height: 1.6; } .tips { font-size: 13px; color: #94a3b8; margin-top: 16px; } </style> </head> <body> <div class="container"> <h1>学习计划生成器</h1> <div class="form-item"> <label for="topic">学习主题</label> <input type="text" id="topic" placeholder="例如:Python 基础语法" /> </div> <div class="form-item"> <label for="hours">每天可用学习时间(小时)</label> <input type="number" id="hours" min="0.5" max="12" step="0.5" value="2" /> </div> <div class="form-item"> <label for="days">学习总天数</label> <input type="number" id="days" min="1" max="90" value="7" /> </div> <button class="btn" id="generateBtn">生成学习计划</button> <div class="plan-list" id="planList"></div> <div class="tips">提示:每天先复习前一天内容,再学习新内容,效果会更好。</div> </div> <script> document.getElementById('generateBtn').addEventListener('click', function () { const topic = document.getElementById('topic').value.trim(); const hours = parseFloat(document.getElementById('hours').value); const days = parseInt(document.getElementById('days').value, 10); if (!topic) { alert('请输入学习主题'); return; } if (isNaN(hours) || hours <= 0) { alert('请填写有效的每日学习时长'); return; } if (isNaN(days) || days <= 0) { alert('请填写有效的学习天数'); return; } const planList = document.getElementById('planList'); planList.innerHTML = ''; const totalStudyHours = hours * days; const basicHours = hours * 0.6; const reviewHours = hours * 0.2; const practiceHours = hours * 0.2; for (let i = 1; i <= days; i++) { const div = document.createElement('div'); div.className = 'plan-day'; const title = document.createElement('h3'); title.textContent = '第 ' + i + ' 天'; const content = document.createElement('p'); if (i === 1) { content.textContent = '主题:学习 ' + topic + ' 的入门概念。' + '预计 ' + basicHours.toFixed(1) + ' 小时用于理解新知识,' + practiceHours.toFixed(1) + ' 小时用于做练习和整理笔记。'; } else { content.textContent = '主题:推进 ' + topic + ' 的学习进度。' + '先用 ' + reviewHours.toFixed(1) + ' 小时复习前一天内容,' + '再用 ' + basicHours.toFixed(1) + ' 小时学习新知识,' + '最后用 ' + practiceHours.toFixed(1) + ' 小时完成实践任务。'; } div.appendChild(title); div.appendChild(content); planList.appendChild(div); } }); </script> </body> </html>这个页面比较简单,但很实用。它展示了一个重要思路:AI 生成的页面未必复杂,关键是它能按照你的输入参数实时生成内容。
这里的计划拆分逻辑是:
- 第一天:以理解概念为主。
- 后续每天:复习旧知识、学习新知识、实践练习三部分。
- 时间占比分别设为 60%、20%、20%。
如果你有更好的学习方法和时间分配策略,可以让 AI 修改basicHours、reviewHours、practiceHours的数值,或者增加单独的休息日逻辑。
4.4 如何把学习网页分享出去
这几个网页都是单文件 HTML,分享方式非常灵活:
- 直接发送
.html文件,别人用浏览器打开即可。 - 将文件放到网盘或聊天工具,适合团队内部共享。
- 部署到静态托管平台,例如 GitHub Pages、Gitee Pages、云开发静态托管等。
静态部署时需要记住:如果页面里有自定义域名、回调地址等配置,需要按照托管平台的规则修改。对本地学习工具来说,直接分享文件是最简单的方式。
5. 常见问题与排查思路
AI 生成网页时,经常遇到几个问题。我把自己的排查思路整理成表格:
| 问题现象 | 常见原因 | 解决思路 |
|---|---|---|
| 中文显示乱码 | HTML 缺少<meta charset="UTF-8"> | 检查 head 区域是否声明 UTF-8 编码 |
| 点击按钮没有反应 | JavaScript 报错或事件绑定失败 | 打开浏览器控制台,把报错信息发给 AI 修复 |
| 页面样式全没了 | CSS 选择器写错或类名不一致 | 检查class和 JS 中querySelector是否一致 |
| 刷新后学习记录丢失 | 没有使用 localStorage | 让 AI 增加本地存储功能 |
| 在手机上排版混乱 | 缺少 viewport 设置 | 确认<meta name="viewport">是否存在 |
| 按钮文字显示了但无法点击 | 元素被其他层遮挡 | 使用开发者工具检查元素的层级关系 |
如果遇到控制台报错,最快的处理方式是复制完整报错信息,然后发给 AI:
页面加载后控制台报错,内容是:xxxxx 请分析原因并给出修复后的完整 HTML 文件代码。大多数情况下,AI 可以直接给出修复结果。你只需要把修复后的代码替换到原文件中,再验证一遍。
6. 最佳实践与工程建议
虽然这些只是学习小网页,但工程上的严谨习惯还是值得保留。
6.1 需求优先级比代码更重要
做工具前,先明确自己要解决什么问题。为了做一个“好看但没用”的页面,会浪费大量时间。我通常先确认:核心操作是什么?用户拿到这个页面后,第一步做什么?只有把这两个问题想明白,后面的开发才会顺畅。
6.2 每次只改一处,保留可回滚版本
AI 生成代码后,不要一次性提出一大堆修改需求。建议每次只提一个改动点,例如“把按钮改成绿色”,验证通过后再继续下一个改动。改动前把当前可用版本复制一份,避免 AI 改挂之后无法回退。
6.3 学习工具可以多利用本地存储
对学习类网页,localStorage 是一个很实用的能力。它可以保存单词学习进度、答题历史、计划内容。善用本地存储,会让工具在关闭浏览器后依然保留用户数据。
下面这段代码是 localStorage 的典型用法:
// 保存数据 localStorage.setItem('wordLevel', '50'); // 读取数据 const level = localStorage.getItem('wordLevel') || '0'; // 删除数据 localStorage.removeItem('wordLevel');当你需要让 AI 增加数据保存功能时,只要在需求里加一句“使用 localStorage 保存进度,刷新页面后恢复”,它就能自动实现。
6.4 注意安全边界
纯前端网页有它的安全边界。不要在页面里存放密码、API Key、个人敏感信息。如果学习工具涉及用户数据收集,一定要明确告知用户数据只保存在本地,不会上传到服务器。涉及认证、权限、队列等生产环境功能时,一定要在测试环境验证,并遵循最小权限原则。
6.5 保持代码可维护性
即使是 AI 生成的代码,也应该保持结构清晰。代码里要有注释、变量命名要可读。我会在需求中要求 AI“给关键代码添加中文注释”,这样以后修改的时候不用重新推理。
7. 总结与继续优化的方向
这篇文章分享了三个完整的学习辅助网页:单词闪卡记忆页、随机测验工具、学习计划生成器。它们覆盖了学习过程中最典型的几个场景:记忆、检测、规划。每个页面都来自 AI 辅助生成,实现成本低,但使用价值很高。
如果你想继续深入,可以从这些方向入手:
- 给单词闪卡增加词库导入导出功能,让它支持任意语言的单词。
- 给测验工具增加按知识点分类的题库,并实现错题记录。
- 给学习计划生成器增加导出为 Markdown 或文本的功能。
- 尝试把多个工具合并成一个导航首页,方便统一使用。
- 学习一下 HTML5 的本地数据库 IndexedDB,处理更大规模的学习数据。
在学习过程中,AI 能帮你省去很多重复工作,但它不能替代你的判断。你要负责确认功能是否符合预期、数据是否正确、内容是否合规。把 AI 当成一个高效的编程伙伴,而不是完全依赖它。
如果你也想做出类似的学习小网页,建议直接复制文章里的代码,先跑通一个,然后从你最常用的学习场景入手,让 AI 帮你做一个真正属于你的小工具。