Cursor 拆 game.js 到 UI.js 报错:改走 TaoToken 通道再排引用?
用 Cursor 把数独小程序的 game.js 拆到 UI.js最容易出现的结局不是「拆成功」而是一片引用报错函数找不到、this 指向丢了、页面 bindtap 指向的函数不在 game.js 里了。撤回和提示这两个功能刚写完就卡在这一步最后回滚版本收场——这不是 AI 不会写代码而是模型只负责「搬函数」不负责「追引用」。想让它把引用关系一并理清楚前提是对话通道别中途掉线先去 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_end 创建一把 Key再把 Cursor 的模型通道指到 TaoToken配通之后再谈拆文件顺序反了就是白折腾。这篇文章不谈「AI 到底能不能写小程序」只谈一件很具体的事当 game.js 里的 UI 渲染部分要拆到 UI.jsCursor 把函数挪过去之后引用对不上怎么一步步查、怎么一步步挪回来。我会把通道配置放在前面因为很多人的第一反应是反复重试、换提示词、甚至让 Cursor「全部重写一遍」结果越改越乱——其实换一条稳定通道再让它先输出引用关系问题会小很多。1. 数独小程序拆 UI.js 之后报错到底红在哪几行1.1 三类最常见的小程序报错先认脸微信小程序的报错信息其实很有规律拆文件之后基本跑不出这三类第一类是xxx is not defined或Cannot read property xxx of undefined。典型场景是原来game.js里有个renderBoard()Page({...})内部直接调用拆到UI.js后game.js里还在调renderBoard()但这个函数已经不在当前作用域了。第二类是模块导入报错比如module utils/UI.js is not defined或者路径写错。小程序对相对路径和大小写都敏感UI.js和ui.js在两个平台上可能表现不一样。第三类是运行时才炸的this.setData is not a function。原来函数挂在Page上下文里this天然指向页面实例挪到UI.js后this变成了模块对象setData自然就没了。1.2 Cursor 只搬函数体不会自动追引用这一点必须说透。你在 Cursor 里选中game.js的一段 UI 代码说「把这段拆到 UI.js」模型看到的上下文通常只有选中的那几十行。它会把函数体搬过去但不会主动去搜这个函数被谁调用、页面 wxss 里有没有引用、onLoad里注册的回调是不是还指向旧位置。所以最后的结果往往是「搬了 70%」。game.js里留下半截调用UI.js里躺着一堆没人引用的函数编译能过但点一下就报错。回滚版本不是失败是你终于意识到这个活儿需要按引用清单来做而不是按函数块来做。2. 让 Cursor 继续改代码之前先把模型通道换成 TaoToken2.1 打开官网创建一把专属 API Key原文里作者是直接打开 Cursor 就开始对话改代码的仿写到这里要先把通道这一步补上打开 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_end 注册账号进控制台创建一把 API Key。Key 只显示有限次数建议建好就存进密码管理器别散落在聊天记录里。这里要说清楚 TaoToken 在整件事里的角色它给 Cursor 提供的是 Key 和 Base URL让这个很吃 Token 的编程工具能稳定调模型。它不负责修你的函数引用引用问题还是得靠对话一步步排。把工具定位摆正后面不容易期待错。2.2 Cursor 自定义 OpenAI 兼容模型字段就这么填Cursor 的设置面板里能挂自定义的 OpenAI 兼容模型。打开设置找到 Models 区域展开 OpenAI 相关的配置项按下面的方式填配置项填什么API KeyYOUR_API_KEY也就是你在 TaoToken 控制台创建的那把Override OpenAI Base URLhttps://taotoken.net/api模型 ID以 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_end 模型广场当时的列表为准Base URL 这一栏最容易填错末尾不要加/v1也不要往上面拼任何查询参数。填成https://taotoken.net/api/v1大概率会返回 404填成带 UTM 的地址则完全是两码事——落地页是给人点的接口地址才是给工具用的。2.3 模型 ID 别自己猜去模型广场抄很多人在这里翻车习惯性地写一个听起来很厉害的模型名结果请求直接报「model not found」。正确的做法是回到 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_end 的模型广场把当前可用的模型 ID 原样复制粘进 Cursor 的模型列表。填完之后在 Cursor 里发一句最简单的对话测试比如「用一句话说明这段代码在做什么」。能正常回说明通道通了报 401 就是 Key 复制时带了空格报 404 基本就是 Base URL 多写了路径。这两个错排掉再进入改代码的环节。3. 先让 Cursor 输出 game.js 与 UI.js 的引用关系清单3.1 只读不改第一轮对话只要一张表通道通了的第一个动作不是让它继续拆而是让它只读。提示词可以这样写先不要修改任何文件。请通读 game.js 和将要新建的 UI.js 的目标范围列出1每个准备迁移的函数名2它在 game.js 里被哪些地方调用3它内部是否用到 this.setData 或 this.data4迁移到 UI.js 后这些调用点需要怎么改。用表格输出。关键在「先不要修改任何文件」这半句。模型在没有写权限压力的情况下更容易老老实实做检索而不是急着给你一份看起来完整、其实引用全断的重构。3.2 自己再核一遍 wxml、json 和 bindtap模型给的清单不能全信尤其是小程序里这些「不在 js 里的引用」index.wxml里的bindtaponCellTap指向的函数必须在页面对象上不能只存在于 UI.jsindex.json里若配置了自定义组件组件路径别写错wx.开头的 API 调用比如wx.showToast属于全局对象搬到哪里都能用这类不用管getApp()取全局数据的地方拆完要确认取到的还是同一个实例。把这几项和 Cursor 给的表格对照一遍你会发现真正需要小心处理的函数往往只占迁移总量的三成。剩下七成是纯计算、纯格式化挪走几乎不会出事。4. 一次只搬一组game.js 到 UI.js 的迁移顺序4.1 先搬不碰 this 的纯函数第一步挑最安全的生成棋盘数组、计算某个九宫格、把数字转成展示文案这一类。它们不依赖页面实例不调setData挪进 UI.js 之后用module.exports或export暴露出来在 game.js 顶部 require 进来就能用。这一批搬完立刻编译一次。小程序开发者工具的编译很快红了就当场看是哪一行。别攒着五六个文件一起改那样出错你根本定位不到是哪个函数。4.2 再搬 setData 渲染函数把 this 显式传进去第二批是渲染类函数它们原本依赖页面上下文。推荐的处理方式不是硬绑this而是把页面实例当参数传// UI.js function renderBoard(page, board) { page.setData({ cells: formatCells(board) }) } module.exports { renderBoard }// game.js const UI require(./UI.js) Page({ refresh() { UI.renderBoard(this, this.data.board) } })这样写的好处是职责清晰UI.js 只管怎么画game.js 只管什么时候画。this不再跨文件漂移setData is not a function这类报错基本就消失了。4.3 撤回和提示这两个功能的引用要单独盯撤回和提示是这篇里刚加完的功能也是最容易在拆分中受伤的部分因为它们同时牵扯数据变更和界面刷新。迁移时注意两点一是历史栈这种状态数据继续留在 game.jsUI.js 只负责展示二是wx.showToast、按钮置灰这类交互反馈要么统一收在 UI.js要么统一留在 game.js别一半一半。判断标准很简单如果某个函数里既有this.data.history.push(...)又有this.setData(...)那它就不该整体搬走而是拆成「改数据」和「刷界面」两段。5. 拆分报错对照红哪一行就查哪一类5.1 路径、大小写、require 与 export 混用小程序的 require 路径是相对当前文件的。game.js和UI.js同目录写require(./UI.js)如果 UI.js 放在utils/下就得写require(./utils/UI.js)。文件名大小写也要一致本地能跑不代表真机没问题。另一个高频坑是导出方式混用一边用module.exports {...}另一边用export default {...}然后require拿到的对象结构和你以为的不一样报错提示往往还很含糊。整个项目统一一种写法最省事。5.2 循环依赖和「搬了一半」的中间状态game.js require UI.jsUI.js 又 require game.js 里的某个常量——这就形成了循环依赖小程序里表现为某个模块在初始化时是空对象。解决办法是把共用常量抽到独立的constants.js两边都从那里拿。还有一种报错不是代码错而是中间状态错函数已经复制到 UI.js但 game.js 里的原函数还没删导致两份实现并存、行为不一致。每次迁移都以「复制 → 改调用 → 删除原函数 → 编译」为一个完整循环别停在中间。6. 引用理顺之后回控制台对一下这次调用6.1 用同一把 Key 做一次端到端确认代码跑通、撤回和提示都还在说明这次拆分真的落地了。这时候可以拿刚才那把 Key在 TaoToken 模型对话 里发一条消息确认通道仍然是通的——毕竟排障过程中你很可能反复改过 Cursor 的模型设置。如果准备长期用 AI 写小程序Token 消耗会比想象中快一次重构对话动辄几万 Token可以顺手看看 Coding Plan 的套餐档位是否合适。需要再开一把 Key 给别的工具用就去 控制台 API Keys 创建想对照着把通道配到其他执行工具上Claude Code 接入文档 里的环境变量写法可以直接参考注意填进工具的 Base URL 始终是https://taotoken.net/api末尾不带/v1。回看整件事拆分失败的核心从来不是模型不够聪明而是我们把「搬函数」当成了「重构」。先配通通道再让它只读列引用最后按依赖顺序一组一组挪、每挪一组编译一次——这套流程比任何一次「帮我全部重写」都靠谱。数独这种体量的小程序尚且如此再大一点的项目引用清单只会更重要。