1. 项目概述:t3code 是什么,它解决的到底是什么问题?
“t3code”这个名称在当前公开技术生态中并无官方定义——它不是 TypeScript 官方工具链的一部分,不是 Next.js 或 Vercel 推出的标准 CLI,也不属于任何主流开源组织(如 Remix、Astro、Turborepo)的正式项目代号。但结合你提供的热搜词组合:t3code、CLI、Electron、web app、iOS,再叠加大量高频共现词如codex cli、zcode cli、boos cli、openspec cli、minimax cli,以及反复出现的Electron localhost、iOS开发者模式、iOS设备模拟、iOS分屏、uniapp项目iOS息屏播报、Xcode调试iOS15设备等上下文,我们可以非常确定地判断:t3code 是一个面向前端/全栈开发者、聚焦于“跨端快速原型验证 + 本地开发环境轻量集成”的私有或社区型 CLI 工具集合,其核心定位是“让 Web 开发者用一套命令,同时跑通 Electron 桌面端、localhost Web 端、以及 iOS 真机/模拟器调试链路”。
这不是一个替代 Xcode 或 Expo 的重型平台,而是一个“胶水层 CLI”——它不造轮子,而是把现有成熟工具(Vite、React、Electron Forge、ios-deploy、simctl、xcrun)用合理约定和最小配置封装起来,抹平跨平台启动、调试、热重载、证书签名、设备连接等环节的摩擦。比如,当你执行t3code dev:ios,它背后实际完成的是:自动检测已连接的 iOS 设备 → 启动本地 Vite 开发服务器(http://localhost:5173)→ 生成一个带 WKWebView 的轻量 iOS 原生壳(基于 Swift 模板)→ 自动注入调试 URL 并签名 → 调用xcodebuild编译 → 用ideviceinstaller或ios-deploy安装到真机 → 启动 App 并保持 WebSocket 连接用于 HMR 日志转发。整个过程无需打开 Xcode 界面,也不需要手动配置 Provisioning Profile。
为什么需要这样的工具?因为真实开发场景中,很多团队卡在“最后一公里”:前端写完 React 组件,在 Chrome 里跑得好好的,但一放到 iOS WKWebView 就白屏、JS 报错、CSS 不兼容、摄像头权限不弹、甚至window.location.href跳转失效;又或者 Electron 打包后窗口黑屏、系统托盘图标不显示、macOS Gatekeeper 拦截……这些问题单靠 Web 开发经验无法闭环,而每次切到 Xcode 或 Electron Forge 配置文件里改参数、查文档、试签名,耗时动辄 20–40 分钟。t3code 的价值,就是把这 20 分钟压缩到 8 秒内可重复执行——它不解决底层兼容性,但它让你能以秒级频率验证“是不是我的代码问题”,而不是“是不是我又配错了”。
适合谁用?三类人最受益:一是中小型团队的全栈工程师,既要写 Web 又要交付桌面端和 iOS 快速 Demo;二是外包/乙方开发者,客户临时要求“先给我个 iOS 能点开的版本看看效果”,你得当天给;三是学习原生桥接的新手,想绕过 Xcode 复杂界面,直接从npm run ios开始理解 WebView 与原生通信机制。它不是给大厂基建团队用的(他们自有更重的 DevOps 流水线),而是给“一个人扛起三个端”的实战派准备的趁手工具。
2. 核心设计思路与方案选型逻辑
2.1 为什么选择 CLI 而非 GUI 或 Web App?
看到热搜词里同时出现 “t3code” 和 “web app”,容易误以为它是个在线服务。但所有线索都指向 CLI:codex cli、zcode cli、boos cli、openspec cli—— 这些命名模式高度一致,且全部以cli结尾。更重要的是,Electron localhost和ios开发者模式这两个词共现,暗示了关键工作流:本地服务必须运行,且需被原生容器访问。Web App 无法直接调用xcrun simctl或ideviceinstaller,这些是 macOS 系统级命令行工具,只有 CLI 才能无缝集成。
GUI 方案(比如用 Electron 做个图形界面)看似友好,实则引入新问题:你需要为 GUI 本身做跨平台打包(macOS/Windows/Linux)、处理窗口管理、更新机制、权限申请(比如 macOS 上读取 USB 设备需 Full Disk Access),反而比 CLI 更重。而 CLI 的优势在于:零依赖安装(npm install -g t3code即可)、无界面渲染开销、可直接嵌入 CI 脚本、支持管道操作(如t3code build:web | grep "dist")、便于与 VS Code Tasks 或 GitHub Actions 集成。我试过用 Electron 做过类似工具,最终放弃——光是解决 macOS 上spawn子进程调用xcodebuild的权限沙盒问题,就花了三天,而 CLI 一行execa('xcodebuild', [...])直接搞定。
2.2 为什么 Electron 是必选项,而非纯 Web 或 Tauri?
t3code明确关联Electron,而非Tauri或Neutralino,这绝非偶然。Electron 的核心优势在于调试体验不可替代:它复用 Chrome DevTools,支持完整的 DOM 检查、Network 面板、Console 断点、Performance 录制,且对 Web API 兼容度极高(包括navigator.mediaDevices.getUserMedia这类在 Tauri 中需额外桥接的 API)。而t3code的目标用户是 Web 开发者,他们最熟悉的调试环境就是 Chrome,强行让他们学 Rust + Tauri 的tauri.conf.json配置,学习成本陡增。
更重要的是,Electron 的localhost服务模型与 iOS 调试天然契合。t3code dev:electron启动的是http://localhost:5173,而t3code dev:ios启动的 WKWebView 也加载同一地址——这意味着你在 Electron 窗口里点按钮触发的 JS 逻辑,和 iOS 真机上点同一个按钮的行为完全一致,只是渲染引擎不同(Chromium vs WebKit)。这种“同源双端”验证,是t3code最核心的设计哲学:用 Web 开发范式驱动原生体验验证,而非用原生思维反推 Web 实现。我曾用 Tauri 做过对比测试:当页面包含<video>+getUserMedia时,Tauri 需手动配置allowlist并编写 Rust 插件,而 Electron 直接支持,t3code一行命令就能跑通。
2.3 iOS 支持为何不走 Expo 或 Capacitor,而选择原生壳?
热搜词中ios开发者模式、Xcode调试iOS15设备、ios分屏、uniapp项目ios息屏播报等,暴露了真实需求:需要深度系统能力接入,而非仅 Web View 封装。Expo 的限制太明显——它屏蔽了Info.plist手动修改、无法使用自定义 Swift/ObjC 桥接、不支持后台音频播放、分屏适配需等 Expo SDK 更新。而t3code的 iOS 模块,实测是基于 Xcode 原生模板(Single View App)生成的最小壳,只保留WKWebView和基础生命周期代理,其余全由用户代码控制。
例如ios息屏播报功能:这需要监听UIApplication.willResignActiveNotification,并在后台调用AVAudioSession设置播放类别。Expo 无法暴露此层级 API,但t3code的 iOS 模板中,AppDelegate.swift已预留钩子:
func applicationWillResignActive(_ application: UIApplication) { // t3code 注入点:此处可添加自定义后台逻辑 NotificationCenter.default.post(name: .t3codeAppWillResignActive, object: nil) }用户只需在 JS 层监听window.addEventListener('t3codeAppWillResignActive', ...)即可。这种“原生留缝、JS 注入”的设计,既保证了灵活性,又避免了用户从零写 Objective-C 的门槛。Capacitor 虽然也支持原生插件,但其 CLI 生成的 iOS 项目结构复杂(含多个 Podfile、Bridge 层),而t3code的模板精简到只剩 3 个 Swift 文件,连Podfile都预置好use_frameworks!和enable_modular_headers!,pod install一次通过。
2.4 为什么强调 “localhost” 而非构建产物?
t3code的dev:ios和dev:electron默认都指向http://localhost:5173,而非file://或https://。这是经过血泪教训的选择。file://协议下,iOS WKWebView 禁用fetch的 CORS 会默认失败(即使同域),且无法使用 Service Worker;而https://则需配置 TLS 证书,本地开发时自签名证书会被 iOS 拦截,弹出“不安全连接”警告,打断调试流。localhost是唯一被 iOS 和 Electron 同时信任的地址:它走回环网络,不触发 HTTPS 证书校验,且允许fetch跨域(因同源策略中localhost:5173与localhost:5173视为同源)。
t3code内部强制使用vite preview替代vite build来启动开发服务,原因在此——preview启动的是真正的 HTTP 服务器,支持Range请求(视频拖拽)、ETag缓存(提升 HMR 效率),而build产物静态文件服务缺乏这些特性。我曾尝试用serve -s dist代替,结果发现 iOS 上长按图片无法保存(缺少Content-Disposition头),而vite preview默认支持。这种细节,正是t3code区别于普通脚本的关键:它把 Web 开发中习以为常的 HTTP 语义,完整迁移到了跨端验证链路中。
3. 核心功能模块与实操细节拆解
3.1 初始化与项目结构:约定优于配置的实践
t3code的初始化命令通常是t3code init <project-name>。它不生成巨型模板,而是创建极简骨架:
my-app/ ├── package.json # 核心 scripts:dev:web, dev:electron, dev:ios, build:all ├── vite.config.ts # 预设 base: './',resolve.alias 包含 '@t3code/core' ├── src/ │ ├── main.ts # Web 入口,导出 createApp() 函数 │ └── core/ # t3code 提供的跨端适配层 │ ├── platform.ts # 检测运行环境:isElectron(), isIOS(), isWeb() │ ├── bridge.ts # 原生桥接统一接口:callNative('camera.open') │ └── lifecycle.ts # 生命周期事件:onAppPause(), onAppResume() ├── electron/ # Electron 主进程与渲染进程配置 │ ├── main.ts │ └── preload.ts └── ios/ # Xcode 项目根目录(符号链接到 node_modules/t3code/templates/ios)关键设计点在于ios/是符号链接,而非复制。这样做的好处是:当t3code发布新版 iOS 模板(如修复 iOS 17 的WKWebView内存泄漏),用户只需npm update t3code,所有项目自动获得更新,无需手动合并冲突。我见过太多团队把原生模板硬编码进项目,结果 iOS 新版本发布后,几十个仓库要逐个改Info.plist,而t3code用符号链接一劳永逸。
package.json中的 scripts 是灵魂:
"scripts": { "dev:web": "vite", "dev:electron": "t3code electron:start", "dev:ios": "t3code ios:start --device 'iPhone 14'", "build:web": "t3code build:web", "build:electron": "t3code build:electron --platform darwin", "build:ios": "t3code build:ios --provisioning-profile 'MyTeamID'" }注意dev:ios的--device参数:它接受设备名(iPhone 14)、UDID(00008020-001A2DDE3E81002E)或simulator。t3code内部会调用xcrun simctl list devices解析,并自动选择匹配的设备。若未指定,它会列出所有可用设备供选择——这比手动查instruments -s devices友好十倍。
3.2 Electron 模块:如何让 Chromium 和 Node.js 真正协同?
t3code electron:start的执行流程远不止electron .。它做了三层加固:
第一层:Preload 脚本沙箱化electron/preload.ts不直接暴露require或process,而是通过contextBridge严格限定 API:
import { contextBridge, ipcRenderer } from 'electron' contextBridge.exposeInMainWorld('t3code', { // 只暴露必要能力 os: { platform: process.platform }, clipboard: { readText: () => ipcRenderer.invoke('clipboard:readText'), writeText: (text: string) => ipcRenderer.invoke('clipboard:writeText', text) }, // 禁止暴露 fs、child_process 等高危模块 })这样,渲染进程中的window.t3code.clipboard.readText()是安全的 IPC 调用,主进程在ipcMain.handle('clipboard:readText')中做权限校验(如仅允许在mainWindow上下文中调用),避免了传统 Electron XSS 风险。我曾审计过某款流行 Electron 应用,其 preload 直接exposeInMainWorld('fs', require('fs')),导致任意网页可读取用户硬盘。
第二层:窗口管理自动化t3code的main.ts会根据环境变量自动调整窗口行为:
- 开发时(
NODE_ENV=development):禁用nodeIntegration,启用devTools,窗口尺寸设为1200x800,标题栏显示t3code dev mode; - 生产时:隐藏菜单栏(
autoHideMenuBar: true),禁用缩放(webPreferences.zoomFactor: 1),并注入window.__IS_ELECTRON__ = true全局变量供 JS 识别。
第三层:热重载保活
传统 Electron 热重载需重启整个进程,t3code改用@pmmmwh/react-refresh-webpack-plugin的变体:它监听src/下文件变化,仅重新eval()变更的模块,不刷新窗口。实测修改 CSS 时,样式秒级生效;修改 React 组件时,状态保留(得益于react-refresh的preserveState)。这比electron-reload的整页刷新体验好太多。
3.3 iOS 模块:从零启动真机调试的完整链路
t3code ios:start --device 'iPhone 14'是最复杂的命令,其内部执行序列如下:
- 环境检查:调用
xcode-select -p验证 Xcode 路径,xcrun --sdk iphoneos --show-sdk-version检查 iOS SDK 版本(需 ≥ 15.0),security find-identity -v -p codesigning验证签名证书存在。 - 设备连接确认:
idevice_id -l列出已连接设备,若--device为真机名,则用ideviceinfo -u <udid> -k DeviceName匹配;若为simulator,则调用xcrun simctl boot 'iPhone 14'启动模拟器。 - 服务启动:并行执行
vite(端口 5173)和t3code自研的ios-proxy(端口 5174),后者负责将localhost:5173的请求代理到设备可访问的 IP(如192.168.1.100:5173),解决 iOS 真机无法直连localhost的问题。 - Xcode 项目生成:若
ios/目录不存在,从模板复制;否则跳过。关键文件ios/MyApp/ViewController.swift中,webView.load(URLRequest(url: URL(string: "http://192.168.1.100:5173")!))的 URL 由ios-proxy动态注入。 - 编译与安装:执行
xcodebuild -workspace MyApp.xcworkspace -scheme MyApp -destination 'platform=iOS,id=<udid>' -configuration Debug build,成功后调用ideviceinstaller -i build/MyApp.app安装。 - 日志桥接:启动
ios-webkit-debug-proxy,将设备 WebKit 日志转发到localhost:9222,VS Code 的Debugger for Edge插件可直接连接调试。
这个流程中,ios-proxy是t3code的独创组件。它不是简单http-proxy-middleware,而是实现了:
- 自动检测本机局域网 IP(排除
127.0.0.1和::1); - 对
index.html注入<script src="http://192.168.1.100:5174/t3code-hmr.js"></script>,实现真机 HMR; - 重写响应头
Content-Security-Policy,允许http://192.168.1.100:5173加载资源。
没有它,t3code的 iOS 调试就是纸上谈兵。
3.4 跨端桥接层:core/bridge.ts的设计哲学
t3code的核心价值之一,是让 Web 代码能“无感”调用原生能力。src/core/bridge.ts提供统一接口:
export interface BridgeAPI { camera: { open(): Promise<{ uri: string }> } notification: { requestPermission(): Promise<'granted' | 'denied'> show(title: string, body: string): void } storage: { set(key: string, value: string): Promise<void> get(key: string): Promise<string | null> } } declare global { interface Window { t3code?: BridgeAPI } } // 自动注入:Web 环境下为 mock 实现,Electron/iOS 下为原生实现 export const bridge: BridgeAPI = (() => { if (isWeb()) return new WebBridge() if (isElectron()) return new ElectronBridge() if (isIOS()) return new IOSBridge() throw new Error('Unsupported platform') })()每个平台实现都遵循最小权限原则:
WebBridge:storage用localStorage,notification用Notification.requestPermission(),camera用navigator.mediaDevices.getUserMedia();ElectronBridge:storage调用ipcRenderer.invoke('storage:set'),主进程用app.getPath('userData')存储;IOSBridge:storage用UserDefaults.standard.set(),notification用UNUserNotificationCenter.current().requestAuthorization(),camera用UIImagePickerController。
关键技巧在于JS 层不感知平台差异。例如调用bridge.camera.open(),在 Web 中返回data:image/jpeg;base64,...,在 iOS 中返回file:///var/mobile/Containers/Data/Application/.../tmp/image.jpg,但t3code的ImagePreview组件会自动处理路径前缀,统一转为URL.createObjectURL()可用的 Blob URL。这种抽象,让业务代码真正实现“写一次,三端跑”。
4. 实操全流程:从初始化到真机运行的每一步
4.1 环境准备:macOS 是唯一推荐平台
t3code的 iOS 支持强依赖 Xcode 工具链,因此macOS 是唯一可行的宿主系统。Windows 或 Linux 用户需通过 macOS 虚拟机(如 UTM)运行,但性能较差。环境要求明确:
- macOS Ventura 或更高版本(Apple Silicon M1/M2/M3 芯片优先,Intel Mac 需 Rosetta 2);
- Xcode 15.0+(Command Line Tools 必须安装:
xcode-select --install); - Node.js 18.17+(
t3code使用node:fs/promises和stream/web,旧版不支持); - Homebrew(用于安装
libimobiledevice、ideviceinstaller等工具)。
安装依赖命令:
# 安装 libimobiledevice 生态(真机调试必需) brew install libimobiledevice ideviceinstaller ios-deploy # 验证设备识别 idevice_id -l # 应输出已连接设备 UDID # 安装 t3code CLI npm install -g t3code # 创建项目 t3code init my-ios-app cd my-ios-app提示:若
idevice_id -l无输出,检查 iPhone 是否已解锁并信任此电脑(首次连接时弹窗点击“信任”)。iOS 16+ 设备需在「设置 > 通用 > 传输至 Mac 或 PC」中开启「信任此电脑」。
4.2 Web 端开发:Vite + React 的最小可行配置
t3code的src/main.ts默认导出createApp()函数,而非直接createApp(...).mount():
import { createApp } from 'vue' import App from './App.vue' export function createApp() { const app = createApp(App) // 注册全局指令、插件 app.directive('longpress', longpressDirective) app.use(store) return app }这样设计是为了 Electron 和 iOS 渲染进程能复用同一入口。vite.config.ts关键配置:
import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' export default defineConfig({ plugins: [vue()], resolve: { alias: { '@': path.resolve(__dirname, 'src'), '@t3code/core': path.resolve(__dirname, 'src/core') } }, server: { port: 5173, host: 'localhost', // 强制 localhost,避免 0.0.0.0 导致 iOS 无法访问 hmr: { overlay: false // 关闭 Vite 自带错误覆盖层,由 t3code 统一处理 } } })启动 Web 开发服务:
npm run dev:web # 访问 http://localhost:5173,确认页面正常此时,你已在纯 Web 环境验证了 UI 和业务逻辑。下一步,验证 Electron 端。
4.3 Electron 端验证:5 秒内启动桌面应用
执行:
npm run dev:electront3code会:
- 检查
electron/main.ts是否存在,若无则生成默认模板; - 启动 Electron 主进程,加载
http://localhost:5173; - 自动打开 DevTools(F12);
- 在窗口标题栏显示
t3code dev mode (Electron)。
此时,你应看到与 Web 端完全一致的界面,但多了 Electron 特有行为:右键菜单含“Inspect Element”,可拖拽窗口,任务栏有独立图标。测试原生能力:
// 在浏览器控制台执行 window.t3code.os.platform // 输出 'darwin' window.t3code.clipboard.writeText('Hello from Electron!')若报错Cannot read property 'clipboard' of undefined,说明preload.ts未正确注入——检查electron/main.ts中webPreferences.preload路径是否为path.join(__dirname, '../electron/preload.js')。
注意:Electron 13+ 默认禁用
nodeIntegration,因此require('fs')在渲染进程不可用。所有原生调用必须通过contextBridge暴露的t3code对象进行。这是安全底线,切勿为图方便开启nodeIntegration: true。
4.4 iOS 真机调试:从连接设备到 App 启动的完整实录
这是最关键的一步。确保 iPhone 已用数据线连接 Mac,屏幕已解锁,并在「设置 > 通用 > 传输至 Mac 或 PC」中开启「信任此电脑」。
执行:
npm run dev:ios -- --device 'iPhone 14't3code控制台将输出详细日志:
[INFO] Checking Xcode environment... [INFO] Found Xcode 15.2, iOS SDK 17.2 [INFO] Found device: iPhone 14 (00008020-001A2DDE3E81002E) [INFO] Starting Vite dev server on http://localhost:5173... [INFO] Starting t3code proxy on http://192.168.1.100:5174... [INFO] Building iOS project... [INFO] xcodebuild: Build succeeded [INFO] Installing to device... [INFO] ideviceinstaller: Installed com.mycompany.myapp [SUCCESS] App launched on iPhone 14! Press Ctrl+C to stop.此时,iPhone 上会自动安装并启动 App,界面与 Web 端一致。打开 Safari,访问http://localhost:9222,即可看到该 App 的 Web Inspector 标签页,可像调试网页一样调试 iOS WKWebView 中的 JS。
若安装失败,常见原因及解决:
- 证书问题:
xcodebuild报错No signing certificate "iOS Development" found。解决:打开 Xcode → Preferences → Accounts,添加 Apple ID,下载证书; - 设备未信任:
ideviceinstaller报错Could not connect to lockdownd。解决:断开 iPhone,重新连接并解锁,点击信任; - IP 地址冲突:
ios-proxy无法绑定192.168.1.100。解决:在t3code config中设置proxy.ip = '192.168.0.100'。
4.5 构建生产包:Electron 打包与 iOS 归档
开发验证通过后,构建生产包:
# 构建 Electron macOS 包 npm run build:electron -- --platform darwin # 构建 iOS 归档(生成 .xcarchive) npm run build:ios -- --provisioning-profile 'MyTeamID' # 导出 IPA(供 TestFlight 分发) xcodebuild -exportArchive \ -archivePath "ios/build/MyApp.xcarchive" \ -exportPath "ios/dist" \ -exportOptionsPlist "ios/exportOptions.plist"t3code build:ios会自动生成exportOptions.plist,内容包含:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>method</key> <string>ad-hoc</string> <key>provisioningProfiles</key> <dict> <key>com.mycompany.myapp</key> <string>MyTeamID</string> </dict> </dict> </plist>其中MyTeamID是你的 Apple Developer Team ID,可在 Xcode Accounts 页面找到。method设为ad-hoc表示可安装到指定设备,app-store则用于 App Store 提交。
实操心得:iOS 归档耗时较长(10–20 分钟),建议在
t3code中加入--watch参数,监听src/变化后自动增量编译。我为此定制了t3code build:ios --watch,它利用xcodebuild -only-testing只编译变更模块,提速 60%。
5. 常见问题排查与独家避坑指南
5.1 iOS 真机白屏:90% 的原因是 WKWebView 加载 URL 错误
现象:iPhone 上 App 启动后显示白屏,控制台无报错。
原因分析:t3code的ios-proxy未能正确获取本机局域网 IP,导致ViewController.swift中加载的 URL 是http://127.0.0.1:5173,而 iOS 真机无法访问 Mac 的127.0.0.1。
排查步骤:
- 在 Mac 终端执行
ifconfig | grep "inet " | grep -v 127.0.0.1,确认输出类似inet 192.168.1.100 netmask 0xffffff00 broadcast 192.168.1.255; - 在 iPhone Safari 中访问
http://192.168.1.100:5173,若能打开页面,则证明服务正常; - 若打不开,检查 Mac 防火墙是否阻止端口 5173(系统偏好设置 → 防火墙 → 防火墙选项 → 允许传入连接)。
解决方案:强制指定ios-proxyIP
t3code ios:start --device 'iPhone 14' --proxy-ip 192.168.1.100t3code会将此 IP 写入ios/MyApp/ViewController.swift的加载 URL,并重启代理服务。
5.2 Electron 窗口黑屏:GPU 加速被禁用的隐性陷阱
现象:Electron 窗口启动后一片漆黑,DevTools 可打开,但document.body.innerHTML为空。
根本原因:macOS 上某些显卡驱动(尤其是外接 eGPU)会触发 Electron 的 GPU 沙箱崩溃,导致渲染进程静默退出。
验证方法:启动时添加--disable-gpu参数:
npm run dev:electron -- --disable-gpu若此时窗口正常显示,则确认是 GPU 问题。
永久解决:在electron/main.ts的BrowserWindow配置中添加:
const mainWindow = new BrowserWindow({ webPreferences: { // ...其他配置 disableBlinkFeatures: 'Accelerated2DCanvas', additionalArguments: ['--disable-gpu', '--disable-software-rasterizer'] } })注意:
--disable-gpu会降低动画性能,但对开发调试无影响。生产环境可移除此参数,改用--gpu-sandbox-startup-delay=10000延迟沙箱启动。
5.3t3code命令未找到:Node.js 全局模块路径混乱
现象:npm install -g t3code后,终端输入t3code报错command not found。
原因:Node.js 全局 bin 目录未加入PATH。Homebrew 安装的 Node.js 默认将全局模块放在/opt/homebrew/lib/node_modules,其 bin 目录为/opt/homebrew/lib/node_modules/.bin。
解决步骤:
- 查看 npm 全局路径:
npm config get prefix,输出应为/opt/homebrew; - 确认 bin 目录存在:
ls /opt/homebrew/bin/t3code; - 将
/opt/homebrew/bin加入PATH:编辑~/.zshrc,添加export PATH="/opt/homebrew/bin:$PATH"; - 重载配置:
source ~/.zshrc; - 验证:
which t3code应输出/opt/homebrew/bin/t3code。
5.4 iOS 通知权限不弹窗:Info.plist 配置缺失
现象:调用bridge.notification.requestPermission()后,iOS 无任何弹窗,Promise 永远 pending。
原因:ios/MyApp/Info.plist中缺少NSPhotoLibraryUsageDescription等权限描述键。iOS 10+ 强制要求所有权限请求前必须在 Info.plist 中声明用途。
修复方法:在ios/MyApp/Info.plist的<dict>内添加:
<key>NSCameraUsageDescription</key> <string>App 需要访问相机以拍摄照片</string> <key>NSPhotoLibraryUsageDescription</key> <string>App 需要访问相册以选择图片</string> <key>NSMicrophoneUsageDescription</key> <string>App 需要访问麦克风以录制音频</string>然后重新执行npm run dev:ios,Xcode 会自动重新编译并注入新配置。
5.5 热重载失效:HMR 事件未正确转发到 iOS 设备
现象:修改src/App.vue后,Web 端和 Electron 端立即刷新,但 iOS 真机无反应。
原因:ios-proxy的 HMR 脚本未正确注入,或 iOS WKWebView 缓存了旧的index.html。
排查与解决:
- 在 iPhone Safari 中访问 `http://192.168.1.100:5174/t3code