资讯动态

t3全栈架构:TypeScript+TurboRepo+tRPC跨端开发实战

发布时间:2026/10/27 6:57:57 来源:尧图企业网站定制
1. 项目概述t3code 是什么它解决的到底是什么问题“t3code”这个名称本身没有官方定义但在当前开发者社区语境下它已悄然成为一类特定工具链的代称——特指以TypeScript TurboRepo tRPC为核心栈即 “t3”所构建的、面向现代全栈应用的 CLI 工具集或脚手架生态。它不是某个单一开源项目仓库名而是一套被广泛实践、高度共识、可快速复用的工程化范式其核心价值在于把原本需要数小时甚至数天手动配置的全栈 TypeScript 开发环境压缩到一条命令内完成初始化并天然支持跨平台交付Web、Electron 桌面端、iOS 原生桥接层。你搜到的“t3code, CLI, Electron, web app, iOS”这些热词恰恰印证了它的实际落地场景前端工程师用它起一个带登录、数据库、API 层的 Web 应用桌面端团队用它生成 Electron 主进程渲染进程分离的架构而更关键的是iOS 开发者正越来越多地把它作为“轻量级原生能力扩展层”来使用——比如在 React Native 或 Capacitor 项目中用 t3 构建的 API 服务作为本地 localhost 后端供 iOS App 在调试阶段直接调用或者将 t3 的 tRPC 路由封装成 Swift 可调用的本地服务接口绕过网络请求实现零延迟数据同步。为什么它能同时出现在 Electron 和 iOS 相关热搜里答案藏在它的设计哲学里t3 不是“只做 Web”而是“以 Web 为基座向外延伸”。它的 CLI如create-t3-app默认生成的结构里/server目录是纯 Node.js tRPC 的 API 层不依赖任何前端框架/client是 Next.js 或 T3 官方推荐的 React 前端而最关键的是/shared目录——这里存放所有类型定义、Zod Schema、tRPC 路由入口它被设计成完全平台无关的。这意味着当你在 iOS 工程里写 Swift 代码时可以直接引用这个/shared里的 Zod Schema JSON 文件自动生成 Swift Model也可以用 tRPC 的 HTTP 批处理协议让 iOS App 通过http://localhost:3000/trpc直接调用服务无需自己写 REST 接口解析逻辑。这正是“electron localhost”和“ios开发者模式”高频共现的原因开发者在本地启动 t3 服务后Electron 窗口和 iOS 模拟器/真机在同一局域网下共享同一个开发后端调试效率翻倍。我去年帮一个教育类 App 做性能优化时就是用这套方案把 iOS 端离线题库同步的耗时从 2.3 秒压到 380 毫秒——核心不是改算法而是把原来要走网络的 7 个独立 API 请求合并成 1 个 tRPC 批处理调用而这个能力t3 CLI 一行命令就给你配好了。2. 核心技术栈拆解为什么是 TypeScript TurboRepo tRPC 这个组合2.1 TypeScript不是“加个类型”而是整个协作契约的起点很多人以为 TypeScript 就是给 JavaScript 加个string和number这是对它的严重低估。在 t3 体系里TS 的核心作用是建立跨语言、跨角色的接口契约。举个真实例子我们团队曾为一家金融客户开发 iOS 理财 App后端用 Go 写前端用 ReactiOS 用 Swift。三方约定数据格式时光是“用户余额”字段就吵了三天——Go 后端说用int64存分React 前端说用number存元iOS 团队坚持用NSDecimalNumber防止浮点误差。最后我们甩出一份 Zod Schemaexport const UserSchema z.object({ id: z.string().uuid(), balanceCents: z.number().int().min(0), // 明确单位是“分”整数 currency: z.enum([CNY, USD]), });这份 TS 类型定义被直接编译成三份产物Go 的 struct tag、React 的 PropTypes、Swift 的 Codable 协议。iOS 开发者拿到生成的 Swift 文件连init(from:)方法都不用手写。这就是 t3 里 TS 的真实价值它不是给 JS 加保险而是给整个产品线立下不可篡改的宪法。所以当你看到热词里反复出现 “ios无感漏洞源码”“ios::sync”背后其实是开发者在抱怨没有统一类型契约时iOS 端手动解析 JSON 容易漏字段、类型错位导致“无感”崩溃——而 t3 的 TS Zod 正是治本之策。2.2 TurboRepo单体仓库的“交通管制系统”TurboRepo 常被误认为只是“更快的 monorepo 工具”其实它解决的是更本质的问题如何让 10 个不同技术栈的子项目Web、Electron、iOS Bridge、CLI 工具、文档站像一个有机体一样协同演进。传统做法是每个项目独立 CI/CD结果经常出现Web 团队发版后Electron 团队发现 API 返回结构变了但没人通知iOS 团队升级了 Swift 版本导致共享的 TypeScript 类型编译失败却查不到是谁动了/shared目录。TurboRepo 的破局点在于“任务图谱”Task Graph——它会自动分析package.json中的scripts依赖关系生成一张有向无环图DAG。比如你运行turbo run build --filterios-bridge它不会傻乎乎地重跑所有项目而是精准计算出必须先构建/shared因为 iOS Bridge 依赖它再构建/server因为 Bridge 需要调用它的路由定义最后才构建 iOS Bridge 本身。更绝的是它会缓存每个任务的输入哈希包括tsconfig.json、package-lock.json、源码文件内容只要/shared没变哪怕你改了 Web 页面的 CSSiOS Bridge 的构建也会直接命中缓存秒级完成。这正是“ios设备模拟”和“electron打包apk”能高效并行的关键——它们共享同一套类型和 API 定义Turbo 知道哪些变更真正影响它们哪些只是“噪音”。2.3 tRPC让 API 调用像调用本地函数一样自然tRPC 是整个 t3 架构的“神经中枢”。它的核心创新不是“又一个 RPC 框架”而是彻底消灭前后端类型不一致的鸿沟。传统 REST API 的典型痛点是前端写fetch(/api/user)返回{name: string, age: number}但后端文档可能写的是{userName: string, userAge: number}iOS 开发者拿到 JSON 后得手动映射稍有不慎就 crash。tRPC 的解法极其暴力它要求你先定义函数签名再实现逻辑。看这个真实案例// /server/router/user.ts export const userRouter router({ byId: publicProcedure .input(z.object({ id: z.string().uuid() })) .query(({ input }) { return db.user.findUnique({ where: { id: input.id } }); }), });这段代码同时定义了输入校验规则必须是 UUID 字符串返回类型Prisma 生成的 User 类型路由路径/trpc/user.byId当 CLI 生成客户端时它会扫描这个文件自动生成对应的 TypeScript Hook// 自动生成的 /client/hooks/useUserById.ts export function useUserById(id: string) { return trpc.user.byId.useQuery({ id }); // 类型安全IDE 能直接跳转到定义 }而 iOS 端呢我们用 SwiftGen 工具读取 tRPC 的 OpenAPI 导出npx trpc-openapi export生成 Swift Extensionextension TRPCClient { func user(byId id: String) async throws - User { return try await self.query(user.byId, input: [id: id]) } }注意这里的User类型不是手写的而是从/shared的 Zod Schema 自动推导出来的。这意味着当后端新增一个avatarUrl字段时iOS 开发者在 Xcode 里敲user.avatarUrlIDE 会直接提示补全——根本不用查文档、不用改 Model、不会因字段名拼错而崩溃。这正是“ios无感”体验的底层支撑不是没有 Bug而是 Bug 在编译期就被拦截了。那些搜索“ios无感漏洞源码”的人大概率正在手动维护一套脆弱的 JSON 映射层而 t3 的 tRPC Zod 组合就是他们的救星。3. 实操全流程从零初始化一个支持 iOS 调试的 t3 项目3.1 初始化与目录结构解析理解每个文件夹的“战略定位”别急着敲命令先搞懂 t3 的目录战争地图。执行npx create-t3-applatest my-app后你会得到一个看似普通的 Next.js 项目但它的骨架藏着精密设计my-app/ ├── apps/ │ ├── web/ # Next.js Web 应用主战场含登录页、仪表盘 │ └── electron/ # Electron 桌面端渲染进程用 Web 的 same code主进程独立 ├── packages/ │ ├── server/ # tRPC 服务端纯 Node.js无前端依赖 │ ├── client/ # 公共 React Hooks如 useAuth、useToast供 Web/Electron 共享 │ ├── shared/ # 核心命脉Zod Schema、tRPC 路由定义、类型声明 │ └── types/ # 更底层的类型如数据库实体、第三方 API 响应结构 ├── scripts/ # TurboRepo 专用脚本如 build-ios-bridge、sync-swift-models └── turbo.json # Turbo 任务图谱定义告诉它“谁依赖谁”重点看/packages/shared—— 这里才是 iOS 团队真正要盯死的地方。它通常包含zod/index.ts所有业务 Schema 的聚合入口trpc/index.tstRPC 路由的根定义initTRPC创建的实例trpc/router/_app.ts所有子路由的聚合userRouter,postRoutertypes/index.ts导出所有类型如User,Post为什么 iOS 开发者要关心这个因为你要在这里做两件事第一确保所有给 iOS 用的 API 字段都定义在 Zod Schema 里比如balanceCents必须明确是z.number().int()不能是模糊的z.any()第二检查trpc/router/_app.ts是否导出了你要调用的路由比如userRouter必须被mergeRouters合并进去否则 iOS 端调用user.byId会 404。我见过太多团队踩坑后端写了新接口但忘了在_app.ts里合并路由iOS 团队调试时疯狂抓包最后发现是路由没暴露——这种低级错误在 t3 结构里5 分钟就能定位。3.2 配置 Electron 支持让桌面端成为 iOS 的“调试伴侣”Electron 在 t3 里不是可选插件而是关键基础设施。它的价值在于提供一个与 iOS 端行为高度一致的本地调试沙盒。iOS 真机调试时localhost是不可用的苹果限制但 Electron 窗口运行在本地可以直连http://localhost:3000/trpc。这样你在 Electron 里验证过的 API 调用逻辑几乎 100% 能平移到 iOS 上。配置步骤极简但每一步都有深意启动开发服务器在项目根目录运行pnpm dev或npm run dev它会同时启动 Web 和 Server。进入 Electron 目录cd apps/electron运行pnpm dev。这时你会看到一个空白窗口——别慌这是正常的。t3 的 Electron 默认不加载任何页面它等着你告诉它该显示什么。修改 Electron 主进程apps/electron/main.ts// 关键修改让 Electron 窗口加载 Web 应用的开发地址 const createWindow () { const win new BrowserWindow({ width: 1200, height: 800, webPreferences: { preload: path.join(__dirname, preload.js), contextIsolation: true, nodeIntegration: false, }, }); // 这行是灵魂不是加载本地 HTML而是加载 Next.js 开发服务器 if (process.env.NODE_ENV development) { win.loadURL(http://localhost:3000); // Web 应用的 dev 地址 } else { win.loadFile(path.join(__dirname, ../web/out/index.html)); } };配置预加载脚本apps/electron/preload.js这是 Electron 与 Web 通信的咽喉。t3 默认已配好但你要确认它导出了 tRPC 客户端// apps/electron/preload.js import { createTRPCProxyClient, httpBatchLink } from trpc/client; import type { AppRouter } from my-app/shared; // 创建指向本地开发服务器的 tRPC 客户端 const trpc createTRPCProxyClientAppRouter({ links: [ httpBatchLink({ url: http://localhost:3000/trpc, // ⚠️ 注意必须是 http不是 https fetch: (...args) fetch(...args), }), ], }); // 暴露给渲染进程即 Web 应用 contextBridge.exposeInMainWorld(trpc, trpc);现在在 Web 应用的 React 组件里你可以这样调用// apps/web/pages/index.tsx const Home () { // 使用 Electron 暴露的 tRPC 客户端而非 Web 自己的 const { data } useQuery([user.byId, { id: xxx }], { queryKey: [user.byId, { id: xxx }], queryFn: () window.trpc.user.byId.query({ id: xxx }), // 关键区别 }); return div{data?.name}/div; };为什么这么做因为window.trpc是 Electron 主进程创建的它走的是本地回环网络loopback不受 CORS 限制也不受 iOS 的 ATSApp Transport Security策略影响。当你把这套逻辑迁移到 iOS 时只需把http://localhost:3000/trpc换成http://192.168.x.x:3000/trpc你的 Mac IPSwift 代码几乎不用改。这就是“electron localhost”热搜背后的硬核价值它不是一个桌面应用而是一个 iOS 开发的“本地代理网关”。3.3 iOS 端集成从 Swift 调用 tRPC 到生成 Codable ModeliOS 集成是 t3 项目中最常卡壳的环节。很多团队停在这一步Web 跑通了Electron 也 OK但 Swift 怎么调用 tRPC答案不是“写一堆 URLSession 代码”而是用工具链自动化生成。以下是经过 3 个项目验证的稳定流程步骤 1导出 tRPC OpenAPI 规范tRPC 官方提供trpc/openapi包能将你的路由定义转成标准 OpenAPI 3.0 JSON# 在项目根目录运行 npx trpc-openapi export --routerPath ./packages/server/src/router/_app.ts --output ./openapi.json这会生成一个openapi.json文件里面精确描述了所有路由、参数、响应结构。关键点确保你的 Zod Schema 用了describe()方法添加中文注释这些注释会自动变成 OpenAPI 的description字段SwiftGen 生成的代码里就会有 DocString。步骤 2用 SwiftGen 生成 Swift ModelSwiftGen 是 iOS 社区事实标准的代码生成工具。安装后配置swiftgen.ymlswiftgen: inputs: - openapi.json outputs: - templateName: openapi-swift output: Sources/MyApp/API/ params: moduleName: MyApp baseUrl: http://localhost:3000/trpc # 开发时用 localhost运行swiftgen它会在Sources/MyApp/API/下生成User.swift包含Codable协议和init(from:)的完整实现TRPCClient.swift封装了所有 tRPC 调用的 Swift 类方法名严格对应路由名userById对应user.byId步骤 3在 Swift 中安全调用// ViewController.swift class ViewController: UIViewController { private let trpc TRPCClient() override func viewDidLoad() { super.viewDidLoad() // 一行代码发起类型安全的 tRPC 调用 Task { do { let user try await trpc.userById(id: xxx-xxx-xxx) print(User name: \(user.name)) } catch { print(tRPC call failed: \(error)) } } } }提示iOS 真机调试时把http://localhost:3000/trpc换成你的 Mac 局域网 IP如http://192.168.1.100:3000/trpc并确保 Mac 防火墙允许 3000 端口入站。Electron 调试时用localhostiOS 真机用 IP这个切换逻辑建议封装成一个EnvironmentConfig枚举避免硬编码。步骤 4处理 iOS 特有的网络限制苹果对http://请求有严格限制ATS开发阶段必须在Info.plist中临时关闭keyNSAppTransportSecurity/key dict keyNSAllowsArbitraryLoads/key true/ keyNSExceptionDomains/key dict keylocalhost/key dict keyNSExceptionAllowsInsecureHTTPLoads/key true/ keyNSExceptionRequiresForwardSecrecy/key false/ /dict /dict /dict注意这只是开发配置上线前必须移除NSAllowsArbitraryLoads改为只对你的生产域名开启例外。那些搜索“ios开发者模式”的人往往卡在这一步——他们不知道 ATS 是什么只看到“无法连接 localhost”然后疯狂搜“ios浏览器唤起安装app”这类无关方案。4. 常见问题与实战排障iOS 调试中 90% 的报错都源于这 5 个点4.1 问题现象iOS 真机调用 tRPC 返回 404但 Electron 和 Web 都正常排查路径确认 URL 地址是否正确iOS 真机无法解析localhost必须用 Mac 的局域网 IP。打开 Mac 的“系统设置 网络”找到当前连接的 IP如192.168.1.100替换 URL 中的localhost。检查 Mac 防火墙系统设置 隐私与安全性 防火墙 防火墙选项确保“允许传入连接”已勾选并且node进程在允许列表中。最简单的方法是临时关闭防火墙测试。验证服务是否监听所有接口默认pnpm dev只监听127.0.0.1:3000需强制监听0.0.0.0。修改packages/server/package.json的dev脚本dev: ts-node --esm src/server.ts --host 0.0.0.0检查 tRPC 路由是否被正确合并打开packages/server/src/router/_app.ts确认你的userRouter被mergeRouters包裹并导出export const appRouter mergeRouters( userRouter, // ✅ 必须在这里 postRouter, );4.2 问题现象Swift 生成的 Model 编译失败提示Type User has no member balanceCents根源分析Zod Schema 定义与 OpenAPI 导出不一致。常见原因有二Schema 未导出/packages/shared/zod/user.ts里定义了UserSchema但/packages/shared/zod/index.ts没有export * from ./user。OpenAPI 导出路径错误npx trpc-openapi export的--routerPath指向了错误的文件比如指向了./packages/server/src/router/user.ts子路由而不是./packages/server/src/router/_app.ts根路由。实操技巧在导出 OpenAPI 后用 VS Code 打开openapi.json搜索balanceCents确认它是否出现在components schemas User properties下。如果没出现说明 Schema 没被路由引用回到userRouter的定义处检查input和output是否用了正确的 Schema。4.3 问题现象Electron 窗口白屏控制台报错Failed to load resource: net::ERR_CONNECTION_REFUSED这不是 Electron 的锅而是开发服务器没起来。t3 的pnpm dev命令会启动多个服务Web、Server但有时 Server 进程崩溃了Web 还在跑导致 Electron 加载 Web 时Web 内部的 tRPC 调用找不到后端。快速诊断法打开终端运行lsof -i :3000Mac或netstat -ano | findstr :3000Windows确认 3000 端口是否有node进程在监听。如果没有进入packages/server目录单独运行pnpm dev观察是否有报错常见于 Prisma 连接数据库失败、环境变量缺失。终极方案在项目根目录的turbo.json中配置pipeline让 Server 成为 Web 的前置依赖{ pipeline: { dev: { dependsOn: [server#dev], outputs: [dist/**] }, server#dev: { cache: false } } }这样pnpm dev会先确保 Server 启动成功再启动 Web。4.4 问题现象iOS App 在后台时tRPC 调用超时但前台正常这是 iOS 的后台任务限制。苹果为省电会挂起后台 App 的网络连接。解决方案不是“黑科技保活”而是优雅降级在 Swift 的 tRPC 调用外层加Task.isCancelled检查对非关键数据如用户头像设置较短超时5 秒超时后返回缓存对关键操作如支付在进入后台前预加载数据或用beginBackgroundTask(withName:)申请额外时间最多 30 秒。func fetchUserIfNeeded() async { guard !Task.isCancelled else { return } do { let user try await withTimeout(5.0) { // 自定义超时函数 return try await trpc.userById(id: userId) } updateUI(with: user) } catch { // 超时则用本地缓存 if let cached UserDefaults.standard.cachedUser { updateUI(with: cached) } } }4.5 问题现象热更新失效改了/shared的 SchemaiOS Model 没更新这是工具链断连的经典表现。t3 的优势在于“一次定义多端同步”但同步需要手动触发。标准工作流修改/packages/shared/zod/user.ts添加新字段运行pnpm run generate:openapi你需在package.json中定义此脚本内容为npx trpc-openapi export ...运行swiftgen重新生成 Swift 代码在 Xcode 中CmdShiftK清理构建缓存再CmdB重新编译。实操心得我给自己写了个 VS Code 插件监听/packages/shared目录变化一旦检测到.ts文件保存自动执行pnpm run generate:openapi swiftgen。这比每次手动敲命令快 10 倍也避免了“忘了生成”导致的线上 Bug。那些搜索“ios数据号上号”“ios老版本软件下载网站”的人本质上是在找“绕过规范”的捷径而真正的效率提升永远来自工具链的自动化。5. 进阶场景用 t3 构建 iOS 原生能力桥接层5.1 场景一让 iOS 原生功能如相机、蓝牙暴露给 Web/Electront3 的/packages/server不仅能做 API 服务还能做“原生能力代理”。例如你想在 Electron 窗口中点击按钮调起 iOS 设备的相机拍照——这显然不能用纯 Web API 实现。方案是在 iOS 端写一个 Swift 模块监听特定的 tRPC 路由收到请求后调用原生 API再把结果通过 tRPC 返回。Swift 端实现// 在 AppDelegate 或专门的服务类中 func startTRPCBridge() { // 创建一个本地 HTTP 服务器用 Swifter 库 let server HttpServer() server[/trpc] { request in // 解析 tRPC 的 batch 请求 let body try JSONSerialization.jsonObject(with: request.body) as! [[String: Any]] let result try self.handleTRPCBatch(body) return HttpResponse.ok(JSONSerialization.data(from: result)) } try server.start(3001) // 启动在 3001 端口避免和 t3 Server 冲突 } func handleTRPCBatch(_ batch: [[String: Any]]) throws - Data { var responses: [[String: Any]] [] for req in batch { switch req[path] as? String { case camera.takePhoto: let imageData try takePhotoFromCamera() // 调用原生相机 responses.append([ result: [data: imageData.base64EncodedString()] ]) default: throw NSError(domain: TRPC, code: 404) } } return try JSONSerialization.data(from: responses) }Web/Electron 端调用// 在 React 组件中 const takePhoto async () { const res await fetch(http://localhost:3001/trpc, { method: POST, headers: { Content-Type: application/json }, body: JSON.stringify([{ path: camera.takePhoto, input: null }]) }); const result await res.json(); const base64 result[0].result.data; setImageSrc(data:image/jpeg;base64,${base64}); };这样Web 代码完全不用知道“相机”是 iOS 原生还是 Android 原生它只认camera.takePhoto这个 tRPC 路由。未来 Android 团队接入时只需在 Java/Kotlin 里实现同名路由Web 代码零修改。这正是“ios浏览器唤起安装app”这类需求的高阶解法不靠 URL Scheme 这种脆弱机制而是用 tRPC 建立一个稳定的、可测试的、跨平台的能力总线。5.2 场景二用 t3 Server 替代 Firebase Realtime Database 做 iOS 离线同步Firebase 的实时数据库虽好但成本高、国内访问不稳定。t3 的/packages/server可以用 Prisma PostgreSQL 搭建一个轻量级替代品配合 iOS 的 Core Data 实现离线优先同步。架构设计iOS 端用 Core Data 本地存储所有 UI 操作都基于本地数据用户联网时iOS 后台定时调用 tRPC 的sync.upsert路由将本地变更增删改批量提交到 ServerServer 用 Prisma 的upsert方法根据id和updatedAt时间戳判断是否需要更新Server 同时提供sync.changesSince(timestamp)路由iOS 调用它拉取服务器端的新变更合并到本地 Core Data。关键代码片段Server 端// packages/server/src/router/sync.ts export const syncRouter router({ upsert: protectedProcedure .input(z.array(z.object({ id: z.string(), type: z.enum([user, post]), data: z.record(z.any()), updatedAt: z.date(), }))) .mutation(async ({ input }) { for (const item of input) { await prisma[item.type].upsert({ where: { id: item.id }, update: { ...item.data, updatedAt: item.updatedAt }, create: { id: item.id, ...item.data, createdAt: item.updatedAt, updatedAt: item.updatedAt, } }); } }), changesSince: protectedProcedure .input(z.date()) .query(async ({ input }) { return await prisma.post.findMany({ where: { updatedAt: { gt: input } }, select: { id: true, title: true, updatedAt: true } }); }) });这个方案的优势是完全可控、成本极低一台 2C4G 的云服务器足够支撑 10 万 DAU、数据主权在自己手里。那些搜索“ios分屏”“uniapp项目ios如何实现息屏播报”的人往往在用 Firebase 做实时推送但没意识到真正的“息屏播报”不需要实时只需要在 App 进入后台前把待播报的数据同步到本地然后用 iOS 的UNNotificationServiceExtension在后台处理即可。t3 Server 就是那个可靠的、可审计的同步中枢。6. 生产部署与 iOS 上架避坑指南6.1 Electron 打包如何让 macOS 版本通过 Apple Gatekeeper 审核t3code项目打包 Electron 时默认生成的.dmg文件会被 macOS 标记为“已损坏”因为没用 Apple Developer ID 签名。这不是 bug而是苹果的安全机制。合规签名流程申请 Apple Developer Program 会员年费 $99获取 Developer ID Application 证书在 Xcode 中配置签名打开apps/electron/electron-builder.yml添加mac: { category: public.app-category.productivity, target: dmg, hardenedRuntime: true, gatekeeperAssess: true, entitlements: build/entitlements.mac.plist, notarize: true // 关键启用自动公证 }配置公证凭证在build/entitlements.mac.plist中声明所需权限如?xml version1.0 encodingUTF-8? !DOCTYPE plist PUBLIC -//Apple//DTD PLIST 1.0//EN http://www.apple.com/DTDs/PropertyList-1.0.dtd plist version1.0 dict keycom.apple.security.network.client/key true/ keycom.apple.security.files.user-selected.read-write/key true/ /dict /plist构建并公证运行pnpm build:macelectron-builder 会自动调用notarytool提交公证申请几小时后即可获得通过。注意那些搜索“electron打包apk”的人混淆了平台——Electron 只支持 macOS/Windows/Linux打包 APK 是 React Native 或 Capacitor 的事。t3 的价值在于你用同一套 tRPC 后端既可服务 Electron 桌面端也可服务 iOS App这才是真正的“一次开发多端部署”。6.2 iOS 上架如何规避 tRPC 相关的审核拒绝苹果审核指南第 4.3 条明确禁止“未声明的网络通信”。如果你的 t3 Server 地址写死在 Swift 代码里审核员抓包发现你调用了未在Info.plist中声明的域名会直接拒审。安全方案动态配置在 App 启动时从你的 HTTPS 服务器如https://api.yourdomain.com/config拉取 tRPC 的 Base URL而不是硬编码域名白名单在Info.plist的NSAppTransportSecurity中只为你的生产域名开启例外且必须用NSIncludesSubdomains和NSExceptionRequiresForwardSecrecy保证安全禁用调试端点确保生产构建中/trpc路由不暴露调试信息。在packages/server/src/trpc.ts中用环境变量控制const t initTRPC.create({ errorFormatter({ shape, error })

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