
在真实开发里做学习工具最麻烦的不是技术而是需求小、周期短、又不想为一个页面引入整套工程。最近用 AI 辅助做了几个帮助学习的小网页比如单词记忆卡、公式速查页、古诗文背诵检查器。这个项目不做任何课程推广也不卖课核心是记录一种工作方式把学习需求拆成清晰条目把 AI 当作结对编程搭档让它先产出可运行页面再由人负责测试、审查和迭代。这篇文章会把这条流程完整展开。适合下面几类读者想用 AI 快速做出实用小工具的开发者、正在学前端但不知道怎么把需求交给 AI 的新手以及想把背单词、背古诗、查公式这类场景做成网页的人。读完以后你可以自己复现出一个可用的学习网页也能学会在 AI 生成代码之后应该从哪些位置检查逻辑、排查问题而不是把 AI 输出当作可直接上线的代码。关键判断先放在前面AI 生成的前端代码能跑通不代表它理解你的学习场景。代码越短、依赖越少、数据越透明你越容易验证它是否真的有效。这也是为什么本文选择了单文件网页作为起点。1. 为什么单文件网页最适合做“学习小工具”1.1 单文件网页解决的是什么问题学习类小网页通常有这些特征内容单一、交互简单、用户只有自己或少数同学、不需要账号体系、数据量小、不强调高并发。用一句话说它更接近“工具”而不是“产品”。对这种场景单文件网页是性价比最高的载体。所谓单文件网页就是所有 HTML、CSS、JavaScript 都写在一个index.html里不依赖构建工具不需要npm install不需要部署后端服务。打开浏览器就能运行也可以直接发给别人。对学习者来说最重要的是立刻看到结果看到一个卡片、点一个按钮、刷新后数据还在。这些反馈越短学习积极性越容易保持。从技术实现来看单文件网页还有几个实际好处。第一没有路径问题图片、样式、脚本都内联不会出现部署后css/main.css找不到的尴尬。第二浏览器可以直接打开适合学习和调试。第三AI 生成这类代码的成功率最高因为上下文短、功能边界清晰、错误通常只集中在几段脚本里。1.2 什么时候该升级到工程化单文件网页不是万能的。如果出现下面这些情况再考虑升级成 Vue、React 这类前端工程功能页面超过 5 个且页面之间存在复杂跳转和状态共享。需要多人协作需要代码分目录、分组件、写单元测试。需要对接后端接口、登录鉴权、实时消息。需要处理大量数据比如几千道题的题库、复杂筛选和统计图表。需要持续迭代维护面向正式发布而不是一次性工具。学习网页通常不会立刻走到这步。更合理的路径是先用单文件网页跑通核心流程等确认学习场景真的被解决再决定是否重构成工程。反之如果一开始就搭脚手架光是构建配置和组件拆分就会消耗掉大部分热情。下面用表格把两种方式放在一起对比对比维度单文件网页前端工程项目启动成本保存即可打开需要安装 Node、建项目、跑构建迭代速度改一处刷新立即生效需要编译、热更新、处理依赖依赖管理尽量不用外部库依赖 npm 包和锁文件状态共享通常靠 localStorage 和全局变量组件状态、路由、状态管理库适用规模1 到 10 个学习页面正式产品、多页面长期项目调试难度浏览器控制台直接看需要看编译报错、运行时报错、网络请求部署方式静态文件直传需要构建后部署通常配合 CI/CD我的建议是学习工具先做单文件跑通后再抽象公共代码。这样既能快速完成又不会因为工程化门槛而放弃一个本来有价值的小工具。2. 准备一套不依赖复杂环境的 AI 网页开发链路2.1 工具与前置条件用 AI 辅助开发网页不需要太重的环境。实际项目中建议准备下面几个工具工具用途是否必须浏览器打开页面、查看控制台、调试 DOM必须代码编辑器查看和修改代码推荐 VS Code强烈建议AI 编程助手生成初稿、解释报错、自动补全必须本地 HTTP 服务验证 localStorage 和部署路径建议Git 与 GitHub 账号保存版本、发布静态页面可选但推荐AI 编程助手的选择比较灵活。可以用 Cursor、GitHub Copilot 这类直接嵌入编辑器的工具也可以用豆包、Kimi、DeepSeek 等网页版对话工具。不同工具对代码的理解能力有差异但对于“生成一个单文件学习网页”这种任务主流的都能胜任。关键是不要把工具当作搜索引擎而是把它当作一个需要明确输入的正则函数你给清晰的需求它才可能给清晰的代码。浏览器建议用 Chrome 或 Edge。不只是为了打开页面更重要的是 F12 开发者工具里的 Console、Network、Application 这三个面板。AI 生成的代码不会一次通过绝大多数问题都需要靠这三个面板定位。本地 HTTP 服务在验证数据持久化时尤其重要。如果直接双击index.html部分浏览器对file://协议下 localStorage 的行为会有差异。更稳妥的方式是在项目目录启动一个静态服务。比如在项目文件夹执行python -m http.server 8000然后浏览器访问http://localhost:8000就能看到页面。这里要说明不同系统下 Python 命令可能是python或python3如果提示命令不存在就换另一个试试或者用 VS Code 的 Live Server 插件。2.2 用 AI 生成初稿之前先把需求拆成一个小卡片很多人让 AI 写网页会直接输入“帮我做一个背单词的网页”。这个提示词太模糊AI 返回的往往是一个“看起来完整但用起来别扭”的页面。真正的做法是把需求拆成具体条目就像提交需求单一样。一份有效的提示词至少包含下面这些要素要素说明示例角色让 AI 知道自己以什么身份产出结果你是一名前端工程师目标页面要解决什么问题帮助用户记忆英文单词页面结构页面包含哪些区块卡片区、按钮区、统计区交互细节用户点哪里、发生什么点击卡片翻面点击认识进入下一张技术约束单文件、不要外部库、存储方案使用 HTML/CSS/JavaScript依赖 localStorage验收标准怎么算完成双击打开能运行刷新后进度保留基于这些要素可以使用下面的提示词作为第一版你是一名前端工程师请帮我生成一个“单词记忆卡”学习网页。 要求 1. 所有代码写在一个 index.html 中内部 CSS 和 JavaScript不要使用外部 CDN。 2. 页面顶部是标题“单词记忆卡”中间是一张卡片卡片上方显示英文单词点击卡片翻面显示中文释义。 3. 卡片下方有三个按钮认识、不认识、下一张。 4. 点击“认识”表示当前单词已掌握点击“不认识”表示还没掌握两个按钮点击后都要自动切换到下一张随机单词。 5. 页面底部显示统计信息总词数、已掌握词数。 6. 使用 localStorage 保存已掌握状态刷新页面后不要丢失。 7. 页面布局简单清晰适配手机屏幕。这个提示词看起来普通但它把功能边界圈得很清楚单文件、不要外部依赖、卡片翻面、随机切换、进度持久化。AI 收到这样的需求后生成的代码会更贴近实际场景而不是只画一个静态页面。2.3 拿到初版代码后的检查流程AI 生成代码后不要急着复制粘贴进项目也不要直接发布。先按下面的流程检查一遍把代码保存为index.html用浏览器打开。页面至少能正常显示标题和卡片。按 F12 打开控制台确认没有红色报错。JavaScript 的报错通常会在 Console 面板中显示比如变量未定义、选择器找不到元素。点一遍所有核心按钮翻转卡片、认识、不认识、下一张。确认每一步都有可见反应。在页面里记住当前进度刷新浏览器确认数据是否还在。这一步验证 localStorage 是否生效。换一台设备或换一个浏览器再打开一次检查是不是只在一个浏览器里能跑通。这里有一个容易忽略的点AI 生成的页面第一版通常只满足“看起来正确”在边界情况下可能有问题。比如单词列表为空时按钮是否还能点击词库里只有两个单词时随机算法是否会让下一张还是同一个。这些都需要人工补测试。注意不要只验证程序能打开还要验证输入、输出、异常分支和日志是否符合预期。学习网页也一样空词库、重复词、特殊字符都可能让页面崩溃。3. 案例用 AI 做一个“单词记忆卡”学习网页3.1 需求拆解卡片、随机、认识、进度为了说明整个流程这里把“单词记忆卡”当成一个最小可运行案例。它包含四个核心模块卡片区当前显示一个英文单词点击卡片翻面显示中文释义。操作区“认识”和“不认识”两个按钮点击后记录当前单词状态并随机切换到下一张。统计区显示总词数和已经掌握的单词数。数据区把词库和进度写入 localStorage刷新后恢复。除了这些功能还要在页面底部留一个自定义词库入口因为内置词库永远无法满足真实学习需求。这个入口可以用一个文本框实现每行按“单词|释义”格式输入点保存后替换当前词库。3.2 完整代码示例第一版下面是一版可以直接运行的示例代码功能覆盖上面四个模块。实际项目可以从这个版本继续迭代文章后面也会说明如何让 AI 继续增加功能。!DOCTYPE html html langzh-CN head meta charsetUTF-8 meta nameviewport contentwidthdevice-width, initial-scale1.0 title单词记忆卡/title style body { font-family: system-ui, -apple-system, sans-serif; margin: 0; padding: 24px; background: #f5f6f8; } .container { max-width: 720px; margin: 0 auto; } h1 { text-align: center; font-size: 24px; } .card { min-height: 240px; background: #fff; border-radius: 12px; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08); display: flex; align-items: center; justify-content: center; font-size: 32px; cursor: pointer; user-select: none; padding: 24px; text-align: center; } .back { display: none; color: #7a4bd8; } .card.flipped .back { display: block; } .card.flipped .front { display: none; } .btn-bar { display: flex; gap: 12px; margin-top: 16px; } button { flex: 1; padding: 12px; font-size: 16px; border: none; border-radius: 8px; cursor: pointer; } .btn-known { background: #4caf50; color: #fff; } .btn-unknown { background: #ef5350; color: #fff; } .btn-next { background: #2196f3; color: #fff; } .stats { margin-top: 12px; color: #666; font-size: 14px; } .import-area { margin-top: 24px; border-top: 1px dashed #ddd; padding-top: 16px; } textarea { width: 100%; margin: 8px 0; font-family: monospace; } /style /head body div classcontainer h1单词记忆卡/h1 div classcard idcard div classfront idfront/div div classback idback/div /div div classbtn-bar button classbtn-known idknownBtn认识/button button classbtn-unknown idunknownBtn不认识/button button classbtn-next idnextBtn下一张/button /div div classstats idstats共 0 个单词已掌握 0 个/div div classimport-area label forwordInput自定义词库导入每行格式单词|释义/label textarea idwordInput rows4 placeholder例如#10;algorithm|算法#10;framework|框架/textarea button idsaveWords保存词库/button /div /div script const DEFAULT_WORDS [ { word: algorithm, meaning: 算法 }, { word: framework, meaning: 框架 }, { word: cache, meaning: 缓存 }, { word: compile, meaning: 编译 }, { word: debug, meaning: 调试 } ]; let words []; let progress {}; let currentIndex 0; function loadData() { try { const saved localStorage.getItem(word-card-data); const parsed saved ? JSON.parse(saved) : null; if (parsed Array.isArray(parsed.words)) { words parsed.words; progress parsed.progress || {}; } else { words DEFAULT_WORDS.slice(); progress {}; } } catch (e) { words DEFAULT_WORDS.slice(); progress {}; } if (words.length 0) { words DEFAULT_WORDS.slice(); } } function saveData() { localStorage.setItem(word-card-data, JSON.stringify({ words, progress })); } function renderCard() { if (words.length 0) { document.getElementById(front).textContent 没有单词; document.getElementById(back).textContent 请先添加词库; return; } const item words[currentIndex % words.length]; document.getElementById(front).textContent item.word; document.getElementById(back).textContent item.meaning; document.getElementById(card).classList.remove(flipped); renderStats(); } function renderStats() { const knownCount Object.keys(progress).filter(function (key) { return progress[key] known; }).length; document.getElementById(stats).textContent 共 words.length 个单词已掌握 knownCount 个; } function nextRandom() { if (words.length 0) return; let next currentIndex; while (next currentIndex words.length 1) { next Math.floor(Math.random() * words.length); } currentIndex next; renderCard(); } document.getElementById(card).addEventListener(click, function () { this.classList.toggle(flipped); }); document.getElementById(knownBtn).addEventListener(click, function () { if (!words.length) return; progress[words[currentIndex].word] known; saveData(); renderStats(); nextRandom(); }); document.getElementById(unknownBtn).addEventListener(click, function () { if (!words.length) return; progress[words[currentIndex].word] unknown; saveData(); renderStats(); nextRandom(); }); document.getElementById(nextBtn).addEventListener(click, nextRandom); document.getElementById(saveWords).addEventListener(click, function () { const text document.getElementById(wordInput).value.trim(); const lines text.split(\n); const newWords []; for (const line of lines) { const parts line.split(|); const word parts[0] ? parts[0].trim() : ; const meaning parts[1] ? parts[1].trim() : ; if (word meaning) { newWords.push({ word: word, meaning: meaning }); } } if (newWords.length 0) { words newWords; progress {}; currentIndex 0; saveData(); renderCard(); document.getElementById(wordInput).value ; } }); loadData(); renderCard(); /script /body /html这个版本把“认识”和“不认识”的状态都记录到了 localStorage但统计只统计“已掌握”这符合第一版需求。后面的迭代可以继续增加“不认识次数”“错误率”等指标。3.3 关键逻辑说明这段代码有几个位置值得细看。DEFAULT_WORDS是内置词库用它保证页面第一次打开时也有内容。第一次打开本地没有数据loadData()会把默认词库拷贝到words。这里用了DEFAULT_WORDS.slice()避免后续修改words时污染默认词库。progress对象保存学习状态key 是单词本身value 是known或unknown。这种结构简单直接但也有限制如果词库里有两个相同单词它们会共享同一个状态。这是后面可以优化的点更稳妥的方案是用自增 id 作为 key。nextRandom()里的 while 循环防止连续两次出现同一个单词。这个逻辑在词库只有 1 个单词时会跳过因为words.length 1条件不满足。这里体现了一个边界处理不能因为随机算法而进入死循环。所有事件绑定都放在脚本末尾因为脚本位于 body 底部此时 DOM 元素已经存在不需要再写DOMContentLoaded监听器。如果把这个脚本移到 head 里就会遇到getElementById返回 null 的问题。3.4 验证功能正常路径与异常路径运行后需要验证的路径包括正常路径第一次打开出现内置词汇卡片显示algorithm。点击卡片卡片翻转显示“算法”。点击“认识”页面切换到下一个随机单词底部“已掌握”数量增加。点击“不认识”切换下一个单词。刷新页面已掌握数量保持不变。在底部文本框输入自定义词库点击“保存词库”卡片切换到新词库。异常路径词库为空时页面提示“没有单词”不会因为按钮点击而报错。localStorage 里的数据格式被破坏时loadData走进 catch 分支使用默认词库页面不会白屏。在file://协议下测试时如果刷新后数据丢失需要改用本地 HTTP 服务验证。4. 让 AI 迭代从能用变成好用4.1 第二个提示词增加自定义词库导入第一版代码虽然已经有导入框但功能比较简陋只能整体替换词库。如果想要更完整的体验比如支持追加词库、合并重复词、导入前预览可以把需求再次交给 AI。实际交互可以这样写提示词在现有单词记忆卡基础上调整自定义词库功能 1. 增加“追加词库”按钮点击后把输入框里的单词追加到当前词库末尾。 2. 如果新单词已存在保留原来的掌握状态不重复插入。 3. 输入框下方增加预览区实时显示将要导入多少条有效数据。 4. 导入完成后显示“本次新增 5 个单词”的提示。这里的关键是一次只让 AI 处理一个功能点。多需求叠加时AI 容易改乱已有逻辑。先追加词库确认没问题再改预览区节奏会更稳。4.2 第三个提示词增加进度统计与重置学习工具只记录“认识/不认识”还不够用户通常想知道自己整体掌握情况。可以继续让 AI 增加统计模块在单词记忆卡页面底部增加学习统计模块 1. 显示总词数、已掌握数、未掌握数、总学习次数。 2. 已掌握指 progress 中状态为 known 的单词数量未掌握指状态为 unknown 的数量。 3. 增加“重置所有进度”按钮点击前弹出确认框确认后清空 localStorage 中的进度恢复默认词库。 4. 所有统计数据在刷新后保持一致。这类功能的实现难度不高但对 AI 来说需要理解已有数据结构。如果你的提示词里没有说明progress是怎么存的AI 可能自己造一个字段导致新代码和旧逻辑对不上。所以迭代式提示词里最好把现有数据结构也带进去。4.3 生成代码后必须人工检查的四个位置AI 迭代代码时最容易在四个位置出错。每次拿到新版本优先看这些地方。第一事件绑定与元素 id 是否匹配。AI 可能在重构时改了 HTML 中的id但脚本里的getElementById还引用旧值。看控制台报错就能发现常见的报错是Cannot read properties of null。第二数据读写是否使用同一个 key。第一版使用word-card-data这个 key后续迭代如果 AI 改成word-card刷新后进度就会丢失。看到新代码时先搜索getItem和setItem确认 key 一致。第三localStorage 写入的数据结构是否兼容旧版本。比如旧数据是{ words: [], progress: {} }新代码如果改成{ wordList: [] }旧数据就无法读取。推荐的兼容方式是像示例代码那样解析失败时就回退到默认词库避免白屏。第四边界条件是否被破坏。重点看空数组、重复单词、特殊字符、超长文本这四种情况。学习工具的用户往往不会按开发者的理想输入如果导入词库时某个单词包含|字符解析逻辑可能把释义切错。5. 常见问题排查页面空白、按钮无效、数据丢失5.1 页面空白控制台有报错现象是浏览器打开后只看到背景色标题和卡片都没有渲染或者白屏无内容。可能原因有这几种JavaScript 脚本执行报错阻止了后续逻辑元素 id 写错导致 DOM 操作失败引用了外部文件但路径错误。对于单文件网页最常见的是第三种也就是代码中明明用了外部 CDN 或外部 JS但网络不通或部署路径错误。检查方式是打开 F12 控制台看是否有红色报错。如果有Uncaught ReferenceError: xxx is not defined说明某个变量或函数名不一致如果 Network 面板显示某个 URL 失败说明外部依赖没加载成功。处理办法是把所有外部依赖内联到 HTML 中尤其是学习页面这种小工具完全不需要依赖 CDN。5.2 按钮点击之后没有反应现象是卡片能显示但点击“认识”或“下一张”没有任何变化。排查顺序建议这样先确认控制台有没有报错。如果报错先看报错位置。检查按钮 id 是否存在。按下 F12 选择元素查看按钮的 id 和脚本中的getElementById是否一致。检查事件绑定是否执行。可以在监听器第一行加console.log(click)点击后如果没打印说明绑定失败或按钮被其他元素遮挡。检查words是否为空数组。如果words.length 0nextRandom直接 return页面自然没有反应。在实际项目中很多“点击无效”其实是事件绑定顺序问题。脚本不在 body 底部也没有用DOMContentLoaded包裹导致监听器绑定时元素还不存在。这是新手最常见的错误之一。5.3 刷新之后学习进度丢失现象是页面能正常操作但按 F5 刷新后已掌握数量清零。排查顺序确认当前页面是通过 HTTP 服务打开还是直接双击的文件路径。直接双击时地址栏是file:///...部分浏览器对 localStorage 支持有限建议改用本地 HTTP 服务。检查浏览器是否处于无痕模式。无痕模式下 localStorage 通常不会持久化关闭窗口后数据就没了这是预期行为。检查getItem和setItem使用的 key 是否一致。不一致会导致读取时拿到 null于是回退到默认词库。检查写入的时机。如果点击“认识”后没有调用saveData()刷新自然会丢失。为减少这类问题可以在页面顶部加一行状态提示比如“最近保存时间12:30”。这能帮助用户确认数据是否真的写进去了。5.4 通用排查清单问题现象常见原因检查方式处理建议页面空白JS 报错、外部依赖加载失败Console、Network 面板内联所有依赖修复报错按钮无反应id 不匹配、事件绑定顺序错误Elements 面板检查 idConsole 验证统一 id 命名脚本放 body 底部刷新数据丢失file:// 协议、无痕模式、key 不一致地址栏、Application 面板启动 HTTP 服务统一存储 key卡片不翻面CSS 类名错误、虚拟 DOM 冲突Elements 面板看 class 变化检查 classList 和样式规则自定义词库不生效解析格式错误、空数据被忽略Console 打印解析结果提示用户格式增加预览6. 发布到线上把学习成果分享给其他人6.1 部署到 GitHub Pages 或 Vercel单文件网页部署成本极低。这里给出一种常见方式使用 GitHub Pages。前提是你有一个 GitHub 账号并且网络可正常访问 GitHub。整个过程只需要新建仓库、上传文件、开启 Pages 三个步骤。在 GitHub 上新建一个仓库名字任意比如learning-pages。把index.html上传到仓库根目录。然后进入仓库的 Settings找到 Pages 选项在 Source 里选择main分支目录选择/root点击 Save。稍等一分钟GitHub 会提供一个形如https://你的用户名.github.io/learning-pages/的访问地址。Vercel 也是常见选择。导入仓库后Vercel 会自动识别静态文件默认不需要构建命令发布逻辑很简洁。对于纯静态的单文件网页这两种方式都可以。差异点在于 GitHub Pages 完全免费、配置简单Vercel 提供预览部署每次 push 自动生成预览地址适合需要多人体验的场景。发布到线上之后原来在本地 localStorage 里存的数据并不会自动同步。每个浏览器、每个域名、每台设备都有自己的 localStorage 空间。如果希望多个设备共享学习进度需要引入后端或云端存储这不是单文件网页的职责范围。6.2 发布前检查清单发布前建议逐项核对下面这些内容页面在手机和电脑两种尺寸下布局是否正常。核心按钮是否都能点击卡片翻面是否顺畅。Console 是否干净没有报错和警告。外部链接是否失效是否引用了外部字体、图片、CDN。本地数据是否能正常保存、读取、清空。是否把个人隐私数据写进了前端代码比如密码、考试答案、账号信息。是否保留了默认词库方便第一次打开的用户看到示例。页面标题、meta 描述是否能说明网站用途。这些检查不复杂但能避免“自己电脑上正常别人打开就白屏”的尴尬。发布静态网页最大的风险就是路径和依赖问题内联所有资源能从根源上减少这类故障。6.3 性能与安全提示学习网页虽然小也要注意性能和隐私。本地数据不要放敏感信息因为 localStorage 可被同域页面读取。如果网页只是给自己用风险不大如果公开分享更要注意不要在前端放任何需要保密的内容。性能方面单文件网页通常已经足够轻量。但要注意不要引入超大图片、高分辨率背景图、远程字体库。学习工具的核心是内容可读性而不是视觉效果。把页面保持在几十 KB 到几百 KB 以内加载速度就会很快。注意生产环境发布时还需要额外考虑日志、权限、监控、回滚和异常处理。单文件学习网页虽然不用全上但至少要有版本备份意识每次改动前保留一份可回退的旧版本。7. 可以继续扩展的学习小网页方向7.1 错题本错题本和单词记忆卡结构非常接近但数据模型不同。错题本需要记录题目内容、正确答案、我的答案、错误次数、最后做错时间。初始词库可以用 JSON 数组放在页面里后续可以自己维护题库文件。用 AI 实现时提示词可以这样写做一个错题本页面 1. 每条错题包含题目、选项、正确答案、我的答案、解析。 2. 列表按最近错误时间倒序排列。 3. 点击错题展开详情显示正确答案和解析。 4. 支持把某道题标记为已掌握。 5. 数据存 localStorage。这个场景的关键不是页面效果而是数据结构设计。字段越清晰AI 生成的代码越接近需求。7.2 古诗文背诵检查器背诵检查器的核心需求是“给出上半句回忆下半句”。它的交互和单词卡类似但提示逻辑不同可以不显示选项只显示提示字数点击“显示答案”后展示完整句子继续点击“下一个”随机出题。这个页面同样适合单文件实现。数据可以内置一个数组每个对象包含title、sentence、source字段。为了增强记忆效果可以增加“背诵完成”状态记录每一篇古诗文的背诵次数。7.3 公式速查页和代码片段速查页公式速查页适合按科目分类每一条记录包含名称、公式、变量说明、示例。代码片段速查页则面向程序员记录常用正则、常用命令、报错处理方案。这两个页面的共同点是“内容检索比交互更重”所以实现重点要放在搜索和分组上。设计上可以做成左侧分类导航、右侧内容列表。分类多了以后再考虑把数据抽成独立的 JSON 文件。不过在单文件阶段直接用一个数组保存所有数据并实现搜索函数已经能满足绝大多数使用场景。7.4 把多个小网页串成工具集当学习网页数量超过 3 个建议做一个简单的工具导航首页把单词记忆卡、错题本、古诗文检查器都链接起来。导航首页可以用纯 HTML 和 CSS 实现不必引入框架。这样一次打开一个工具互不干扰数据也各自独立保存。扩展时要始终遵循一个原则每次只增加一个功能每次改动后都先确认旧功能没有坏掉。把 AI 生成的代码当作“第一次提交”再通过自己的测试来驱动后续提交这样学习小工具才会越做越稳而不是越改越乱。做学习小网页的价值不在于页面本身有多么惊艳而在于你学会了如何把模糊需求拆成可执行任务如何用 AI 快速生成初稿又如何通过测试和排查让代码真正可用。建议从本文的单词记忆卡开始复现一遍然后换成自己真正需要的场景让 AI 帮你完成第二个、第三个小工具。每一步都确认之后再往下走这个流程会慢慢变成你自己的工程习惯。