5个大模型写Todo List实测:前端代码生成能力深度拆解
1. 项目概述一场不带滤镜的前端能力压力测试“谁在划水谁在整活”——这句话不是段子是我把5个当前公认顶级的大模型拉进真实前端开发场景后脱口而出的第一反应。没有PPT包装不看官网宣传口径我把它们全扔进一个真实、琐碎、充满边界条件的前端任务里从零实现一个带表单验证、响应式布局、状态管理、本地存储和轻量动画的「待办事项Todo List」Web应用并要求输出可直接运行的完整HTML文件。不是只写几行代码示意而是真正能打开浏览器就用、能增删改查、能记住你昨天勾掉的第三条任务、能在手机上正常滑动的成品。这个标题里的“划水”和“整活”我给它下了明确定义划水 生成代码存在硬性错误如语法崩溃、逻辑死循环、关键API调用失败、缺失核心功能比如完全没做本地存储、或交付物根本无法运行整活 在满足基础功能前提下主动加入合理优化如用CSS变量统一主题色、补充健壮性处理如空输入防崩、甚至给出清晰注释和扩展建议。而“夸一下才行”指的是某些模型明显需要明确指令激励才会释放潜力——你让它“请务必确保所有交互逻辑完整本地存储必须持久化代码要加中文注释”它才肯把藏在知识库角落里的最佳实践掏出来。这个测评不是给技术小白看的“哪个模型更聪明”排行榜而是给一线前端工程师、技术选型负责人、以及正在评估AI辅助开发价值的团队负责人准备的实操参考。它解决的是三个最痛的问题第一大模型生成的前端代码到底能不能直接进项目第二不同模型在理解DOM操作、事件流、异步存储这些前端特有逻辑时能力断层有多大第三作为开发者我该怎么跟它们“对话”才能让它们少犯低级错误、多出可用成果接下来的内容就是我把这5个模型GPT-4 Turbo、Claude 3 Opus、Gemini 1.5 Pro、Qwen2-72B-Instruct、DeepSeek-V2塞进同一个沙盒记录下每一行输出、每一次报错、每一次重试后的完整复盘。没有修饰只有代码、截图、错误日志和我敲键盘时的真实吐槽。2. 测评设计与底层逻辑为什么是Todo List为什么是这5个模型2.1 为什么选Todo List作为唯一测评载体很多人觉得Todo List太简单不配当“顶级测评”。恰恰相反它的极简外表下藏着前端开发最核心的五层能力栈缺一不可第一层HTML结构语义化与DOM操作——不是堆div而是用main、section、button typesubmit等语义标签且所有JS操作必须精准对应DOM节点比如document.getElementById(todo-input)不能写成getElementById(input)第二层JavaScript事件流与状态管理——要处理input实时监听、submit表单拦截、click删除事件还要维护一个内存中的todos数组状态更新必须触发视图重绘第三层浏览器API深度调用——localStorage.setItem/getItem必须正确序列化/反序列化数组且要处理null返回值首次访问时getItem返回null直接JSON.parse会报错第四层CSS响应式与交互反馈——媒体查询适配移动端:hover和:focus伪类提供视觉反馈完成项用text-decoration: line-through删除按钮要有transition动画第五层工程化意识萌芽——代码组织是否清晰比如把渲染逻辑抽成renderTodos()函数注释是否说明关键决策如“此处用localStorage而非sessionStorage因需跨会话持久化”。提示我刻意避开了React/Vue等框架全部用原生JSHTMLCSS。因为框架封装会掩盖模型对底层浏览器行为的理解深度。一个能写出完美原生Todo的模型大概率也能写出合格的框架组件反之如果原生都搞不定框架里只会暴露更多隐性问题。2.2 为什么是这5个模型选型背后的三重过滤市面上大模型很多但我只筛出这5个基于三个硬性标准真实生产环境可及性必须是当前2024年中国内开发者能稳定、合规、无需特殊网络配置即可调用的模型。这意味着排除所有仅限海外区域访问、或需绑定境外支付方式的闭源模型。GPT-4 Turbo通过Azure或OpenRouter接入Claude 3 Opus通过Anthropic官方API或国内合作平台Gemini 1.5 Pro通过Google AI Studio国内可直连Qwen2-72B和DeepSeek-V2则直接部署在本地或私有云——全部路径我都实测过延迟和稳定性。上下文窗口与长文本理解能力Todo List虽小但完整实现涉及HTML/CSS/JS三块代码加上注释和说明总token数轻松破2000。模型必须能在一个请求内消化全部需求而不是分三次回答导致逻辑割裂。GPT-4 Turbo128K、Claude 3 Opus200K、Gemini 1.5 Pro1M、Qwen2-72B128K、DeepSeek-V2128K全部达标而早期的GPT-3.54K或Llama3-8B8K直接被筛掉——它们在生成CSS时经常“忘记”前面定义的JS变量名。代码生成专项优化程度不是所有大模型都认真训练过代码。我交叉验证了Hugging Face的CodeEval基准和我们团队内部的前端代码测试集含100个真实业务片段这5个模型在“HTML结构完整性”、“JS事件绑定正确率”、“CSS选择器作用域准确性”三项指标上均位列Top 5。尤其Qwen2-72B在中文注释生成和DOM操作习惯上明显比纯英文模型更贴合国内开发者语境。2.3 指令工程同一份Prompt如何榨干每个模型的潜力所有模型接收的原始Prompt完全一致这是公平性的基石请用原生HTML、CSS、JavaScript实现一个完整的待办事项Todo List应用。要求 1. HTML语义化结构包含标题、输入框、添加按钮、待办列表区域 2. CSS响应式设计手机端竖屏适配完成项加删除线悬停/聚焦有视觉反馈删除按钮带淡入动画 3. JS支持添加新任务回车或点击按钮、标记完成点击任务文字、删除任务点击删除图标、本地存储页面刷新后数据不丢失 4. 代码输出一个完整的、可直接保存为.html文件并用浏览器打开运行的单文件 5. 注释所有关键逻辑处添加中文注释说明为什么这样写例如“此处用localStorage.setItem(todos, JSON.stringify(todos))因需跨会话持久化”。 请勿使用任何框架React/Vue等仅用原生技术。但实测发现纯“要求式”Prompt对部分模型效果有限。于是我在每轮测试中增加了两套“增强指令”作为对照组A组基础版上述原始Prompt不加任何额外说明B组激励版在末尾追加一句“你是一个资深前端工程师你的代码将被用于真实生产环境请务必保证100%可运行、无语法错误、无逻辑漏洞本地存储必须可靠。”C组约束版在开头增加约束“请严格按以下顺序输出① 完整HTML代码含 ② 不要额外解释③ 不要输出代码块标记如html④ 所有注释用//或/* */格式不要用HTML注释。”结果很有趣Claude 3 Opus在A组就交出95分答卷B组提升到98分而Gemini 1.5 Pro在A组频繁漏掉localStorage的null判断直到C组明确“不要额外解释”后才把注意力全集中在代码本身错误率骤降。这说明模型不是“笨”而是对“什么是好代码”的认知权重不同——有的重逻辑严谨有的重表达完整有的重格式规范。作为使用者你得先读懂它的“性格”再决定怎么下指令。3. 核心细节解析5个模型的代码质量逐行拆解3.1 GPT-4 Turbo稳如老狗但细节藏雷GPT-4 Turbo的输出堪称教科书级工整HTML结构语义清晰CSS用Flexbox做响应式JS把renderTodos()、addTodo()、toggleTodo()、deleteTodo()四个函数拆得明明白白。本地存储部分它甚至主动加了错误捕获try { localStorage.setItem(todos, JSON.stringify(todos)); } catch (e) { console.error(本地存储失败:, e); }这很专业但问题出在更隐蔽的地方。我把它保存为todo-gpt.html用Chrome打开输入“买牛奶”点添加——成功。再点“买牛奶”左侧的复选框标记完成——页面卡死。打开控制台报错Uncaught TypeError: Cannot read properties of null (reading classList)。定位到JS里这行const todoItem document.querySelector([data-id${id}]); todoItem.classList.toggle(completed); // 这里todoItem是null为什么因为HTML里生成的todo项是这样的li>li>// 状态对象所有操作围绕它展开 const state { todos: JSON.parse(localStorage.getItem(todos) || []), nextId: parseInt(localStorage.getItem(nextId) || 1) }; // 添加todo时自动生成唯一ID并存入localStorage function addTodo(text) { const newTodo { id: state.nextId, text, completed: false }; state.todos.push(newTodo); saveState(); } function saveState() { localStorage.setItem(todos, JSON.stringify(state.todos)); localStorage.setItem(nextId, state.nextId.toString()); }这段代码几乎挑不出毛病ID自增防重复、saveState()统一入口、JSON.parse前做了|| []兜底。但CSS部分它用了大量CSS Grid和clamp()函数.todo-list { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 1rem; }这在Chrome最新版跑得飞起但在我测试的Edge 115企业内网常用版本上repeat(auto-fit)完全失效所有todo挤成一列。更致命的是它给删除按钮写的动画.delete-btn { animation: fadeIn 0.3s ease-out forwards; } keyframes fadeIn { from { opacity: 0; transform: scale(0.8); } to { opacity: 1; transform: scale(1); } }问题在于forwards会让按钮在动画结束后保持scale(1)但用户点击删除后按钮本该消失而opacity:1却让它一直挂着。Claude的“文艺”体现在它追求视觉精致却忽略了动画与DOM生命周期的耦合。注意Claude 3 Opus的弱点是“过度设计”。它默认把用户当成追求前沿CSS的设计师而非需要兼容IE11虽然现在少了或老旧Android WebView的工程师。我的应对是在Prompt里加约束“CSS必须兼容Chrome 90、Firefox 85、Safari 15、Edge 100禁用实验性CSS特性如container queries、:has()”。3.3 Gemini 1.5 Pro快准狠但“本地存储”是它的阿喀琉斯之踵Gemini 1.5 Pro的响应速度最快3秒内吐出完整代码。HTML和CSS几乎零错误JS逻辑也清爽。但它在localStorage环节连续翻车两次第一次它写了// 页面加载时读取 if (localStorage.getItem(todos)) { todos JSON.parse(localStorage.getItem(todos)); }这看起来没问题但localStorage.getItem(todos)返回null时if(null)为false跳过赋值todos保持初始空数组[]看似OK。可一旦用户添加一条todoJSON.stringify([])存进去下次getItem返回[]字符串JSON.parse([])得到[]还是OK。问题出在首次访问localStorage为空getItem返回nullif(null)跳过todos[]但用户没添加任何todo就刷新页面todos仍是[]一切正常。等等这没错啊别急看第二次翻车。第二次它把saveToStorage()函数写在了addTodo()内部但没在toggleTodo()和deleteTodo()里调用。结果是你能添加todo刷新后还在但标记完成或删除后页面显示变了localStorage里的数据却没更新刷新页面刚标记的完成项又变回未完成。这是典型的“状态与存储不同步”。我重试时在Prompt里加了B组激励“你的代码将被用于真实生产环境请务必保证100%可运行”它立刻修正了把saveToStorage()抽成独立函数并在所有修改状态的地方调用。这印证了标题里那句“夸一下才行”——Gemini需要明确的“责任提醒”否则它默认按“最小可行代码”交付省略了非核心但必要的同步逻辑。3.4 Qwen2-72B-Instruct中文语境王者但JS作用域有点迷Qwen2-72B的代码最让我有亲切感。它用的全是中文变量名待办列表、添加按钮、渲染函数注释更是像老同事在教你// 注意这里用 let 声明因为 todos 会在后续函数中被重新赋值 // 如果用 const后面 todos 新数组 会报错 let todos JSON.parse(localStorage.getItem(todos) || []);DOM操作也深谙国内开发者习惯比如获取元素不用document.getElementById而用更短的$函数它自己实现的const $ (selector) document.querySelector(selector); const $$ (selector) document.querySelectorAll(selector);这很实用但问题出在作用域。它把todos声明在全局所有函数都能访问这没问题。可它在addTodo()里写了function addTodo() { const 输入框 $(#todo-input); const 文本 输入框.value.trim(); if (!文本) return; // 这里直接 push没用 let 声明新变量 todos.push({ id: Date.now(), text: 文本, completed: false }); 渲染函数(); 输入框.value ; }看着很顺但Date.now()作为ID在高并发下可能重复虽然Todo List不会并发更严重的是它没处理localStorage的setItem。我检查全文发现saveToStorage()函数被它写在了文件末尾但addTodo()里根本没调用而且这个saveToStorage()函数本身还有bugfunction saveToStorage() { localStorage.setItem(todos, JSON.stringify(todos)); // 它漏掉了 try-catch }当用户在无痕模式下运行localStorage被禁用setItem直接抛错整个JS崩掉。Qwen2-72B对中文表达极度友好但对JS的“防御性编程”意识稍弱似乎默认运行环境完美无缺。实操心得Qwen2-72B适合快速原型和教学场景但生产环境必须人工补上try-catch和存储调用。我的做法是在Prompt里加一句“所有修改状态的操作添加/修改/删除必须立即同步到localStorage并用try-catch包裹setItemcatch中console.error”。3.5 DeepSeek-V2性能怪兽但“响应式”理解有偏差DeepSeek-V2的代码体积最大因为它生成了超详细的注释每行JS都有说明。更惊人的是它主动加了节流防抖// 防抖避免用户狂点添加按钮导致重复提交 let addTimer; function addTodo() { clearTimeout(addTimer); addTimer setTimeout(() { const 文本 $(#todo-input).value.trim(); if (!文本) return; todos.push({ id: nextId, text: 文本, completed: false }); saveToStorage(); render(); $(#todo-input).value ; }, 300); }这已经超出Todo List需求属于高级前端技巧。但它的“响应式”实现让我哭笑不得。它用媒体查询写了media (max-width: 768px) { .todo-container { width: 95vw; padding: 0 2vw; } }这本身没问题可它把整个应用包在一个div classtodo-container里而这个div的CSS是.todo-container { max-width: 800px; margin: 0 auto; }问题来了在手机上max-width: 800px让容器永远宽800px远超手机屏幕导致横向滚动条出现。而width: 95vw的媒体查询根本没生效因为父容器max-width锁死了宽度。DeepSeek-V2理解“响应式”这个词但没吃透“viewport宽度”和“容器宽度”的层级关系——它把响应式规则写在了错误的CSS选择器上。4. 实操过程与核心环节实现从Prompt到可运行文件的全流程4.1 环境准备5分钟搭好公平竞技场要让5个模型在同一起跑线竞争环境必须绝对干净、可复现。我用的是最简配置操作系统Windows 11 22H2确保Edge、Chrome最新版浏览器Chrome 126主测、Edge 126兼容性辅测本地服务不用任何服务器直接双击.html文件运行规避CORS等网络问题代码编辑器VS Code Prettier插件统一格式化避免风格差异干扰判断关键一步是创建标准化测试模板。我新建一个test-template.html内容只有!DOCTYPE html html langzh-CN head meta charsetUTF-8 meta nameviewport contentwidthdevice-width, initial-scale1.0 titleAI前端测评 - Todo List/title /head body !-- 所有AI生成的代码将完整替换此处 -- /body /html这个模板强制模型输出完整HTML含!DOCTYPE和html而不是代码片段。很多模型喜欢只输出div部分导致直接运行报错。用模板兜底确保所有输出都是“开箱即用”。提示别小看这个模板。我测试初期GPT-4 Turbo有次输出的代码开头是script标签没html双击打开一片空白。加了模板后它立刻学会从!DOCTYPE开始写。这就是“约束即自由”——明确边界反而激发模型的结构化输出能力。4.2 Prompt工程实战三轮迭代榨干模型潜力第一轮A组基础版暴露了所有模型的“出厂设置”水平。GPT-4 Turbo和Claude 3 Opus直接过关Gemini 1.5 Pro和Qwen2-72B在存储同步上掉链子DeepSeek-V2的响应式写错了地方。第二轮B组激励版针对掉链子的模型。我在Prompt末尾加了“你是一个有10年经验的前端架构师这个Todo List将部署到百万级用户的产品首页任何bug都会导致客诉。请用你最严谨的工程思维交付。” 结果Gemini 1.5 Pro立刻补全了所有saveToStorage()调用Qwen2-72B在saveToStorage()里加了try-catchDeepSeek-V2重写了CSS把max-width改成width: 100%媒体查询终于生效。但新问题浮现Claude 3 Opus在B组下为了追求“百万用户”加入了Service Worker缓存逻辑这完全超出Todo List范畴还导致Chrome控制台报Uncaught DOMException: Failed to execute register on ServiceWorkerContainer——因为本地文件协议file://不支持Service Worker。这说明过度激励可能引发“画蛇添足”。第三轮C组约束版专治“画蛇添足”和格式混乱。我加了硬性规则输出格式强制要求 ① 第一行必须是 !DOCTYPE html ② 最后一行必须是 /html ③ 中间所有代码禁止出现任何自然语言解释如“以下是HTML代码” ④ 所有注释必须用 // 或 /* */禁止使用 !-- HTML注释 -- ⑤ CSS必须写在style标签内JS必须写在script标签内禁止外链这一轮Claude 3 Opus删掉了Service WorkerGemini 1.5 Pro的代码长度缩短了30%可读性反而提升。Qwen2-72B的中文注释更精炼DeepSeek-V2的节流代码被简化为防重复点击disabled属性更符合场景。4.3 错误排查与修复手把手带你修通5个模型的“最后一公里”每个模型的代码我都没直接用而是走一遍“开发者验收流程”语法检查粘贴到ESLint在线工具https://eslint.org/demo/看是否有Parsing error。GPT-4 Turbo和Claude 3 Opus零错误Gemini 1.5 Pro有Unexpected token定位到它用了可选链?.而我的Chrome 126支持但ESLint默认配置较旧手动升级配置后通过运行时调试F12打开控制台逐个操作添加→完成→删除→刷新。记录所有Uncaught TypeError存储验证在Application → Local Storage里手动清空再操作确认todos键值实时更新响应式测试用Chrome DevTools的Device Toolbar切iPhone 12、Pixel 5、iPad Pro看布局是否错乱。修复过程不是“改错”而是“理解模型的思维盲区”。比如修复GPT-4 Turbo的>// 改为禁用按钮视觉反馈更直接 function addTodo() { const btn $(#add-btn); btn.disabled true; btn.textContent 添加中...; setTimeout(() { // ...核心逻辑 btn.disabled false; btn.textContent 添加; }, 100); }这比防抖更符合用户心智模型。模型提供了“技术方案”而开发者要把它转化为“用户体验方案”。4.4 性能与体验对比不只是能跑还要跑得爽代码能跑只是及格线真实体验才是王道。我用Chrome的Lighthouse跑了5次审计每次清空缓存取平均分模型PerformanceAccessibilityBest PracticesSEOGPT-4 Turbo92989596Claude 3 Opus88949092Gemini 1.5 Pro95979698Qwen2-72B85908889DeepSeek-V280858284Gemini 1.5 Pro性能最高因为它生成的CSS最精简JS没用任何花哨APIDeepSeek-V2最低节流代码和冗余注释增加了JS解析时间。但有意思的是Accessibility无障碍得分Qwen2-72B意外领先——它给所有按钮加了aria-label输入框有aria-describedby关联提示而其他模型基本没考虑。这说明中文模型在本土化细节如无障碍上有天然优势。体验上我让5个同事盲测不告诉是谁生成的用手机操作10分钟然后打分1-5分操作流畅度Gemini 1.5 Pro4.8、GPT-4 Turbo4.5、Claude 3 Opus4.3、Qwen2-72B4.0、DeepSeek-V23.7代码可维护性让他们试着加个“清空全部”按钮Claude 3 Opus4.9、GPT-4 Turbo4.6、Qwen2-72B4.2、Gemini 1.5 Pro3.8、DeepSeek-V23.5。结论很清晰Gemini 1.5 Pro胜在“交付即用”Claude 3 Opus胜在“长期可演进”而Qwen2-72B在中文语境下新人上手成本最低。5. 常见问题与排查技巧实录那些没写在文档里的坑5.1 “代码复制过去就报错”——90%的问题出在这3个地方这是新手最常问的问题。我整理了5个模型输出中高频出现的3类“隐形炸弹”附带一键修复方案问题现象根本原因修复方案出现场景Uncaught ReferenceError: $ is not defined模型生成了$(#xxx)但没定义$函数在script开头加const $ (s) document.querySelector(s);brconst $$ (s) document.querySelectorAll(s);Qwen2-72B、DeepSeek-V2高频Uncaught TypeError: Cannot set property textContent of nullquerySelector没找到元素通常因ID写错或元素未加载在操作前加判断const el $(#xxx);brif (el) el.textContent xxx;所有模型GPT-4 Turbo最常漏Uncaught SyntaxError: Unexpected token 复制时不小心把HTML注释!-- --或script标签外的内容粘进去了用VS Code的“Select All”“Copy as Plain Text”或粘贴后全选→右键→“Format Document”所有模型尤其Claude 3 Opus爱写长注释注意别迷信“一键修复”。比如$ is not defined如果你的项目已用jQuery那$是全局的加这个定义反而冲突。真正的修复是“理解上下文”——模型生成的代码是独立运行的所以它有权定义自己的$而你的项目是集成环境就得按项目规范来。5.2 “本地存储不生效”——浏览器策略比你想的更严格localStorage失效90%不是代码问题而是浏览器策略无痕模式所有主流浏览器在无痕模式下默认禁用localStoragesetItem会静默失败或抛错。测试时务必用普通窗口第三方Cookie限制某些浏览器如Safari对file://协议有额外限制localStorage可能被禁用。解决方案用Live Server插件起一个http://127.0.0.1:5500/服务存储配额超限虽然Todo List不可能超但如果你在代码里写了localStorage.setItem(big-data, hugeString)可能触发配额错误。检查Application → Storage → Local Storage的使用量。我遇到最诡异的一次是Chrome 125的某个Beta版localStorage.getItem(todos)返回undefined而非null导致JSON.parse(undefined)直接崩溃。修复方案是统一用const saved localStorage.getItem(todos); const todos saved ? JSON.parse(saved) : [];5.3 “响应式在手机上错乱”——媒体查询的3个致命误区模型写的媒体查询常犯三种错误单位混淆用px写断点如media (max-width: 768px)但在meta nameviewport里没设initial-scale1.0导致手机上768px实际是384物理像素断点失效。修复确保meta标签完整选择器权重不足模型写了media (max-width: 768px) { .todo-item { width: 100%; } }但前面有.todo-item { width: 300px; }后者权重更高。修复提高媒体查询内选择器权重或用!important不推荐或重构CSS优先级Flex/Grid容器未设宽高如display: flex的父容器没设height: 100vh子元素flex: 1就无效。修复给根容器加height: 100vh。实操心得我现在的标准动作是生成代码后立刻在Chrome DevTools里切到“Responsive Design Mode”点右上角“⋯”→“Show device frame”再点“Toggle device toolbar”最后点“Reload”——强制刷新并重载viewport。这能绕过大部分缓存导致的响应式失效。5.4 “为什么它不听我的话”——Prompt失效的4个真相不是模型不听话是你没摸清它的“沟通协议”真相1模型没有“记忆”。每次请求都是全新会话。你以为说“上文提到的localStorage”它其实不知道“上文”在哪。必须把所有上下文写进本次Prompt真相2模型会“脑补”。你让它“用CSS实现响应式”它可能脑补出container实验性特性因为训练数据里有。要禁用就得明说“禁用实验性CSS”真相3模型有“默认偏好”。GPT-4 Turbo偏好TypeScriptClaude 3 Opus偏好函数式编程Qwen2-72B偏好中文。对抗偏好不如利用偏好——比如要中文注释就选Qwen2-72B真相4模型怕“模糊”。你说“代码要简洁”它可能删掉所有注释说“代码要易读”它可能加一堆注释。要精确就说“注释必须说明每个函数的输入、输出、副作用”。我现在的Prompt模板固定包含三段【角色】你是一个有10年经验的前端工程师专注原生Web开发。 【任务】用HTML/CSS/JS实现XXX要求YYY。 【约束】必须遵守① ZZZ② AAA③ BBB。角色定调任务明确约束兜底三者缺一不可。6. 经验总结与延伸思考当AI成为你的“