引言

uni-app是DCloud公司推出的一个使用Vue.js开发所有前端应用的框架,开发者编写一套代码,可发布到iOS、Android、Web等多个平台。对于想要快速开发跨平台APP的开发者来说,uni-app是一个非常优秀的选择。

本文将详细介绍使用uni-app开发APP并上架iOS的全过程,从开发环境搭建到最终App Store上架,提供完整的图文教程和实战经验。

开发环境搭建

1. 安装HBuilderX

HBuilderX是uni-app官方推荐的开发工具,集成了uni-app的开发环境。

步骤1:下载HBuilderX

步骤2:安装HBuilderX

  • 解压下载的压缩包
  • 运行HBuilderX.exe(Windows)或HBuilderX.app(Mac)

2. 创建uni-app项目

步骤1:新建项目

  1. 打开HBuilderX
  2. 点击”文件” -> “新建” -> “项目”
  3. 选择”uni-app” -> “默认模板”
  4. 填写项目名称和路径
1
2
3
4
5
6
7
8
9
10
11
12
// 项目结构示例
my-uniapp-project/
├── pages/ // 页面目录
│ ├── index/
│ │ └── index.vue // 首页
│ └── login/
│ └── login.vue // 登录页
├── static/ // 静态资源
├── App.vue // 应用入口
├── main.js // 主入口文件
├── manifest.json // 应用配置
└── pages.json // 页面配置

步骤2:项目配置
manifest.json中配置应用基本信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
{
"name": "我的APP",
"appid": "__UNI__XXXXXXX",
"description": "这是一个使用uni-app开发的跨平台应用",
"versionName": "1.0.0",
"versionCode": "100",
"transformPx": false,
"app-plus": {
"usingComponents": true,
"nvueStyleCompiler": "uni-app",
"compilerVersion": 3,
"splashscreen": {
"alwaysShowBeforeRender": true,
"waiting": true,
"autoclose": true,
"delay": 0
},
"modules": {},
"distribute": {
"android": {
"permissions": [
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\" />",
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\" />",
"<uses-permission android:name=\"android.permission.VIBRATE\" />",
"<uses-permission android:name=\"android.permission.READ_LOGS\" />",
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\" />",
"<uses-feature android:name=\"android.hardware.camera.autofocus\" />",
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\" />",
"<uses-permission android:name=\"android.permission.CAMERA\" />",
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\" />",
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\" />",
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\" />",
"<uses-permission android:name=\"android.permission.WAKE_LOCK\" />",
"<uses-permission android:name=\"android.permission.FLASHLIGHT\" />",
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\" />"
]
},
"ios": {
"appstore": "true"
},
"sdkConfigs": {}
}
},
"quickapp": {},
"mp-weixin": {
"appid": "",
"setting": {
"urlCheck": false
},
"usingComponents": true
},
"mp-alipay": {
"usingComponents": true
},
"mp-baidu": {
"usingComponents": true
},
"mp-toutiao": {
"usingComponents": true
},
"uniStatistics": {
"enable": false
},
"vueVersion": "3"
}

3. 开发环境配置

步骤1:安装Node.js

步骤2:安装uni-app CLI

1
2
3
4
5
6
# 全局安装uni-app CLI
npm install -g @dcloudio/uvm
uvm ls

# 安装HBuilderX CLI
npm install -g @dcloudio/cli

项目开发

1. 页面开发

首页开发示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
<!-- pages/index/index.vue -->
<template>
<view class="container">
<view class="header">
<text class="title">欢迎使用我的APP</text>
</view>

<view class="content">
<view class="card" v-for="(item, index) in list" :key="index">
<image :src="item.image" class="card-image"></image>
<view class="card-content">
<text class="card-title">{{ item.title }}</text>
<text class="card-desc">{{ item.description }}</text>
</view>
</view>
</view>

<view class="footer">
<button @click="goToLogin" class="login-btn">立即登录</button>
</view>
</view>
</template>

<script>
export default {
data() {
return {
list: [
{
title: '功能一',
description: '这是功能一的描述',
image: '/static/images/feature1.png'
},
{
title: '功能二',
description: '这是功能二的描述',
image: '/static/images/feature2.png'
},
{
title: '功能三',
description: '这是功能三的描述',
image: '/static/images/feature3.png'
}
]
}
},
methods: {
goToLogin() {
uni.navigateTo({
url: '/pages/login/login'
})
}
}
}
</script>

