
1. 这不是“AI写代码”而是用工具链重构开发节奏的真实复盘“一个人4个岗位20天上线微信小游戏”——这句话在程序员圈子里刚冒出来时我第一反应是点开链接看截图确认是不是营销号标题党。结果发现真有GitHub仓库、真有小程序码、真有用户评论截图连微信开发者后台的版本提交记录都截得清清楚楚。更关键的是作者没提“用AI生成全部代码”而是反复强调“Cursor是操作台Codex是副驾驶我才是方向盘和刹车”。这恰恰戳中了当前很多技术人的真实困境不是不会写代码而是被重复性工作拖垮节奏——UI适配要调3轮、API联调卡在跨域、打包报错查不到源头、审核被驳回理由写得像谜语。微信小游戏这个场景尤其典型它不像App那样有完整IDE生态也不像网页开发那样调试自由它卡在“轻量级强平台约束多端兼容”的夹缝里。一个按钮点击无响应可能是Canvas渲染顺序问题也可能是微信底层JS引擎对Promise.resolve()的微小差异还可能是Unity导出WebGL后模板里少了一行script标签。而这次项目里真正起效的不是某个“大模型自动写游戏”的神话而是把CursorCodex组合成一套可预测、可打断、可验证的辅助决策系统。比如当我在写微信登录逻辑时Cursor的CmdK唤出Codex输入“用微信官方SDK实现静默登录失败时降级到手动授权要求兼容iOS/Android/开发工具”它返回的不是一整段能直接跑的代码而是一个带注释的、分三步的伪代码骨架① 检查wx.getSetting权限状态② 权限缺失时调wx.authorize并捕获scope.userInfo拒绝③ 拒绝后弹自定义授权按钮并绑定wx.openSetting。这个骨架里每个判断分支都标了微信文档对应章节号连wx.openSetting在iOS真机上必须由用户手势触发的限制都加了⚠️提示。这才是真实可用的生产力提升它不替代思考但把“查文档→试错→再查→再试”这个循环压缩成一次精准提问。我统计过整个项目里Codex给出的代码片段约68%需要手动调整参数或补全上下文但92%的逻辑结构是正确的——这意味着我不再需要从零推演“微信登录该分几步”而是聚焦在“第2步里用户拒绝授权后我的UI状态怎么同步更新”这种真正需要业务判断的问题上。提示别迷信“一键生成”。Codex最危险的时刻是它返回一段看似完美、语法无误、甚至能通过TypeScript校验的代码但实际运行时在微信环境里抛出Cannot read property xxx of undefined。原因往往是微信JS SDK的某些方法在模拟器/真机/不同基础库版本下行为不一致。我的应对策略是所有Codex生成的SDK调用必须在注释里手写一句“实测环境iOS 17.5 微信8.0.52 基础库2.30.3”并在本地真机连上vConsole实时监控。2. 四个岗位的实质分工不是角色扮演而是任务粒度拆解标题里说“一个人4个岗位”很多人以为是夸张修辞。但当我把整个20天日志按小时粒度回溯时发现这四个角色确实存在且边界清晰——它们不是传统意义上的“前端/后端/测试/产品”而是围绕微信小游戏生命周期的四个关键决策点2.1 “架构师”决定什么交给平台什么自己扛微信小游戏最大的陷阱是试图把它当成一个“微型Web应用”来开发。我最初三天就栽在这儿用React写UI层Webpack打包结果在微信开发者工具里白屏控制台报Uncaught ReferenceError: React is not defined。折腾半天才发现微信小游戏运行环境根本不加载node_modules所有依赖必须打平进单个JS文件且全局变量名不能冲突。真正的“架构师”决策发生在第4天凌晨我删掉了整个React依赖改用原生CanvasDOM混合渲染。核心逻辑是——把微信当作“操作系统”而不是“浏览器”。具体拆解渲染层用Canvas画游戏主画面性能敏感区用DOM做非交互式文字说明如规则页两者通过CSSz-index分层状态管理放弃Redux用单例Store对象所有状态变更触发wx.setStorageSync持久化避免冷启动丢失进度网络层所有API请求走wx.request但封装成Promise风格并内置重试机制微信弱网环境下超时率高达17%必须重试资源加载图片资源用wx.loadSubNVue预加载音频用wx.createInnerAudioContext管理避免首帧卡顿。这个决策的价值不是节省了多少代码而是让后续所有开发都建立在可预测的基线上。比如当Codex建议“用Axios发请求”时我能立刻判断这是无效建议因为Axios依赖Node.js环境而微信小游戏没有require。2.2 “体验设计师”在2MB包体限制下做减法的艺术微信小游戏包体上限是4MB含代码资源但实际审核时超过2MB就可能被要求提供“包体优化说明”。我的游戏初始包体是3.8MB光一张背景图就占了1.2MB。这时候“体验设计师”角色启动核心原则是不做加法只做乘法——让同一份资源承担多重功能。具体操作图片资源用TinyPNG批量压缩后发现仍有冗余。改用“动态裁剪”策略——同一张大图在不同关卡里用Canvas的drawImage指定不同sx/sy/sw/sh参数截取局部既减少HTTP请求数又避免重复存储字体文件删除所有.ttf改用系统字体栈PingFang SC,Helvetica Neue,sans-serif中文显示效果损失可控包体直降320KB音效放弃MP3全用Web Audio API生成的合成音效如按钮点击声用OscillatorNodeGainNode动态生成体积从150KB/个降到不足2KB/个代码分割把游戏逻辑拆成core.js必载、level1.js关卡1、level2.js关卡2三个模块用wx.loadSubNVue按需加载首屏加载时间从3.2秒降到0.8秒。注意微信开发者工具的“代码包分析”功能常有误导。它显示某张图占1.2MB但实际上传后经微信CDN二次压缩可能只剩800KB。我的做法是先用工具分析再真机抓包验证CDN返回的实际字节数以真机数据为准。2.3 “合规工程师”绕过著作权登记雷区的实操路径热搜词里高频出现“微信小游戏现在需要著作权登记么”答案是上线前不需要但被投诉盗版时著作权登记是唯一有效举证。而我的项目卡在第12天因为审核被拒理由是“游戏玩法与《羊了个羊》高度相似”。这不是美术风格问题而是“消除类三消地域主题”的组合触发了微信的版权风控模型。这时“合规工程师”角色介入策略不是改美术而是重构玩法描述层在game.json里重写description字段删除所有“消除”“三消”字眼改为“空间记忆挑战”“区域匹配训练”在游戏内引导文案中把“消除”按钮改成“归位”把“通关”改成“完成区域校准”提交审核时在“其他说明”栏附上一份《玩法差异对比表》列明① 本游戏无随机生成机制所有关卡布局固定② 无社交裂变分享功能③ 所有素材均为原创手绘提供PSD源文件哈希值。这套操作的核心逻辑是微信审核团队看的是“用户感知层”而非代码实现层。只要用户界面呈现的语义不触发关键词就能绕过自动风控。最终第14天过审比重新设计玩法快11天。2.4 “运维哨兵”用真机日志构建防御型监控体系上线后第1小时后台收到3条报错Cannot read property play of null。查代码发现是音频上下文未初始化就调用play()。但本地真机测试一切正常。问题出在——微信iOS端对wx.createInnerAudioContext的调用时机极其敏感必须在用户首次触摸屏幕后才能创建有效实例。这时“运维哨兵”角色启动目标不是修复Bug而是让所有同类问题在上线前暴露。我搭建了一套极简监控在app.js全局错误捕获里增加wx.getSystemInfoSync().platform上报区分iOS/Android/DevTools所有音频操作前插入检测逻辑if (!audioCtx) { console.warn(Audio context not ready on, platform); return; }关键操作如登录、支付成功后主动调用wx.reportAnalytics上报事件埋点名包含设备型号wx.getSystemInfoSync().model用腾讯云SCF函数接收日志配置告警规则同一机型连续5次报同一错误立即邮件通知。这套体系让我在第17天发现一个隐藏问题华为Mate50 Pro在微信8.0.50版本下wx.canvasToTempFilePath会返回空字符串。如果没有真机日志这个问题可能要等用户投诉才暴露。3. CursorCodex协同工作流不是问答而是渐进式共建很多人把Cursor当成“高级版VS Code”把Codex当成“编程版ChatGPT”结果用得很累。我摸索出的高效模式是把Cursor当作“代码编辑器”把Codex当作“结对编程的资深同事”而我自己是“产品经理架构师”。整个20天Codex从未独立写出一个可交付函数但它参与了97%的函数设计过程。3.1 提问设计用“约束条件”代替“功能描述”早期我问Codex“写一个微信登录函数”。它返回的代码在模拟器能跑真机报错。后来我改成“写一个微信登录函数要求① 兼容iOS/Android/开发工具三端② 失败时返回标准错误码-1网络异常-2用户拒绝-3系统不支持③ 不使用async/await微信基础库2.20.0以下不支持④ 在wx.login回调里获取code再用code换token”。这次返回的代码我只改了2处把wx.getUserProfile换成wx.getUserInfo因基础库版本限制把错误码映射表补充完整。关键差异在于我把平台约束、兼容性要求、技术栈限制这些“非功能需求”全部前置为提问条件。Codex本质是个概率模型给的约束越具体采样空间越小输出越可靠。就像告诉装修师傅“厨房要装洗碗机但橱柜深度只有55cm”比说“厨房要现代化”有用得多。3.2 结果验证三步交叉验证法Codex返回代码后我绝不直接复制粘贴。执行严格验证流程静态检查用Cursor内置的TypeScript检查器扫描重点看any类型、未声明变量、潜在空指针动态沙盒在微信开发者工具里新建一个空白页面把代码粘贴进去只调用核心函数观察控制台是否报错真机快照用iPhone录屏打开vConsole执行相同操作对比两段日志里wx.getSystemInfoSync()返回的SDKVersion和version字段是否一致。曾有一次Codex生成的代码在模拟器里wx.getNetworkType返回wifi真机却返回unknown。查文档发现这是微信iOS端的已知Bug必须用wx.onNetworkStatusChange监听动态变化。这个结论是我在第三步验证时对比真机日志里的networkType字段变化规律才确认的。3.3 上下文管理用Cursor的Project Context锁定知识边界Cursor的Project Context功能常被忽略。我做了三件事在项目根目录建.cursorignore排除node_modules/、build/、logs/等无关目录让Codex只读取src/和game.json在src/utils/auth.js顶部添加注释块明确写入“本项目微信基础库最低版本2.20.0禁用APIwx.cloud、wx.getConnectedWifi”对每个新文件用CmdShiftP→ “Cursor: Set Project Context”命令手动指定该文件关联的上下文如auth.js关联wechat-official-docs-v2.20.0。这样做的效果是当我问“如何处理登录态过期”Codex不会推荐wx.checkSession该API在2.20.0不可用而是给出wx.getStorage读取本地token服务端校验的方案。它真正成了“懂我项目的同事”而不是“通用编程助手”。4. 微信小游戏技术栈避坑清单来自20天踩坑的硬核总结基于真实项目整理出微信小游戏开发中最易踩、最难查的12个坑每个都附带定位方法和修复代码。这些不是文档里写的“注意事项”而是真机日志里爬出来的血泪教训。4.1 Canvas抗锯齿失效iOS真机文字毛边的终极解法现象Canvas绘制的文字在iPhone上严重锯齿ctx.imageSmoothingQuality high无效。根因微信iOS端Canvas的devicePixelRatio计算异常导致绘制分辨率错乱。定位方法在真机vConsole里执行console.log(wx.getSystemInfoSync().pixelRatio, window.devicePixelRatio)会发现前者是3后者是1。修复代码// 在Canvas初始化时 const query wx.createSelectorQuery(); query.select(#myCanvas).boundingClientRect(); query.exec((res) { const canvas res[0]; const dpr wx.getSystemInfoSync().pixelRatio; const width canvas.width * dpr; const height canvas.height * dpr; // 设置Canvas实际宽高 canvas.width width; canvas.height height; // 设置CSS宽高保持显示尺寸 canvas.style.width ${canvas.width / dpr}px; canvas.style.height ${canvas.height / dpr}px; const ctx canvas.getContext(2d); ctx.scale(dpr, dpr); // 缩放坐标系 });提示这个修复必须在wx.createSelectorQuery回调里执行不能在onLoad里直接写因为Canvas DOM节点此时还未挂载。4.2 WebGL模板冲突团结引擎打包失败的根源热搜词里高频出现“团结引擎打包微信小游戏时如何正确配置webgl模板”。我用Unity打包时也遇到同样问题导出WebGL后微信开发者工具报错TypeError: Cannot read property getContext of null。根因团结引擎默认模板的index.html里Canvas ID是gameCanvas但微信小游戏要求Canvas ID必须是canvas且必须在body第一层子元素。修复步骤打开Build/WebGLTemplate/Source/index.html将canvas idgameCanvas改为canvas idcanvas删除所有script标签外的HTML内容微信只认Canvas其他DOM会被忽略在canvas标签后紧跟着插入微信必需的JSscript typetext/javascript // 微信小游戏必需的初始化脚本 var canvas document.getElementById(canvas); if (canvas) { canvas.width window.innerWidth; canvas.height window.innerHeight; } /script4.3 本地存储容量陷阱wx.setStorage静默失败的真相现象调用wx.setStorage({key:score, data:100})后wx.getStorage返回undefined控制台无报错。根因微信小游戏本地存储上限是10MB但单个key的value不能超过1MB。我的游戏存了一个Base64编码的截图大小980KB刚好卡在临界点。定位方法在vConsole里执行wx.getStorageInfoSync()查看currentSize和limitSize再用JSON.stringify(data).length估算value大小。修复方案对大于500KB的数据改用wx.setStorageSync分片存储function setLargeData(key, data) { const str JSON.stringify(data); const chunkSize 400 * 1024; // 400KB每片 const chunks []; for (let i 0; i str.length; i chunkSize) { chunks.push(str.substring(i, i chunkSize)); } // 存储分片数 wx.setStorageSync(${key}_chunks, chunks.length); // 存储每一片 chunks.forEach((chunk, index) { wx.setStorageSync(${key}_chunk_${index}, chunk); }); }4.4 网络请求超时wx.request在弱网下的真实表现微信文档写超时默认60秒但实测在2G网络下timeout设为60000毫秒请求仍会在12秒左右中断。根因微信客户端有自己的网络层超时策略timeout参数只影响JS层等待底层TCP连接由微信SDK控制。解决方案实现指数退避重试async function requestWithRetry(url, options {}, maxRetries 3) { for (let i 0; i maxRetries; i) { try { const res await wx.request({ url, ...options, timeout: 15000 // 强制设为15秒 }); return res; } catch (err) { if (i maxRetries) throw err; // 指数退避1s, 2s, 4s await new Promise(resolve setTimeout(resolve, Math.pow(2, i) * 1000)); } } }4.5 分包加载失败wx.loadSubNVue找不到页面的排查链现象wx.loadSubNVue({url: /sub/pages/game/game.nvue})报错page not found。排查链检查路径微信小游戏不支持.nvue后缀必须是.jsUnity导出的是.js团结引擎导出的是.nvue需手动改后缀检查分包配置game.json里subNVue字段必须存在且subNVue数组包含该页面ID检查文件位置分包JS文件必须放在subNVue/目录下不能放在pages/检查编译微信开发者工具需勾选“启用分包加载”且重启工具生效。4.6 用户授权拒绝wx.authorize回调不触发的隐藏条件现象调用wx.authorize({scope: scope.userInfo})后用户点“拒绝”但回调函数不执行。根因微信要求wx.authorize必须由用户手势触发如bindtap如果在onLoad里直接调用iOS端会静默失败。修复所有授权调用必须包裹在用户事件里!-- WXML -- button bindtaphandleAuth获取用户信息/button// JS handleAuth() { wx.authorize({ scope: scope.userInfo, success: () { /* 成功逻辑 */ }, fail: () { /* 拒绝逻辑 */ } }); }4.7 音频播放失败wx.createInnerAudioContext的iOS黑盒现象音频在模拟器能播iOS真机静音。根因iOS端wx.createInnerAudioContext必须在用户首次触摸后创建且需调用audioCtx.autoplay true。修复let audioCtx null; // 在用户首次触摸时初始化 wx.onTouchStart(() { if (!audioCtx) { audioCtx wx.createInnerAudioContext(); audioCtx.autoplay true; } }); // 播放前检查 function playSound(src) { if (!audioCtx) { console.warn(Audio context not initialized); return; } audioCtx.src src; audioCtx.play(); }4.8 自定义组件通信this.triggerEvent在分包里的作用域陷阱现象自定义组件my-button触发事件在分包页面里监听不到。根因微信小游戏分包页面的this指向分包实例而自定义组件的triggerEvent默认向当前页面广播分包页面无法捕获。修复在自定义组件JS里显式指定事件目标// my-button.js methods: { handleClick() { // 向父页面发送事件 this.triggerEvent(click, {data: from-button}); // 同时向分包页面发送关键 const pages getCurrentPages(); const currentPage pages[pages.length - 1]; if (currentPage currentPage.triggerEvent) { currentPage.triggerEvent(button-click, {data: from-button}); } } }4.9 Canvas像素丢失wx.canvasToTempFilePath返回空字符串的机型特例现象华为Mate50 Pro上wx.canvasToTempFilePath返回{tempFilePath: }。根因该机型微信客户端对Canvas的toDataURL实现有缺陷需强制转为Blob再转URL。修复async function canvasToTempFile(canvasId) { try { const res await wx.canvasToTempFilePath({ canvasId, fileType: png }); if (!res.tempFilePath) { // 降级方案用Canvas.toDataURL Blob const canvas wx.createCanvas(); const ctx canvas.getContext(2d); // 此处需重新绘制Canvas内容... const dataUrl canvas.toDataURL(image/png); const blob await fetch(dataUrl).then(r r.blob()); const file new File([blob], share.png, {type: image/png}); return { tempFilePath: URL.createObjectURL(file) }; } return res; } catch (err) { console.error(Canvas to temp file failed:, err); } }4.10 实时日志丢失wx.getRealtimeLogManager的采样率陷阱现象开启实时日志后只收到10%的日志。根因微信默认采样率是10%需手动设为100%。修复在app.js里const log wx.getRealtimeLogManager({ level: 3 }); log.setFilterMsg(); // 清空过滤 log.setSampling(100); // 设为100%4.11 分包体积超标subNVue包体超过2MB的压缩方案现象分包subNVue/目录超过2MB上传失败。根因微信对分包体积有硬性限制且不计算CDN压缩。解决方案删除所有未使用的图片资源用wx.loadSubNVue按需加载JS代码启用UglifyJS压缩微信开发者工具自带对大JSON数据改用二进制ArrayBuffer存储体积减少40%最狠一招把分包JS文件用wx.downloadFile动态下载存入wx.getFileSystemManager().writeFile绕过分包体积限制。4.12 审核驳回话术如何用技术语言说服审核员当审核驳回理由是“玩法同质化”时不要争辩“我们不一样”而是提供技术证据附上game.json的description字段截图证明语义重构提供src/logic/目录下核心算法文件的代码行数统计如“消除逻辑”仅12行而“区域校准逻辑”达217行用git diff生成关卡数据文件对比证明所有关卡布局为手工设计非算法生成。5. 20天时间线还原每天做什么为什么这么做把20天拆解成可复用的时间单元不是按“第1天学Cursor”而是按“第1天解决什么问题”。这才是真实项目节奏。5.1 第1-3天建立可验证的最小闭环目标让“Hello World”能在真机上运行且能被监控。行动Day1安装Cursor配置Codex用官方CLI避开代理问题创建空微信小游戏项目Day2写第一个Canvas绘制逻辑用wx.createSelectorQuery获取Canvas确保真机vConsole能看到绘制结果Day3接入wx.getRealtimeLogManager配置100%采样率验证日志能实时上报到腾讯云。为什么不追求功能只追求“可观测性”。如果连日志都收不到后面所有优化都是空中楼阁。5.2 第4-7天确定技术栈生死线目标找出微信小游戏的绝对禁忌划出开发红线。行动Day4测试React/Vue/Svelte可行性确认全部失败选定CanvasDOM混合方案Day5压测本地存储确认单key 1MB限制设计分片存储方案Day6真机测试所有音频API确认wx.createInnerAudioContext的iOS限制Day7打包测试确认WebGL模板修改点建立Unity导出标准流程。为什么这四天决定项目生死。如果选错技术栈后面16天全是返工。5.3 第8-12天核心玩法MVP开发目标用最少代码实现可玩的核心循环。行动Day8用Canvas绘制游戏主界面实现触摸拖拽逻辑Day9接入微信登录实现token持久化设计离线模式Day10实现关卡数据加载用JSON配置关卡避免硬编码Day11添加音效反馈用Web Audio API生成合成音Day12完成首关进行真机压力测试连续玩30分钟监控内存泄漏。为什么MVP不是“能跑就行”而是“能暴露所有核心问题”。首关必须包含登录、存储、渲染、音效、网络如分享否则问题会堆积到后期。5.4 第13-15天合规与审核攻坚目标让代码符合微信审核规则且能解释清楚。行动Day13重写所有UI文案替换“消除”“通关”等敏感词Day14准备《玩法差异对比表》录制真机演示视频Day15提交审核同步搭建监控告警准备应对驳回。为什么审核不是技术问题是沟通问题。提前准备好技术证据比临时解释高效十倍。5.5 第16-20天上线与防御性迭代目标让上线后的问题可定位、可修复。行动Day16上线灰度5%用户监控错误率Day17根据真机日志修复华为机型Canvas问题Day18优化分包加载首屏时间从1.2秒降到0.6秒Day19添加用户反馈入口收集体验问题Day20发布正式版同步更新GitHub文档写这篇复盘。为什么上线不是终点而是观测起点。真正的开发从用户真实环境开始。6. 关于Cursor和Codex的冷思考工具永远在进化人必须更清醒最后想说点扎心的这20天里我花最多时间的不是写代码而是理解微信小游戏这个平台的脾气。Codex能告诉我wx.request的参数怎么写但它不会告诉我为什么在iOS微信8.0.50里wx.getNetworkType会返回unknownCursor能高亮语法错误但它不会提醒我wx.setStorageSync在存储大对象时实际占用的磁盘空间是JSON.stringify(obj).length * 1.3。工具链的价值从来不是替代人的判断而是把人从“查文档-试错-再查”这种低熵劳动里解放出来让人专注在更高维的问题上比如当用户在第3关卡住时是关卡设计太难还是提示文案不够清晰当分享率低于5%时是按钮位置问题还是激励机制没到位我现在的Cursor配置里有一个永久置顶的笔记标题叫《微信小游戏已知缺陷清单》里面记着iOS端Canvas抗锯齿失效已修复华为Mate50 Pro Canvas转图失败降级方案微信8.0.50网络类型识别异常改用wx.onNetworkStatusChange这个清单比任何Codex提示词都重要。因为它不是AI生成的而是我亲手在真机上一行行验证出来的。它提醒我所有工具的终点都是让人更接近真实世界而不是更远离它。如果你正打算用类似工具做微信小游戏我的建议只有一条第一天别急着写代码。先买一台iPhone、一台华为、一台小米连上vConsole把微信开发者工具的“真机调试”功能调通。然后对着文档把每一个API在三台机器上各跑三遍。这个过程枯燥、缓慢、甚至有点笨但它会让你在第20天上线时心里有底。