uniapp开发APP重点
UniApp 开发 iOS/Android App 端
本文档为纯App端专属内容,剔除所有跨端冗余信息,完整覆盖从项目搭建、核心配置、生命周期、条件编译、原生能力、性能优化到打包上架的全流程。
项目实践
暂无
一、App端项目初始化与核心配置(全量专属详解)
App端的所有原生能力、打包规则、权限配置均围绕 manifest.json 展开,是App开发的核心根基,所有配置项必须精准设置,否则会导致功能不可用、打包失败、上架被拒。
1.1 项目初始化
- 使用HBuilderX创建项目,选择「uni-app项目」,根据技术栈选择Vue2/Vue3模板,推荐使用uni-ui官方模板,原生兼容性最优。
- 项目创建后,优先配置
manifest.json,所有App端专属能力均在此文件中开启和配置。
1.2 manifest.json App端全量核心配置
1.2.1 基础配置(全局唯一,上架后不可修改)
| 配置项 | 核心说明 | 硬性规则 |
|---|---|---|
| 应用名称 | App在手机桌面、应用商店显示的名称 | 控制在6个字以内,避免特殊字符,需与应用商店上架名称一致 |
| 应用版本名称 | 对外显示的版本号,如1.0.0 |
遵循语义化版本规范(主版本.次版本.修订版本),每次上架必须递增 |
| 应用版本号 | 对内的整数版本号,如1、2、3 |
Android对应versionCode,iOS对应CFBundleVersion,每次打包上架必须严格递增,否则无法更新应用商店版本 |
| 应用标识(AppID) | App的唯一身份标识 | Android对应包名(推荐反向域名格式,如com.example.app),iOS对应Bundle ID,上架后不可修改,必须与开发者后台申请的包名/Bundle ID完全一致 |
1.2.2 图标与启动图配置(上架必备,避免被拒)
- 应用图标
- Android端:必须提供
48*48、72*72、96*96、144*144、192*192分辨率图标,同时提供自适应前景+背景图标,避免拉伸变形、圆角裁切异常。 - iOS端:必须提供
1024*1024(App Store专用)、60*60@2x、60*60@3x、76*76@2x等全分辨率图标,禁止包含透明通道、圆角、alpha通道,否则上架App Store会被直接拒审。
- Android端:必须提供
- 启动图(Splash图)
- 提供不同分辨率的启动图,适配全面屏、折叠屏、平板等不同屏幕尺寸,Android端推荐使用
.9.png格式,避免拉伸。 - 必须开启「等待首页渲染完成后关闭启动图」选项,避免启动后出现白屏。
- 可配置启动图的状态栏样式(深色/浅色、是否隐藏)、开屏广告时长、关闭逻辑。
- 提供不同分辨率的启动图,适配全面屏、折叠屏、平板等不同屏幕尺寸,Android端推荐使用
1.2.3 App模块配置(功能可用的核心)
必须勾选App中需要使用的原生模块,未勾选的模块打包后会被完全剔除,无法调用对应API,不需要的模块禁止勾选,否则会增加包体积、申请不必要的权限,导致上架被拒。
常用核心模块清单:
| 模块名称 | 核心作用 | 适用场景 |
|---|---|---|
| Payment | 支付模块,集成微信支付、支付宝支付原生SDK | App内商品支付、订单支付 |
| OAuth | 第三方登录鉴权模块,集成微信、QQ、微博登录 | 第三方账号一键登录 |
| Push | 推送模块,集成个推/极光推送原生SDK | 离线消息推送、运营通知推送 |
| Maps | 地图模块,集成高德/百度地图原生SDK | 地图展示、路径规划、POI检索 |
| Geolocation | 定位模块,集成系统定位、高德/百度定位 | 获取用户经纬度、位置信息 |
| Camera | 相机模块 | 拍照、扫码、人脸识别 |
| Gallery | 相册模块 | 选择/保存图片、视频 |
| Video | 视频播放模块 | 本地/在线视频播放、全屏播放 |
| Audio | 音频播放模块 | 本地/在线音频、后台音频播放 |
| File | 文件系统模块 | 本地文件读写、文件夹管理 |
| SQLite | 本地数据库模块 | 本地结构化数据存储、离线数据管理 |
| Bluetooth | 蓝牙模块 | 蓝牙设备连接、数据通信 |
| WebView | 网页视图模块 | 加载远程网页、H5页面交互 |
| Share | 分享模块 | 分享内容到微信、QQ、微博等平台 |
1.2.4 权限配置(合规核心,上架必查)
仅申请功能必须的权限,非必要权限禁止勾选,否则会因隐私合规问题被应用商店拒审。
- Android权限配置
常用核心权限清单:权限名称 核心作用 申请前提 android.permission.INTERNET网络访问权限 所有需要联网的App必须申请 android.permission.ACCESS_NETWORK_STATE网络状态获取权限 监听网络切换、判断网络是否可用 android.permission.CAMERA相机访问权限 拍照、扫码、录像功能 android.permission.ACCESS_FINE_LOCATION精确定位权限 获取用户精准位置、导航功能 android.permission.READ_EXTERNAL_STORAGE存储读取权限 读取相册、本地文件 android.permission.WRITE_EXTERNAL_STORAGE存储写入权限 保存图片、文件到本地 android.permission.RECORD_AUDIO麦克风录音权限 语音录制、视频拍摄 - iOS权限配置
所有申请的权限必须填写清晰、真实的用途描述,否则上架App Store会被直接拒审,常用核心权限与描述:权限Key 用途描述规范示例 NSCameraUsageDescription需要访问您的相机,用于拍摄照片、扫码识别商品 NSLocationWhenInUseUsageDescription需要访问您的位置信息,用于展示附近门店、导航服务 NSPhotoLibraryAddUsageDescription需要访问您的相册,用于保存图片到手机 NSPhotoLibraryUsageDescription需要访问您的相册,用于选择图片发布内容 NSMicrophoneUsageDescription需要访问您的麦克风,用于录制语音、视频拍摄收音
1.2.5 原生配置
- 屏幕方向配置:配置App支持的横竖屏方向,默认仅开启竖屏,视频、直播类页面可单独配置横屏。
- 安全区域适配:强制开启「适配iPhone X及以上安全区域」,避免内容被刘海屏、底部小黑条遮挡。
- Android原生配置:自定义
AndroidManifest.xml内容,如应用主题、Activity启动模式、intent-filter(第三方跳转、URL Scheme)、meta-data(第三方SDK配置)。 - iOS原生配置:自定义
info.plist内容,如URL Scheme(微信/支付宝回调、第三方跳转)、LSApplicationQueriesSchemes(应用跳转白名单)、后台运行模式、ATS配置。
1.2.6 打包配置
- Android打包配置
- 签名配置:必须生成
.keystore签名文件,填写别名、密码、密钥库密码,签名文件必须妥善备份,丢失后无法更新应用商店的App。 - 打包类型:Debug测试包(用于开发测试)、Release正式包(用于上架应用商店)。
- CPU架构:仅勾选
armeabi-v7a、arm64-v8a,可覆盖99%以上Android手机,同时大幅减少包体积。 - 代码混淆:开启ProGuard代码混淆,保护代码安全,必须配置第三方SDK的混淆规则,避免SDK被混淆后无法使用。
- 签名配置:必须生成
- iOS打包配置
- 证书配置:配置iOS开发证书(真机测试)、发布证书(上架App Store),以及对应的描述文件,所有证书必须从Apple Developer后台申请。
- 打包类型:Debug测试包(真机调试)、Release正式包(上架App Store)。
- 最低支持iOS版本:推荐设置为iOS 11.0以上,覆盖99%以上活跃iOS设备。
1.3 pages.json App端专属配置
大部分基础配置与跨端规范一致,App端专属差异与核心配置如下:
- tabBar页面可放在分包中,无小程序端的主包限制。
- 页面
style支持配置软键盘弹出模式:adjustResize(页面压缩)、adjustPan(页面上移),适配输入框被软键盘遮挡的场景。 - 支持自定义下拉刷新样式,如
circle圆形刷新样式,可自定义颜色、背景。 - 可配置页面是否支持iOS侧滑返回,默认开启,可通过
popGesture: none关闭。 - 支持配置页面状态栏样式、是否隐藏状态栏,适配沉浸式页面。
二、App端全量生命周期体系(含Plus引擎专属)
App端生命周期融合了UniApp标准生命周期与HTML5+(Plus)原生引擎生命周期,所有Plus原生API必须在生命周期钩子触发后调用,否则会报错,全量钩子详解如下。
2.1 应用生命周期(仅在App.vue中生效,全局唯一)
控制整个App的启动、前后台切换、全局错误捕获,是App原生能力初始化的唯一入口。
| 生命周期钩子 | 触发时机 | 接收参数 | 核心使用场景 | App端专属注意事项 |
|---|---|---|---|---|
onLaunch |
App冷启动初始化完成时触发,全局仅触发1次,此时Plus原生引擎已完全就绪 | options对象,包含:- path:启动页面路径- query:启动页面参数- scene:启动场景(桌面图标、推送点击、第三方App跳转)- referrerInfo:来源应用的包名/Bundle ID |
1. 全局原生能力初始化:注册Android返回键监听、推送点击监听、前后台切换监听 2. 第三方SDK初始化:推送、统计、支付、地图SDK 3. 全局状态初始化:检查用户登录状态、token有效性 4. App热更新检查、整包版本更新检查 5. 调用Plus API获取设备信息、原生模块初始化 |
1. 仅冷启动触发,热启动(后台切回)不触发 2. 此时Plus引擎已100%就绪,可安全调用所有 plus.* API,无需额外监听plusready事件3. 禁止写入高耗时同步逻辑,否则会导致App启动白屏、卡顿 |
onShow |
App启动,或从后台切入前台显示时触发,每次切前台都会触发 | 同onLaunch的options参数 |
1. 刷新用户登录状态、token续期 2. 检查App版本更新、热更新 3. 重启暂停的音频、定位、蓝牙服务 4. 统计App前台停留时长、启动次数 |
1. 冷启动时,在onLaunch之后触发2. 热启动仅触发 onShow,不触发onLaunch3. 锁屏后解锁、从其他App切回都会触发,触发频率高,禁止写入高耗时逻辑 |
onHide |
App从前台切入后台时触发(按Home键、切换App、锁屏) | 无参数 | 1. 暂停音频、视频播放,避免后台耗电 2. 保存用户临时操作数据、页面状态 3. 断开非必要的websocket、定位、蓝牙连接 4. 统计App后台停留时长 |
1. Android系统对后台App的运行时间、内存占用有限制,禁止执行耗时过长操作 2. 锁屏时触发 onHide,解锁时触发onShow |
onError |
App发生脚本错误、API调用失败、原生模块报错时触发 | error:错误信息完整字符串 |
1. 全局错误捕获与上报 2. 错误日志写入本地文件,便于后续排查 3. 友好错误提示,避免App白屏、无响应 |
1. 同步代码错误、异步Promise reject、原生模块报错均会触发 2. 禁止在此钩子内抛出新错误,导致死循环 |
onPageNotFound |
App打开的页面路径不存在时触发 | 对象参数,包含path(无效页面路径)、query(页面参数)、isEntryPage(是否为启动页) |
1. 无效页面的兜底跳转,如跳转到首页/404页 2. 无效路径上报 3. 旧版本下线页面的重定向兼容 |
重定向必须使用uni.reLaunch/uni.redirectTo,禁止使用uni.navigateTo |
onUnhandledRejection |
App出现未处理的Promise reject时触发 | 对象参数,包含reason(拒绝原因)、promise(对应Promise对象) |
全局捕获异步接口、异步操作的错误,统一上报和友好提示 | App端全版本支持,无需考虑基础库兼容 |
onThemeChange |
App系统深色/浅色主题切换时触发 | 对象参数,包含theme(dark/light) |
监听系统主题变化,动态切换App的深色/浅色模式 | 仅iOS 13.0+、Android 10.0+系统支持 |
App.vue 完整代码示例(App端专属)
1 | |
2.2 Plus引擎专属就绪事件(特殊场景备用)
UniApp的onLaunch钩子已保证Plus引擎完全就绪,常规开发无需监听此事件,仅在脱离UniApp生命周期的独立JS文件、原生回调中需要使用。
1 | |
2.3 页面生命周期(App端专属详解)
每个页面.vue文件中生效,控制单个页面的加载、渲染、显示、隐藏、卸载,App端专属规则与用法如下:
| 生命周期钩子 | 触发时机 | 接收参数 | 核心使用场景 | App端专属注意事项 |
|---|---|---|---|---|
onLoad |
页面加载时触发,每个页面仅触发1次 | options:接收上一页通过URL传递的参数 |
1. 获取页面入参,初始化核心数据 2. 发起页面初始数据接口请求 3. 初始化定时器、事件监听 |
1. 仅触发1次,页面卸载前不会重复触发 2. 页面返回再进入,不会触发 onLoad,仅触发onShow3. 此时DOM未渲染,不能获取节点尺寸、调用原生UI组件 |
onShow |
页面显示时触发,每次切入前台都会触发(首次加载、返回当前页、App切前台) | 无参数 | 1. 刷新页面数据,如详情页返回后刷新列表状态 2. 重启暂停的动画、音频、定位 3. 更新实时数据(倒计时、用户信息) |
1. tabBar页面切换时,仅触发onShow/onHide,不触发onLoad/onUnload2. 触发频率高,禁止写入高耗时同步逻辑 |
onReady |
页面初次渲染完成时触发,每个页面仅触发1次 | 无参数 | 1. 获取DOM节点尺寸、位置信息 2. 初始化canvas、地图、视频等原生组件 3. 执行依赖DOM渲染完成的动画 |
1. 仅触发1次,在onLoad/onShow之后触发2. 必须配合 this.$nextTick使用,确保节点渲染完成3. 可通过 this.$scope.$getAppWebview()获取当前页面的原生Webview实例,进行原生级操作 |
onHide |
页面隐藏时触发(跳转到下一页、tab切换、App切后台) | 无参数 | 1. 暂停动画、定时器、音频播放 2. 保存临时输入内容、页面滚动位置 3. 解绑事件监听,避免内存泄漏 |
页面跳转时,先触发当前页onHide,再触发下一页onLoad |
onUnload |
页面卸载时触发,每个页面仅触发1次 | 无参数 | 1. 销毁所有定时器、延时器 2. 解绑所有事件监听(事件总线、推送监听) 3. 销毁canvas、地图、视频等占用内存的组件 4. 取消未完成的接口请求 |
1. 触发场景:uni.redirectTo、uni.navigateBack关闭当前页、uni.reLaunch关闭所有页面2. uni.navigateTo不会触发当前页onUnload,仅触发onHide3. 必须在此清理所有副作用,否则会导致App内存泄漏、卡顿、闪退 |
onPullDownRefresh |
页面下拉刷新时触发 | 无参数 | 重新请求页面数据,刷新列表、重置页面状态 | 1. 必须在pages.json的globalStyle或页面style中配置enablePullDownRefresh: true才能触发2. 刷新完成后必须调用 uni.stopPullDownRefresh()停止刷新动画 |
onReachBottom |
页面滚动到底部时触发 | 无参数 | 列表分页加载,触发下一页数据请求 | 1. 触发距离可通过onReachBottomDistance配置,默认50px2. 页面高度不足1屏时不会触发 3. scroll-view的滚动不会触发此钩子,需用scrolltolower事件 |
onPageScroll |
页面滚动时实时触发 | 对象参数,包含scrollTop(垂直滚动距离,单位px) |
1. 滚动时动态修改导航栏透明度、标题样式 2. 控制回到顶部按钮的显示/隐藏 |
触发频率极高,必须做节流处理(推荐100ms以上),禁止在此做频繁的数据更新,严重影响性能 |
onTabItemTap |
tabBar页面点击tab按钮时触发 | 对象参数,包含index(tab序号,从0开始)、pagePath、text |
1. 点击tab时刷新页面数据 2. 拦截tab点击,如未登录时跳转到登录页 |
仅在tabBar页面中生效,非tabBar页面不触发,点击当前tab也会触发 |
onBackPress |
App端页面返回时触发(物理返回键、顶部返回按钮、侧滑返回) | 对象参数,包含from(触发来源:backbutton/navigateBack) |
1. 拦截返回行为,如未保存内容时提示用户 2. 自定义返回逻辑,如返回指定页面 |
App端专属钩子,小程序端不支持;return true会阻止默认返回行为 |
页面生命周期完整代码示例(App端专属)
1 | |
2.4 组件生命周期(App端专属)
App端组件生命周期完全遵循Vue标准生命周期,无额外专属钩子,核心规则如下:
| 生命周期钩子 | 触发时机 | 核心使用场景 | App端注意事项 |
|---|---|---|---|
beforeCreate |
组件实例刚创建,数据观测未初始化 | 极少使用 | 无法访问data、methods,触发时机极早 |
created |
组件实例创建完成,data、methods已初始化,DOM未生成 | 1. 发起组件所需的接口请求 2. 初始化组件内部状态 |
组件未挂载,不能操作DOM节点 |
beforeMount |
组件挂载开始前,模板编译完成 | 极少使用 | 触发在created之后,mounted之前 |
mounted |
组件挂载到DOM完成 | 1. 获取组件内DOM节点信息 2. 初始化canvas、动画等依赖DOM的逻辑 |
必须配合this.$nextTick使用,确保节点渲染完成 |
beforeUpdate |
组件数据更新,DOM重渲染前触发 | 监听数据变化,获取更新前的DOM状态 | 频繁触发,慎用,避免影响性能 |
updated |
组件数据更新,DOM重渲染完成后触发 | 数据更新后,操作更新后的DOM | 禁止在此修改数据,否则会触发无限更新循环 |
beforeDestroy |
组件实例销毁前触发,实例完全可用 | 1. 清理定时器、延时器 2. 解绑事件监听 3. 取消未完成的接口请求 |
必须在此清理所有副作用,避免App内存泄漏 |
destroyed |
组件实例销毁完成后触发 | 最终清理逻辑,销毁占用内存的对象 | 仅触发1次 |
三、App端条件编译 全场景实战详解
条件编译是App端处理iOS/Android平台差异、原生能力兼容、环境区分的核心方案,通过特殊注释语法,让代码仅在对应平台编译时生效,避免冗余代码和兼容报错。
3.1 基础语法与平台标识
核心语法
#ifdef 平台标识:如果当前编译平台匹配该标识,编译这段代码#ifndef 平台标识:如果当前编译平台不匹配该标识,编译这段代码#endif:条件编译结束标记
App端专属平台标识
| 平台标识 | 对应平台 |
|---|---|
| APP-PLUS | 所有App端(iOS+Android) |
| APP-PLUS-IOS | 仅iOS App端 |
| APP-PLUS-ANDROID | 仅Android App端 |
| H5 | 仅H5端(用于排除App端逻辑) |
| MP | 所有小程序端(用于排除App端逻辑) |
3.2 模板(template)中的条件编译
用于控制iOS/Android平台显示不同的组件、内容、功能入口,最常用场景示例:
1 | |
3.3 脚本(script)中的条件编译
用于处理iOS/Android平台的原生API差异、原生插件调用、逻辑区分,避免不兼容API报错,全场景示例:
1 | |
3.4 样式(style)中的条件编译
用于处理iOS/Android平台的样式适配、安全区域适配、系统UI差异,示例:
1 | |
3.5 配置文件(pages.json)中的条件编译
用于处理iOS/Android平台的页面配置、tabBar样式、窗口表现差异,示例:
1 | |
3.6 环境区分条件编译
用于区分开发环境和生产环境,配置不同的接口域名、调试开关,示例:
1 | |
四、App端核心原生能力(Plus API 全量核心用法)
App端与小程序、H5端的核心差异,在于可以通过HTML5+(Plus)引擎调用系统原生能力,突破浏览器限制,实现和原生App一致的功能体验,核心能力全量详解如下。
4.1 运行时与应用管理(plus.runtime)
| 核心API | 功能说明 | 代码示例 |
|---|---|---|
plus.runtime.version |
获取App版本名称 | const version = plus.runtime.version |
plus.runtime.versionCode |
获取App整数版本号 | const versionCode = plus.runtime.versionCode |
plus.runtime.quit() |
退出App(仅Android支持) | plus.runtime.quit() |
plus.runtime.restart() |
重启App | plus.runtime.restart() |
plus.runtime.install() |
安装WGT热更新包、APK安装包 | plus.runtime.install(filePath, { force: true }, successCallback, errorCallback) |
plus.runtime.openURL() |
打开链接、调用第三方App | plus.runtime.openURL('tel:10086') // 拨打电话 |
plus.runtime.isApplicationExist() |
判断第三方App是否安装 | plus.runtime.isApplicationExist({ pname: 'com.tencent.mm' }) // 判断微信是否安装 |
4.2 文件系统管理(plus.io)
App端可完全访问本地文件系统,实现文件读写、文件夹管理、缓存清理等功能,核心用法示例:
1 | |
4.3 本地数据库(plus.sqlite)
App端支持SQLite本地数据库,可存储大量结构化离线数据,核心用法示例:
1 | |
4.4 原生UI与交互(plus.nativeUI)
App端可调用系统原生UI组件,体验比web组件更流畅,核心API:
| API | 功能说明 |
|---|---|
plus.nativeUI.toast() |
原生Toast提示,比uni.showToast更稳定 |
plus.nativeUI.alert() |
原生弹窗提示 |
plus.nativeUI.confirm() |
原生确认弹窗 |
plus.nativeUI.prompt() |
原生输入弹窗 |
plus.nativeUI.showWaiting() |
原生加载等待框,不可被点击穿透 |
plus.nativeUI.closeWaiting() |
关闭加载等待框 |
plus.nativeUI.pickDate() |
原生日期选择器 |
plus.nativeUI.pickTime() |
原生时间选择器 |
4.5 设备信息与系统能力(plus.device)
| 核心API | 功能说明 |
|---|---|
plus.device.uuid |
获取设备唯一标识 |
plus.device.model |
获取设备型号 |
plus.device.vendor |
获取设备厂商 |
plus.device.os |
获取设备系统名称(iOS/Android) |
plus.device.version |
获取系统版本号 |
plus.device.vibrate() |
设备震动 |
plus.device.setVolume() |
设置设备音量 |
plus.device.getInfo() |
获取完整设备信息 |
4.6 下载与上传(plus.downloader / plus.uploader)
App端支持后台下载、断点续传、批量上传,不受浏览器限制,核心用法示例:
1 | |
4.7 核心业务能力
- 支付能力:通过
uni.requestPayment调用微信支付、支付宝支付,需在manifest.json中勾选Payment模块,配置支付参数。 - 第三方登录:通过
uni.login调用微信、QQ、微博登录,需在manifest.json中勾选OAuth模块,配置平台参数。 - 分享能力:通过
uni.share分享内容到微信、QQ、微博等平台,需在manifest.json中勾选Share模块。 - 推送能力:通过
plus.push实现离线消息推送,需在manifest.json中勾选Push模块,配置推送平台参数。 - 地图与定位:通过
plus.maps、plus.geolocation实现地图展示、定位、路径规划,需在manifest.json中勾选Maps、Geolocation模块。
五、App端原生插件开发与集成
当UniApp内置API和Plus API无法满足需求时,可通过原生插件扩展App能力,实现和原生App完全一致的功能。
5.1 插件市场原生插件使用
- 打开DCloud插件市场,筛选「App原生插件」,选择符合需求的插件(免费/付费)。
- 点击「购买并下载」,绑定你的DCloud账号和App的包名/Bundle ID。
- 在HBuilderX中,右键项目 → 「原生插件」 → 「选用云端插件」,勾选已购买的插件。
- 制作自定义调试基座:HBuilderX → 「运行」 → 「运行到手机或模拟器」 → 「制作自定义调试基座」,等待云端打包完成。
- 使用自定义基座运行项目,即可调用插件API,插件用法参考插件文档。
注意:原生插件必须使用自定义调试基座才能生效,标准基座不包含原生插件,调用会报错。
5.2 本地原生插件集成
- 项目根目录创建
nativeplugins文件夹,将下载的原生插件解压到该目录。 - 在
manifest.json→ 「App原生插件配置」 → 「本地插件」中,勾选本地插件。 - 制作自定义调试基座,运行项目即可调用。
5.3 原生插件开发基础
- Android原生插件开发:使用Android Studio开发,基于UniApp的Android插件规范,继承
UniModule类,编写原生方法,通过@UniJSMethod注解暴露给前端调用。 - iOS原生插件开发:使用Xcode开发,基于UniApp的iOS插件规范,继承
DCUniModule类,编写原生方法,通过UNI_EXPORT_METHOD宏暴露给前端调用。 - 前端调用原生插件:
1
2
3
4
5
6
7
8// #ifdef APP-PLUS
// 引入原生插件
const MyPlugin = uni.requireNativePlugin('插件包名')
// 调用插件方法
MyPlugin.nativeMethod({ param1: 'test' }, (res) => {
console.log('原生方法返回结果:', res)
})
// #endif
5.4 离线打包
当需要深度修改原生代码、集成公司内部SDK、自定义原生功能时,需使用离线打包:
- 从DCloud官网下载对应版本的UniApp离线SDK。
- Android端:使用Android Studio打开离线SDK中的Android项目,配置包名、签名、模块、原生插件,编译生成APK。
- iOS端:使用Xcode打开离线SDK中的iOS项目,配置Bundle ID、证书、模块、原生插件,编译生成IPA。
六、App端数据通信与状态管理
6.1 全局状态管理
- Vue2项目:使用Vuex管理全局状态(用户信息、token、全局配置),适合中大型项目。
- Vue3项目:使用Pinia管理全局状态,比Vuex更简洁,支持TypeScript,推荐使用。
- 小型项目:使用
getApp().globalData挂载全局变量,简单便捷,适合少量全局数据。
6.2 页面间通信
- URL参数传递:最常用方式,通过
uni.navigateTo的url拼接参数,目标页面onLoad中接收,适合少量简单参数。 - 事件总线(EventBus):通过
uni.$emit触发事件,uni.$on监听事件,实现跨页面通信,必须在页面销毁时用uni.$off解绑,避免内存泄漏。1
2
3
4
5
6
7
8
9
10
11
12// 页面A:触发事件
uni.$emit('refreshList', { page: 1 })
// 页面B:监听事件
onLoad() {
uni.$on('refreshList', (data) => {
this.getList(data.page)
})
},
onUnload() {
uni.$off('refreshList')
} - 全局存储:通过
uni.setStorageSync/uni.getStorageSync存储和读取数据,适合大量数据、持久化数据传递。 - 页面栈获取:通过
getCurrentPages()获取页面栈实例,直接操作上一个页面的数据和方法,适合父子页面通信。1
2
3
4const pages = getCurrentPages()
const prevPage = pages[pages.length - 2]
prevPage.getList() // 调用上一页的方法
prevPage.$vm.status = 1 // 修改上一页的数据
6.3 原生层与前端层通信
- 原生插件通过回调函数、事件通知的方式,将原生层的数据传递给前端。
- 前端通过
uni.requireNativePlugin调用原生方法,传递参数给原生层。 - 通过
plus.globalEvent监听原生层的全局事件,实现原生到前端的主动通信。
七、App端专属性能优化全方案
7.1 启动速度优化(核心指标)
- 包体积瘦身:仅勾选必须的原生模块,剔除未使用的模块;图片、视频等资源压缩,非必要资源上传CDN;开启代码混淆、摇树优化,剔除冗余代码。
- 启动逻辑精简:
App.vue的onLaunch中,仅执行必须的初始化逻辑,非必须逻辑延迟到首页渲染完成后执行,避免阻塞启动。 - 首页优化:首页
onLoad/onShow中不做高耗时同步逻辑,先渲染骨架屏,再异步加载数据;首页DOM结构精简,减少嵌套层级。 - 渲染引擎优化:manifest.json中开启「v3编译器」「App端渲染加速」,提升渲染速度。
- 原生启动图优化:开启「等待首页渲染完成后关闭启动图」,避免启动后白屏;启动图精简,避免加载耗时资源。
7.2 内存优化(解决闪退、卡顿)
- 页面栈管理:App端页面栈无10层限制,但层级过深会导致内存占用过大,推荐使用
uni.redirectTo代替uni.navigateTo关闭不需要的页面,避免页面栈层级超过20层。 - 资源释放:页面
onUnload、组件beforeDestroy时,必须清理所有定时器、事件监听、websocket、canvas、地图、视频实例,避免内存泄漏。 - 图片内存优化:大图、长图使用缩略图,点击查看原图;图片设置固定宽高,避免渲染时缩放;大量图片使用虚拟列表,仅渲染可视区域图片,开启懒加载。
- 数据优化:避免在data中存放大量不渲染的数据,仅把需要渲染的数据放在data中;大列表数据分页加载,避免一次性渲染上万条数据。
7.3 渲染性能优化(解决卡顿、掉帧)
- DOM优化:页面DOM节点数控制在1000个以内,嵌套层级不超过10层;频繁切换的组件用
v-show,仅渲染一次的用v-if,避免频繁销毁重建。 - 长列表优化:超过100条的长列表,必须使用虚拟滚动,仅渲染可视区域节点,推荐使用
uni-list、mescroll-uni组件;列表item结构精简,减少嵌套层级。 - 动画优化:优先使用CSS3动画,避免JS动画;使用
transform、opacity实现动画,触发硬件加速,避免修改width、height、top等属性导致重排。 - 滚动优化:
onPageScroll中必须做节流处理,禁止频繁修改数据、操作DOM;长列表滚动时,暂停非必要的接口请求、图片加载。
7.4 包体积优化
- 原生模块:仅勾选必须的模块,剔除所有未使用的模块,每个模块都会增加包体积。
- CPU架构:Android端仅勾选
armeabi-v7a、arm64-v8a,减少包体积。 - 静态资源:所有图片、音频、视频资源必须压缩,非必要资源上传CDN,不打包到App中。
- 代码瘦身:开启代码压缩、摇树优化,剔除未使用的代码、组件、依赖;避免引入过大的第三方库,按需引入。
- 资源分包:非核心页面、资源放入分包,按需加载,减少主包体积。
7.5 热更新优化
- 区分整包更新和热更新:原生模块修改必须整包更新,仅前端代码修改使用WGT热更新。
- 热更新包体积优化:WGT包仅包含修改的文件,剔除未修改的资源,减少下载体积。
- 增量更新:实现增量更新,仅下载修改的文件,减少流量消耗和更新时间。
- 更新时机:App启动时、切前台时检查更新,非强制更新可在用户空闲时后台下载更新包。
八、App端调试、兼容与适配
8.1 调试方法
- 真机运行调试:HBuilderX连接手机,直接运行项目到真机,查看控制台日志。
- 自定义基座调试:集成原生插件后,必须制作自定义调试基座,使用自定义基座运行调试。
- Chrome开发者工具调试:App运行到手机后,打开Chrome浏览器,输入
chrome://inspect,即可调试App的WebView页面,查看DOM、Console、Network、Storage等。 - 原生日志调试:Android端使用Android Studio查看Logcat日志,iOS端使用Xcode查看控制台日志,排查原生模块报错。
- 离线包调试:打包后的APK/IPA,可通过HBuilderX的「云端打包」生成Debug包,连接调试工具排查问题。
8.2 屏幕适配
- 单位适配:优先使用
rpx单位(基于750px设计稿),配合flex弹性布局,适配不同屏幕尺寸。 - 安全区域适配:iOS端必须适配刘海屏、底部小黑条,使用
env(safe-area-inset-top)、env(safe-area-inset-bottom)设置内边距。 - 全面屏适配:Android端适配全面屏、挖孔屏、折叠屏,避免内容被遮挡。
- 字体适配:禁止使用固定字体大小,使用
rpx单位,适配系统字体大小调整。
8.3 系统版本兼容
- 最低版本设置:Android最低支持5.0+,iOS最低支持11.0+,覆盖99%以上活跃设备。
- API兼容:高版本系统新增的API,必须做版本判断,低版本系统做降级处理。
- 权限兼容:Android 10+分区存储权限、Android 13+通知权限、iOS 14+定位权限、iOS 15+隐私权限,必须做兼容处理。
- 厂商适配:适配华为、小米、OPPO、vivo等国产Android厂商的定制系统,如后台权限、自启动权限、推送权限。
8.4 iOS/Android平台差异兼容
| 差异点 | iOS端 | Android端 | 兼容方案 |
|---|---|---|---|
| 返回键 | 无物理返回键,仅侧滑返回、顶部返回按钮 | 有物理返回键,需监听backbutton事件 |
通过条件编译分别处理,Android端必须监听返回键事件 |
| 安全区域 | 刘海屏、底部小黑条,必须适配 | 大部分机型无底部安全区域,仅全面屏有顶部挖孔 | iOS端使用env(safe-area-inset-*)适配,Android端单独处理 |
| 权限申请 | 申请权限必须填写用途描述,一次申请,永久生效/拒绝 | 权限可多次申请,部分权限需要动态申请 | 统一封装权限申请方法,通过条件编译处理平台差异 |
| 状态栏 | 可自定义状态栏样式,沉浸式适配简单 | 不同厂商系统状态栏适配复杂,部分机型不支持状态栏文字颜色修改 | 使用plus.navigator设置状态栏样式,做机型兼容 |
| 安装包格式 | IPA格式,仅能通过App Store、企业证书安装 | APK格式,可直接安装、应用商店安装 | 整包更新区分平台,提供对应安装包 |
九、App端打包、签名与上架全流程
9.1 Android端打包、签名与上架
9.1.1 生成签名文件
- 安装JDK环境,使用
keytool命令生成.keystore签名文件:1
keytool -genkey -alias myapp -keyalg RSA -keysize 2048 -validity 36500 -keystore myapp.keystore - 填写别名、密码、姓名、组织、城市等信息,生成签名文件,妥善保管,丢失后无法更新应用商店的App。
9.1.2 打包生成正式APK
- 在HBuilderX中,打开
manifest.json→ 「App发行」 → 「原生App-云打包」。 - 选择Android平台,填写包名,选择「使用自有证书」,上传.keystore签名文件,填写别名、密码。
- 勾选「Release正式包」,选择CPU架构,开启代码混淆,点击「打包」,等待云端打包完成。
- 打包完成后,下载正式APK文件,进行加固、测试。
9.1.3 应用加固
上架前必须对APK进行加固,防止反编译、破解,推荐使用腾讯乐固、360加固、梆梆加固等平台。
9.1.4 上架应用商店
- 注册开发者账号:华为开发者联盟、小米开放平台、OPPO开放平台、vivo开放平台、应用宝等。
- 完善应用信息:应用名称、简介、图标、截图、资质文件、隐私政策、用户协议。
- 上传加固后的APK文件,填写版本号、更新日志。
- 提交审核,等待审核通过后,即可发布上线。
9.2 iOS端打包、签名与上架
9.2.1 申请证书与描述文件
- 注册Apple Developer开发者账号,年费99美元。
- 在开发者后台,创建App ID(Bundle ID),必须与manifest.json中的AppID完全一致。
- 生成开发证书、发布证书(.p12格式),安装到Mac电脑中。
- 生成开发描述文件、发布描述文件(.mobileprovision格式)。
9.2.2 打包生成正式IPA
- 在HBuilderX中,打开
manifest.json→ 「App发行」 → 「原生App-云打包」。 - 选择iOS平台,填写Bundle ID,上传发布证书(.p12)、发布描述文件(.mobileprovision),填写证书密码。
- 勾选「Release正式包」,点击「打包」,等待云端打包完成。
- 打包完成后,下载正式IPA文件。
9.2.3 上架App Store
- 登录App Store Connect后台,创建App,填写App信息、简介、图标、截图、隐私政策、资质文件。
- 使用Mac电脑,通过Transporter软件,将IPA文件上传到App Store Connect。
- 上传完成后,在App Store Connect中,创建新版本,选择上传的构建版本,填写更新日志。
- 提交审核,等待审核通过后,即可发布上线。
十、App端安全合规与上架避坑
10.1 安全防护
- 代码安全:开启代码混淆、加固,防止反编译、破解;敏感代码使用原生插件实现,避免前端明文存储。
- 数据安全:用户敏感信息加密存储,禁止明文存储;token设置有效期,定期续期;接口请求做签名校验,防止抓包篡改。
- 网络安全:所有接口使用HTTPS协议,禁止使用HTTP;证书校验,防止中间人攻击;敏感数据传输加密。
- 权限安全:遵循最小必要原则,仅申请功能必须的权限,不申请无关权限;权限申请必须明确告知用户用途,用户同意后才能使用。
10.2 合规要求
- 隐私合规:必须制定完整的《隐私政策》《用户协议》,App首次启动时弹窗提示,获取用户同意后,才能收集用户信息、申请权限;禁止未经用户同意收集个人信息、私自上传用户数据。
- 权限合规:禁止在用户未同意隐私政策前申请权限;禁止非必要场景申请权限;权限申请必须明确告知用途,用户拒绝后不能强制退出App。
- 内容合规:App内无违规内容、违法信息,符合国家法律法规;特殊类目(金融、医疗、教育、新闻等)必须提供对应资质文件。
- 支付合规:iOS端禁止使用微信支付、支付宝等第三方支付进行虚拟商品支付,必须使用苹果内购(IAP),否则会被App Store拒审。
10.3 上架避坑核心要点
- App Store上架避坑
- 必须提供完整的测试账号,供审核人员测试所有功能。
- 禁止隐藏违规功能、热更新违规内容。
- 虚拟商品必须使用苹果内购,禁止第三方支付。
- 所有申请的权限必须填写真实、清晰的用途描述。
- 禁止诱导用户好评、分享、下载。
- 必须提供完整的隐私政策链接,且可正常访问。
- Android应用商店上架避坑
- 必须提供软件著作权证书,大部分应用商店强制要求。
- 必须完成APP备案,2024年起国内应用商店强制要求。
- 隐私政策必须符合《个人信息保护法》,通过第三方隐私合规检测。
- 禁止私自收集用户信息、私自申请权限。
- 必须提供完整的资质文件,特殊类目必须提供对应许可证。