<style>
.container {
padding: 20rpx;
background-color: #f5f5f5;
min-height: 100vh;
}

.header {
text-align: center;
padding: 40rpx 0;
}

.title {
font-size: 36rpx;
font-weight: bold;
color: #333;
}

.content {
margin: 20rpx 0;
}

.card {
background-color: #fff;
border-radius: 10rpx;
padding: 20rpx;
margin-bottom: 20rpx;
display: flex;
align-items: center;
}

.card-image {
width: 80rpx;
height: 80rpx;
margin-right: 20rpx;
}

.card-content {
flex: 1;
}

.card-title {
font-size: 32rpx;
font-weight: bold;
color: #333;
display: block;
margin-bottom: 10rpx;
}

.card-desc {
font-size: 28rpx;
color: #666;
display: block;
}

.footer {
position: fixed;
bottom: 0;
left: 0;
right: 0;
padding: 20rpx;
background-color: #fff;
border-top: 1rpx solid #eee;
}

.login-btn {
width: 100%;
height: 80rpx;
background-color: #007aff;
color: #fff;
border-radius: 10rpx;
font-size: 32rpx;
border: none;
}
</style>

登录页面开发示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
<!-- pages/login/login.vue -->
<template>
<view class="login-container">
<view class="login-header">
<image src="/static/images/logo.png" class="logo"></image>
<text class="app-name">我的APP</text>
</view>

<view class="login-form">
<view class="input-group">
<input
v-model="username"
placeholder="请输入用户名"
class="input"
:class="{ 'error': usernameError }"
/>
<text v-if="usernameError" class="error-text">{{ usernameError }}</text>
</view>

<view class="input-group">
<input
v-model="password"
placeholder="请输入密码"
password
class="input"
:class="{ 'error': passwordError }"
/>
<text v-if="passwordError" class="error-text">{{ passwordError }}</text>
</view>

<button @click="handleLogin" class="login-btn" :disabled="loading">
{{ loading ? '登录中...' : '登录' }}
</button>
</view>

<view class="login-footer">
<text class="footer-text">还没有账号?</text>
<text @click="goToRegister" class="register-link">立即注册</text>
</view>
</view>
</template>

<script>
export default {
data() {
return {
username: '',
password: '',
usernameError: '',
passwordError: '',
loading: false
}
},
methods: {
validateForm() {
this.usernameError = ''
this.passwordError = ''

if (!this.username) {
this.usernameError = '请输入用户名'
return false
}

if (!this.password) {
this.passwordError = '请输入密码'
return false
}

if (this.password.length < 6) {
this.passwordError = '密码长度不能少于6位'
return false
}

return true
},

async handleLogin() {
if (!this.validateForm()) {
return
}

this.loading = true

try {
// 模拟登录请求
const result = await this.loginRequest()

if (result.success) {
uni.showToast({
title: '登录成功',
icon: 'success'
})

// 保存用户信息
uni.setStorageSync('userInfo', result.data)

// 跳转到首页
setTimeout(() => {
uni.switchTab({
url: '/pages/index/index'
})
}, 1500)
} else {
uni.showToast({
title: result.message || '登录失败',
icon: 'none'
})
}
} catch (error) {
uni.showToast({
title: '网络错误,请重试',
icon: 'none'
})
} finally {
this.loading = false
}
},

loginRequest() {
return new Promise((resolve) => {
setTimeout(() => {
if (this.username === 'admin' && this.password === '123456') {
resolve({
success: true,
data: {
id: 1,
username: this.username,
nickname: '管理员'
}
})
} else {
resolve({
success: false,
message: '用户名或密码错误'
})
}
}, 1000)
})
},

goToRegister() {
uni.navigateTo({
url: '/pages/register/register'
})
}
}
}
</script>

<style>
.login-container {
padding: 40rpx;
background-color: #f5f5f5;
min-height: 100vh;
}

.login-header {
text-align: center;
padding: 80rpx 0 60rpx;
}

.logo {
width: 120rpx;
height: 120rpx;
margin-bottom: 20rpx;
}

.app-name {
font-size: 40rpx;
font-weight: bold;
color: #333;
}

