uni.onerror 和 uni.onunhandledrejection 必须在 app.vue 的 onlaunch 中注册,用于捕获 app 端 js 异常;原生崩溃需接入 sdk(如 uni-crash-report);日志上报须结构化、带上下文、脱敏、防死循环,并支持本地缓存与多触发机制。

uni.onError 和 uni.onUnhandledRejection 必须在 App.vue 的 onLaunch 里注册
App端(iOS/Android)的JS异常无法靠 window.onerror 或 Vue.config.errorHandler 兜住,因为原生容器不走标准浏览器事件流。真正有效的入口只有两个:uni.onError 和 uni.onUnhandledRejection,且必须在 App.vue 的 onLaunch 生命周期中立即注册——延迟到页面或组件里注册,冷启动阶段的异常就彻底丢失了。
uni.onError 能捕获同步错误、throw、生命周期钩子内抛出的异常;uni.onUnhandledRejection 捕获未被 catch 的 Promise 拒绝(包括 async/await 中漏掉的 try)。但注意:Vue 组件模板渲染错误(如访问 undefined 属性、语法错)这两者都收不到,App端也用不了 config.errorHandler。
- 注册位置只能是
App.vue的onLaunch,不能写在mounted或其他页面钩子里 - 不要在
onLaunch里加异步逻辑(比如先请求用户信息再注册),会错过首屏异常 - 建议把错误处理函数抽成独立模块,在
onLaunch中直接调用初始化方法
原生崩溃必须用 SDK,uni-app 自身不提供采集能力
JS层异常只是表象,真正致命的是原生崩溃:iOS 的 EXC_BAD_ACCESS、Android 的 NullPointerException 等,完全绕过 JS 引擎,uni.onError 一概收不到。必须接入原生 SDK 才能捕获。
推荐方案是通过 uni-app 插件市场引入已封装好的原生插件,比如 uni-crash-report(集成了 Bugly 和 UMeng Crash),而不是自己手写原生模块。这类插件通常要求你在 manifest.json → App 模块配置 中手动勾选对应 SDK,否则打包后无效。
- 调试阶段原生崩溃日志只出现在 Xcode 控制台或 Android Studio 的 Logcat,不会自动上报
- iOS 后台任务有时间限制(约 30 秒),大体积崩溃日志需分片上传,避免超时丢弃
- 纯 JS 异常上报和原生崩溃监控必须共存,二者覆盖范围完全不同
日志结构化上报要带上下文,不能只传 err.stack
线上问题排查依赖上下文,光发 err.message 和 err.stack 几乎没用。真实可用的日志至少要包含:当前页面路径、用户登录态、设备型号、网络类型、uni-app 版本、是否在后台、vuex/pinia 当前关键状态快照。
上报前必须做脱敏处理:过滤手机号、token、身份证号等敏感字段;加锁防死循环——比如错误处理函数里又触发新错误,可能造成上报无限递归。
- 建议用
uni.getSystemInfoSync()获取设备信息,plus.runtime.version获取 App 版本 - 用户 ID 建议从
uni.getStorageSync('user_id')读取,避免未登录时为空导致上报失败 - 上报失败时别静默丢弃,可降级为本地缓存 + 下次网络恢复时重试
日志上传时机不能只靠定时,要结合事件触发
单纯用 setInterval 定时上传日志,在弱网或用户快速退出场景下极易丢失。更可靠的方式是混合触发:应用进入后台、捕获到未处理异常、关键业务完成、日志体积超阈值(如 500KB)时立刻上传。
uni.onAppHide() 是最稳妥的事件入口之一,iOS/Android 都支持;但要注意 iOS 后台执行时间极短,上传逻辑必须轻量,大文件应提前压缩或分片。
- 异常触发上传优先级最高,确保崩溃现场日志不丢失
- WiFi 网络下可设较短间隔(如 5 分钟),移动网络下拉长至数小时,减少流量消耗
- 用户主动点击“反馈问题”时,应打包最近 100 条日志 + 当前页面截图(如有)一并上传











