1. 零基础在 Cursor 里跑通 React Native 跨端 App 的模型调用
你可能刚装好 Cursor,听说它能写代码、能对话,但真到要做一个 iOS、安卓、小程序、网页都能跑的跨端 App 时,第一步就卡住了:模型调用怎么配?Key 放哪?Base URL 填什么?我当初也是这么过来的,所以这篇就把「0 经验用 Cursor 开发跨端 App」这件事拆成能照着敲的步骤,重点解决 React Native 项目里接入统一 Key/API 通道的问题。
先说清楚这篇适合谁:完全没写过 React Native、但想用 Cursor 辅助生成代码的开发者;手里有一个模型 API Key,却不知道在跨端项目里怎么安全配置的人;以及想用一个统一入口同时给 iOS、安卓、Web 端提供 AI 能力的新手。核心检索词就是「Cursor 开发跨端 App」「React Native 接入模型 API」「TaoToken 统一 Key 配置」,这三个词会贯穿全文。
React Native 本身是一套用 JavaScript/TypeScript 写一次、编译到多端的框架,iOS、安卓、Web 都能覆盖,小程序则需要额外适配层。它的好处是生态成熟、Cursor 对它的代码补全和报错解释都很友好。而模型调用这块,跨端项目最容易踩的坑是:Key 硬编码在客户端、不同端请求地址不一致、环境变量在打包时丢失。所以我会用一个统一的 API 通道来收口这些配置,让 Cursor 生成的代码只关心业务逻辑,不关心底层走哪个模型。
下面从项目创建开始,一步步走到「发一个请求、拿到模型回复」的完整验证。你不需要提前懂 React Native,跟着敲就行。
2. TaoToken 前置准备:拿到统一 Key 和 Base URL
在写任何代码之前,先把「钥匙」准备好。TaoToken 在这里扮演的角色是一个统一的模型调用入口,你只需要一个 Key 和一个 Base URL,就能在 React Native 项目里发起对话请求,不用为每个模型单独记地址。官网入口是 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 根地址是 https://taotoken.net/api ,注意这个 API 地址后面不加任何查询参数。
第一步,打开控制台创建 Key。进入 https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite ,登录后在 API Keys 页面点新建,复制那串以 sk- 开头的字符串。这个 Key 只显示一次,建议先粘到本地一个临时文本里,等会儿要写进环境变量文件。如果你还没决定用哪个模型,可以先去模型对话页 https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_content=model_chat&utm_campaign=rewrite 试一句,确认通道是通的,再回到项目里配置。
第二步,确认你要用的 Model ID。跨端 App 的 demo 阶段,建议选一个响应快、成本低的对话模型,把它的准确 ID 记下来,比如常见的对话模型 ID 格式是厂商名/模型名。这个 ID 后面会写进请求体,写错了会直接报模型不存在。你可以在接入文档 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite 里查到当前支持的模型列表和对应的 ID 写法。
第三步,理解三个核心参数的关系。Base URL 决定请求发到哪,Key 决定你有没有权限,Model ID 决定用哪个模型。这三者在 React Native 里最好通过环境变量注入,而不是写死在代码里。原因很直接:跨端项目会打包成多个产物,硬编码的 Key 一旦进了客户端包,等于公开泄露。用环境变量 + 构建时注入,至少能让 Key 不进版本库。
这里有个新手常问的点:为什么不用一个固定的请求地址,非要搞 Base URL?因为统一通道的价值就在于,你换模型时只改 Model ID,地址和鉴权方式不变。Cursor 生成的代码里,请求函数只认这三个变量,业务层完全无感。这也是后面配置片段要严格对齐路径的原因。
准备好这三样东西,就可以进 Cursor 建项目了。如果你打算长期做编码类或 Agent 类项目,也可以顺手了解一下 Coding Plan https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding_plan&utm_campaign=rewrite ,它在额度使用上对持续开发更友好,demo 阶段先用按量 Key 即可。
3. 可复制配置:Cursor 里创建 React Native 项目并写入环境变量
现在进 Cursor,打开一个空文件夹作为工作区。在终端里执行 React Native 官方脚手架命令创建项目:
npx @react-native-community/cli init mobileApp这一步会拉取模板并安装依赖,网络慢的话耐心等。完成后进入项目目录并启动 Metro 打包服务:
cd mobileApp && npx react-native start启动后 Cursor 通常会提示你安装 Xcode 并下载 iOS 模拟器,按提示装完即可。模拟器就绪后,另开一个终端安装项目依赖:
cd mobileApp && npm install接下来是这篇最关键的部分:环境变量配置。React Native 默认不读取 .env 文件,需要借助 react-native-dotenv 或 babel 插件。为了 demo 简单,我用一个 config 文件加环境变量模板的方式,既能被 Cursor 识别,又方便你替换。先在项目根目录创建.env文件:
TAOTOKEN_BASE_URL=https://taotoken.net/api TAOTOKEN_API_KEY=sk-你的真实Key TAOTOKEN_MODEL_ID=你的模型ID注意.env必须加进.gitignore,否则 Key 会跟着代码提交上去。然后在项目根目录创建src/config/env.ts,把环境变量收口成一个对象:
// src/config/env.ts export const ENV = { baseUrl: process.env.TAOTOKEN_BASE_URL ?? 'https://taotoken.net/api', apiKey: process.env.TAOTOKEN_API_KEY ?? '', modelId: process.env.TAOTOKEN_MODEL_ID ?? '', };如果你用的是 TypeScript,还需要在babel.config.js里加上 dotenv 插件,让打包时能读到 .env:
// babel.config.js module.exports = { presets: ['module:@react-native/babel-preset'], plugins: [ ['module:react-native-dotenv', { moduleName: '@env', path: '.env', safe: false, allowUndefined: true, }], ], };装插件:
npm install react-native-dotenv --save-dev装完后把env.ts改成从@env导入,这样类型提示更准:
// src/config/env.ts import { TAOTOKEN_BASE_URL, TAOTOKEN_API_KEY, TAOTOKEN_MODEL_ID } from '@env'; export const ENV = { baseUrl: TAOTOKEN_BASE_URL, apiKey: TAOTOKEN_API_KEY, modelId: TAOTOKEN_MODEL_ID, };到这里,三个核心参数就通过环境变量注入到项目里了。Cursor 在补全ENV.时会自动提示这三个字段,你不需要记具体值。这一步做完,跨端项目的配置层就算收口了,后面写请求函数只引用 ENV,不出现任何硬编码字符串。
4. 验证请求:从发出到拿到模型响应的完整动作
配置写好了,得验证它真的能跑通。我在src/api/chat.ts里写一个最小的请求函数,用 fetch 发到统一通道:
// src/api/chat.ts import { ENV } from '../config/env'; export async function chatOnce(userText: string): Promise<string> { const res = await fetch(`${ENV.baseUrl}/v1/chat/completions`, { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${ENV.apiKey}`, }, body: JSON.stringify({ model: ENV.modelId, messages: [{ role: 'user', content: userText }], }), }); if (!res.ok) { const errText = await res.text(); throw new Error(`请求失败 ${res.status}: ${errText}`); } const data = await res.json(); return data.choices?.[0]?.message?.content ?? ''; }然后在 App 入口加一个按钮触发它,方便在模拟器里点一下看结果:
// App.tsx 片段 import React, { useState } from 'react'; import { View, Button, Text } from 'react-native'; import { chatOnce } from './src/api/chat'; export default function App() { const [reply, setReply] = useState('等待请求...'); const onPress = async () => { try { const text = await chatOnce('用一句话介绍 React Native'); setReply(text); } catch (e: any) { setReply(e.message); } }; return ( <View style={{ padding: 24 }}> <Button title="发一个请求" onPress={onPress} /> <Text style={{ marginTop: 16 }}>{reply}</Text> </View> ); }保存后回到模拟器,点「发一个请求」。如果配置正确,几秒内你会看到模型返回的一句话,比如「React Native 是一套用 JavaScript 构建原生移动应用的框架」。这就是一次完整的「请求发出 → 通道鉴权 → 模型响应 → 前端渲染」链路。
验证时重点看三件事:状态码是不是 200,返回体里有没有choices数组,message.content是不是非空字符串。如果返回的是 401,说明 Key 没读到或写错了;如果报模型不存在,说明 Model ID 不对;如果 fetch 直接抛网络错误,检查 Base URL 是不是写成了带斜杠结尾或带了多余路径。这个验证动作做完,你的跨端 App 就已经具备 AI 能力了,iOS、安卓、Web 端共用同一套请求逻辑。
5. 本篇常见错排查:401、local proxy failed、reading choices 怎么解
新手在这一步最容易撞几个固定报错,我按真实遇到的顺序列出来,对照着改就行。
第一个是 401 Unauthorized。报错原文通常是{"error":{"message":"Invalid API key"}}。原因基本是环境变量没生效:要么.env没被 babel 插件读到,要么 Key 复制时带了空格。排查方法是先在chat.ts里临时打印ENV.apiKey?.slice(0, 6),看前几位是不是 sk- 开头。如果打印出来是 undefined,说明 dotenv 插件没配好,回到第 3 节检查babel.config.js的 plugins 数组,改完必须重启 Metro,因为 babel 配置变更不会热更新。
第二个是local proxy failed或类似的连接失败提示。这通常出现在你本地网络环境对请求地址做了拦截,或者 Base URL 写成了https://taotoken.net/api/(多了结尾斜杠)导致路径拼接成//v1/...。把 Base URL 严格写成https://taotoken.net/api,不要加尾斜杠,也不要在后面手动拼/v1以外的路径。请求函数里${ENV.baseUrl}/v1/chat/completions这种拼法是标准写法,照抄即可。
第三个是Cannot read property 'choices' of undefined或reading 'choices'。这说明请求返回了,但返回体结构和你预期的不一样。常见原因是 Model ID 写错,通道返回了一个错误对象而不是正常的对话结构。解决办法是先把res.json()的结果完整打印出来,看里面有没有error字段。如果有,按 error.message 提示改 Model ID;如果没有 error 但也没有 choices,检查请求体里messages的格式是不是标准数组。
第四个是 OAuth 相关报错,比如OAuth token expired或unauthorized_client。这类一般出现在你误用了需要 OAuth 流程的接入方式,而 demo 阶段应该用 API Key 直连。确认你用的是Authorization: Bearer sk-xxx这种头,而不是 OAuth 的 access_token 流程。如果你在 Cursor 里配置过 Claude Code 或 Codex 的 auth.json,注意那是编辑器侧的配置,和 React Native 项目里的请求是两回事,不要混用。
第五个是打包后真机运行报 Key 为空。这是因为.env只在开发时被 babel 读取,生产打包需要额外注入。demo 阶段先在模拟器验证逻辑,等要出包时再把环境变量通过 CI 或构建脚本注入。记住一个原则:任何情况下都不要把 Key 写进源码提交到仓库。
排查完这几个,你的请求链路基本就稳了。如果还想确认模型侧是否正常,可以回到模型对话页手动发一句,对比返回结构,这样能快速区分是项目配置问题还是通道问题。
6. 继续往下走:把统一 Key 用在更多跨端场景
跑通第一个请求之后,你会发现这套配置的扩展性很好。比如你要加一个「地址搜索」功能,只需要在chatOnce基础上包一层业务函数,把用户输入拼进 messages,Model ID 和 Base URL 完全不用动。跨端的好处也在这里体现:同一份src/api/chat.ts在 iOS、安卓、Web 端都能跑,小程序端只需要把 fetch 换成对应的请求适配层,配置层保持不变。
如果你打算把这个 demo 继续做成真正的项目,建议下一步做两件事:一是把请求函数抽成带重试和超时的版本,避免网络抖动导致体验差;二是把 Model ID 做成可切换的配置,方便在不同场景下换模型。这些改动都不影响你已经配好的统一 Key 通道。
需要查更多模型 ID 或接入细节时,接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite ,API Key 管理在 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite 。如果你后面要接 Claude Code 这类编码工具,可以参考 https://taotoken.net/claude-code-anthropic?utm_source=taotoken_aicg_blog_end&utm_content=claudecode&utm_campaign=rewrite 里的配置说明,思路和这篇一致:Base URL、Key、Model ID 三件套对齐,剩下的交给工具。
最后留一个我踩过的坑:改完.env或 babel 配置后,一定要停掉 Metro 再重启,否则旧的环境变量会一直缓存着,你会以为配置没生效,其实是进程没刷新。这个细节能帮你省下不少排查时间。