.login-form {
background-color: #fff;
border-radius: 20rpx;
padding: 40rpx;
margin-bottom: 40rpx;
}

.input-group {
margin-bottom: 30rpx;
}

.input {
width: 100%;
height: 80rpx;
border: 2rpx solid #eee;
border-radius: 10rpx;
padding: 0 20rpx;
font-size: 32rpx;
box-sizing: border-box;
}

.input.error {
border-color: #ff4757;
}

.error-text {
color: #ff4757;
font-size: 24rpx;
margin-top: 10rpx;
display: block;
}

.login-btn {
width: 100%;
height: 80rpx;
background-color: #007aff;
color: #fff;
border-radius: 10rpx;
font-size: 32rpx;
border: none;
margin-top: 20rpx;
}

.login-btn:disabled {
background-color: #ccc;
}

.login-footer {
text-align: center;
}

.footer-text {
font-size: 28rpx;
color: #666;
}

.register-link {
font-size: 28rpx;
color: #007aff;
margin-left: 10rpx;
}
</style>

2. 页面配置

pages.json配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
{
"pages": [
{
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "首页",
"enablePullDownRefresh": true
}
},
{
"path": "pages/login/login",
"style": {
"navigationBarTitleText": "登录",
"navigationStyle": "custom"
}
},
{
"path": "pages/register/register",
"style": {
"navigationBarTitleText": "注册"
}
},
{
"path": "pages/profile/profile",
"style": {
"navigationBarTitleText": "个人中心"
}
}
],
"globalStyle": {
"navigationBarTextStyle": "black",
"navigationBarTitleText": "我的APP",
"navigationBarBackgroundColor": "#F8F8F8",
"backgroundColor": "#F8F8F8"
},
"tabBar": {
"color": "#7A7E83",
"selectedColor": "#3cc51f",
"borderStyle": "black",
"backgroundColor": "#ffffff",
"list": [
{
"pagePath": "pages/index/index",
"iconPath": "static/images/home.png",
"selectedIconPath": "static/images/home-active.png",
"text": "首页"
},
{
"pagePath": "pages/profile/profile",
"iconPath": "static/images/profile.png",
"selectedIconPath": "static/images/profile-active.png",
"text": "我的"
}
]
},
"uniIdRouter": {}
}

iOS证书配置

1. Apple Developer账号准备

步骤1:注册Apple Developer账号

步骤2:加入Apple Developer Program

  • 点击”Enroll”加入开发者计划
  • 选择个人开发者或企业开发者
  • 支付年费(个人$99/年,企业$299/年)

2. 证书申请

步骤1:创建App ID

  1. 登录Apple Developer后台
  2. 进入”Certificates, Identifiers & Profiles”
  3. 点击”Identifiers” -> “+”
  4. 选择”App IDs” -> “Continue”
  1. 填写App ID信息:
    • Description: 应用描述
    • Bundle ID: 应用包名(如:com.yourcompany.yourapp)

步骤2:创建开发证书

  1. 点击”Certificates” -> “+”
  2. 选择”iOS App Development” -> “Continue”
  3. 上传CSR文件(Certificate Signing Request)

步骤3:创建发布证书

  1. 点击”Certificates” -> “+”
  2. 选择”iOS Distribution (App Store)” -> “Continue”
  3. 上传CSR文件

步骤4:创建Provisioning Profile

  1. 点击”Profiles” -> “+”
  2. 选择”iOS App Development” -> “Continue”
  3. 选择App ID -> “Continue”
  4. 选择证书 -> “Continue”
  5. 选择设备 -> “Continue”
  6. 填写Profile名称 -> “Generate”

3. HBuilderX证书配置

步骤1:配置iOS证书

  1. 打开HBuilderX
  2. 点击”发行” -> “原生App-云打包”
  3. 选择iOS平台
  4. 配置证书信息:
1
2
3
4
5
6
7
8
{
"ios": {
"appstore": "true",
"mobileprovision": "证书文件路径.mobileprovision",
"p12": "证书文件路径.p12",
"password": "证书密码"
}
}

步骤2:配置应用信息

  • App名称:在App Store显示的名称
  • Bundle ID:与Apple Developer中配置的一致
  • 版本号:应用的版本号
  • 构建号:每次打包递增

App Store上架流程

1. App Store Connect配置

步骤1:创建App

  1. 登录App Store Connect:https://appstoreconnect.apple.com/
  2. 点击”我的App” -> “+”
  3. 选择”新建App”
  4. 填写应用信息:
    • 平台:iOS
    • 名称:应用名称
    • 主要语言:简体中文
    • Bundle ID:选择已创建的App ID
    • SKU:应用唯一标识符

步骤2:配置应用信息

  1. 填写应用描述
  2. 上传应用截图(不同尺寸)
  3. 设置应用分类
  4. 配置应用价格

2. 应用打包上传

步骤1:云打包

  1. 在HBuilderX中点击”发行” -> “原生App-云打包”
  2. 选择iOS平台
  3. 配置打包参数
  4. 点击”打包”

步骤2:下载安装包

  • 打包完成后下载.ipa文件
  • 使用Application Loader或Xcode上传到App Store Connect

步骤3:上传到App Store Connect

  1. 打开Xcode
  2. 选择”Window” -> “Organizer”
  3. 选择”Archives”
  4. 点击”Distribute App”
  5. 选择”App Store Connect”
  6. 上传应用

3. 应用审核

步骤1:提交审核

  1. 在App Store Connect中选择应用
  2. 点击”构建版本”
  3. 选择上传的版本
  4. 填写审核信息
  5. 提交审核

步骤2:审核要点

  • 应用功能完整性
  • 用户界面友好性
  • 隐私政策合规性
  • 内容符合App Store审核指南

常见问题解决

1. 证书问题

问题1:证书过期

1
2
3
4
# 解决方案:重新生成证书
# 1. 删除过期证书
# 2. 重新创建证书
# 3. 更新Provisioning Profile

问题2:Bundle ID不匹配

1
2
3
4
5
6
7
8
9
10
// 解决方案:检查manifest.json配置
{
"app-plus": {
"distribute": {
"ios": {
"bundleId": "com.yourcompany.yourapp"
}
}
}
}

2. 打包问题

问题1:打包失败

  • 检查证书配置是否正确
  • 确认网络连接正常
  • 查看打包日志错误信息

问题2:应用崩溃

  • 检查代码语法错误
  • 确认API调用正确
  • 测试真机运行

3. 审核问题

问题1:审核被拒

  • 仔细阅读拒绝原因
  • 修改相关问题
  • 重新提交审核

问题2:隐私政策缺失

  • 添加隐私政策页面
  • 在应用描述中说明
  • 确保合规性

性能优化建议

1. 代码优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// 使用条件编译优化代码
// #ifdef APP-PLUS
// APP端特有代码
// #endif

// #ifdef H5
// H5端特有代码
// #endif

// 图片懒加载
<image
:src="item.image"
lazy-load
@load="onImageLoad"
@error="onImageError"
></image>

// 列表优化
<scroll-view
scroll-y
:refresher-enabled="true"
:refresher-triggered="refreshing"
@refresherrefresh="onRefresh"
@scrolltolower="onLoadMore"
>
<view v-for="item in list" :key="item.id">
<!-- 列表项内容 -->
</view>
</scroll-view>

2. 资源优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 图片压缩
// 使用webp格式
// 合理设置图片尺寸

// 代码分包
// 使用uni-app的分包功能
{
"subPackages": [
{
"root": "pages/sub",
"pages": [
{
"path": "page1/page1",
"style": {
"navigationBarTitleText": "页面1"
}
}
]
}
]
}

总结

使用uni-app开发APP并上架iOS是一个系统性的工程,需要掌握以下关键技能:

  1. 开发技能:Vue.js、uni-app框架、移动端开发
  2. 证书管理:Apple Developer账号、证书申请、配置管理
  3. 上架流程:App Store Connect、应用审核、版本管理
  4. 问题解决:常见问题排查、性能优化、合规性

通过本文的详细教程,相信您已经掌握了uni-app开发iOS应用的全过程。在实际开发中,建议:

  • 充分测试应用功能
  • 关注App Store审核指南
  • 持续优化应用性能
  • 及时处理用户反馈

参考资料

  1. uni-app官方文档
  2. Apple Developer文档
  3. App Store审核指南
  4. HBuilderX使用手册
  5. Vue.js官方文档