第5章 5.4 自定义基座与真机调试
上一章我们学会了怎么把小程序发布到微信公众平台,从「写完代码」正式走到了「让人能用」。但发布之前有个关键环节被很多人忽略了——真机调试。你可能在电脑上模拟得完美无缺,结果到真机上闪退、样式乱飞、甚至白屏。问题就出在「我没在真机上跑过」。这一章,我们来解决这个问题。
🎯 开场 3 分钟:为什么要学这个?
场景还原:电脑没问题,手机出事了
想象你开了家外卖店,厨房里做好的菜摆盘精美,但客人收到的时候汤洒了、菜凉了——问题出在「外卖打包和配送」这个环节。
写代码也一样。HBuilderX 的模拟器就是你的厨房,微信开发者工具就是外卖打包,而真机才是客人真正吃到的样子。
你有没有遇到过这些崩溃现场?
- 电脑上好好的小程序,点开就闪退
- 样式在模拟器里漂漂亮亮,真机上乱成一团
- API 报错,但电脑上完全正常
原因就一个:你没在真机上调试过。
这一章你能解决什么?
学完本文,你将掌握:
- 什么是自定义基座,为什么不能用默认\n\n
\n\n
\n\n基座 - 如何配置 iOS 和 Android 的自定义基座
- 怎么用「扫码联调」实时看真机日志
- 遇到真机问题怎么快速定位(真机日志技巧)
🧱 基础 25 分钟:核心概念
5.4.1 先搞懂什么是「基座」
是什么?
基座,你可以理解成运行小程序的「手机系统」。你写的小程序不是直接跑在手机上的,它需要跑在一个「壳」里面,这个壳就是基座。
UniApp 官方给了一个默认基座,就像快餐店提供的通用餐盒。但如果你想加个鸡腿、加点调料,对不起,通用餐盒装不下——你得用自定义基座。
为什么要用自定义基座?
| 场景 | 默认基座能搞定吗 | 自定义基座才能干啥 |
|---|---|---|
| 调用微信支付 | ❌ 不行 | ✅ 需要配置微信支付参数 |
| 第三方推送推送 | ❌ 不行 | ✅ 需要填 AppKey |
| 特殊权限(如获取手机号) | ❌ 不行 | ✅ 需要配置 manifest |
| 调式时看详细日志 | ⚠️ 能看一点 | ✅ 能看完整日志 |
怎么用?
打开 HBuilderX → 运行 → 运行到手机或模拟器 → 制作自定义基座
简单说:自定义基座 = 给你的小程序量身定制的「外卖餐盒」,装得下所有功能。
5.4.2 iOS 自定义基座配置
是什么?
iOS 基座就是让你的小程序跑在苹果手机上。配置 iOS 基座需要苹果开发者账号(个人或企业)。
为什么要用?
如果你的小程序要上线 App Store,或者用户主要是苹果用户,那你必须测试 iOS 基座。
怎么用?
第一步:获取证书
- 登录 Apple Developer 官网
- 进入 Certificates, Identifiers & Profiles
- 创建 iOS Development 证书(开发用)或 iOS Distribution 证书(发布用)
- 下载
.cer证书文件,双击导入 Mac 的「钥匙串访问」
第二步:导出 p12 证书
- 打开「钥匙串访问」应用
- 找到刚才导入的证书
- 右键 → 导出 → 保存为
.p12格式(记得设密码)
第三步:配置到 HBuilderX
// 在 HBuilderX 中打开 manifest.json
// 切换到「App 图标」视图
// 在「证书」区域:
// - 选择证书类型:iOS 开发证书 / iOS 发布证书
// - 上传 .p12 证书文件
// - 输入证书密码
第四步:制作基座
运行 → 运行到手机或模拟器 → 制作自定义基座 → 勾选 iOS → 选择证书 → 开始制作
注意!第一次制作基座大概需要 5-10 分钟,耐心等待,别以为卡死了。
5.4.3 Android 自定义基座配置
是什么?
Android 基座让你的小程序跑在安卓手机上。安卓相对苹果更开放,配置更简单。
为什么要用?
- 安卓市场份额大(国内尤其)
- 调试方便,不需要证书(开发阶段)
- 能接更多第三方 SDK
怎么用?
第一步:申请微信开放平台账号
微信支付、微信登录等功能需要微信开放平台的 AppID。
第二步:获取应用签名
使用「微信开放平台签名工具」APK,或者用命令行生成:
# 使用 keytool 生成签名(需要先有 keystore)
keytool -list -v -keystore your-keystore.jks -alias your-alias
第三步:在 manifest.json 配置
{
"appid": "__UNI__XXXXXXXX", // 替换成你的 AppID
"name": "我的小程序",
"platforms": {
"android": {
"minSdkVersion": 21,
"targetSdkVersion": 30
}
}
}
第四步:制作基座
运行 → 运行到手机或模拟器 → 制作自定义基座 → 勾选 Android → 开始制作
5.4.4 扫码联调:电脑和手机「同步」
是什么?
扫码联调就是用手机扫描电脑上的二维码,让手机和电脑保持连接,你改代码,手机实时刷新。
生活类比:遥控器和电视
扫码联调就像给电视配遥控器——配对之后,你按遥控器(改代码),电视(手机)立即响应。
为什么要用?
- 不用每次改代码都重新打包安装
- 能实时看控制台日志
- 手机上的操作能触发电脑上的断点(条件:基座开启了调试模式)
怎么用?
第一步:确保电脑和手机在同一 WiFi 下
这一步很多人忘,导致扫完码没反应。
第二步:基座开启调试模式
运行 → 运行到手机或模拟器 → 运行基座控制台 → 勾选「开启调试」
第三步:手机扫二维码
- HBuilderX 选择 运行 → 运行到手机或模拟器 → 手机扫描二维码
- 用手机基座内置的「扫码」功能扫二维码
- 连接成功后,手机上会显示「已连接」
第四步:验证连接
手机扫码后,HBuilderX 底部控制台会显示:
[设备连接] iPhone 12 - 192.168.1.100:3000
现在你在 HBuilderX 改代码,保存后手机会自动刷新。
5.4.5 真机日志:定位问题的「黑匣子」
是什么?
真机日志就是手机端运行小程序的「行车记录仪」,记录了每一行代码执行的情况、错误信息、网络请求等。
为什么要用?
电脑上的控制台只能看模拟情况,真机上遇到的问题(如支付失败、定位不准、第三方 SDK 报错)只有在真机日志里才能看到真相。
怎么用?
方式一:HBuilderX 控制台日志
扫码联调成功后,HBuilderX 底部会显示手机端的 console.log 输出:
// 在你的代码里加日志
console.log('用户点击了按钮')
console.log('请求参数:', params)
console.error('出错了,错误信息:', error)
连接成功后,这些日志会实时显示在 HBuilderX 控制台。
方式二:手机端查看
在手机基座上:
1. 点击基座右上角的菜单(三条横线)
2. 选择「控制台」或「日志」
3. 查看完整的控制台输出和错误堆栈
方式三:vConsole(推荐)
在 main.js 或 App.vue 的 onLaunch 里加入:
// #ifdef MP-WEIXIN || H5
// 微信小程序或H5开启vConsole
import vConsole from 'vconsole'
new vConsole()
// #endif
这样手机端会显示一个小绿标,点开就能看到类似电脑开发者工具的调试面板。
🔥 实战 35 分钟:3 个递进小项目
项目 1(5 分钟):制作并安装 Android 自定义基座
目标:亲手制作一个 Android 自定义基座,安装到手机上。
完整代码:
这个项目没有代码,是操作练习。按照上面的「5.4.3 Android 自定义基座配置」走一遍即可。
预期输出:
- HBuilderX 控制台显示:
[基座制作完成] Android 基座已生成 - 手机上出现一个新的「自定义基座」App 图标
- 打开后显示 UniApp 的欢迎页面
一句话解释:
自定义基座就是给你的小程序造的「专用手机」,第一次造比较慢,之后就快了。
项目 2(15 分钟):真机日志定位「支付失败」问题
目标:模拟一个支付场景,用真机日志定位问题。
完整代码:
// pages/pay/pay.js
Page({
data: {
orderId: '',
amount: 0
},
onLoad(options) {
// 模拟从上一个页面传来的订单信息
this.setData({
orderId: options.orderId || 'ORDER20240115001',
amount: options.amount || 99.00
})
console.log('订单页面加载,订单号:', this.data.orderId)
},
// 点击支付按钮
goPay() {
console.log('开始支付,订单号:', this.data.orderId)
// 模拟调用微信支付
uni.request({
url: 'https://api.example.com/pay',
method: 'POST',
data: {
orderId: this.data.orderId,
amount: this.data.amount,
payType: 'wechat'
},
success: (res) => {
console.log('支付接口返回:', res)
if (res.data.code === 200) {
console.log('支付成功!')
uni.showToast({
title: '支付成功',
icon: 'success'
})
} else {
console.error('支付失败,错误码:', res.data.code)
uni.showToast({
title: '支付失败:' + res.data.message,
icon: 'none'
})
}
},
fail: (err) => {
console.error('网络请求失败:', err)
uni.showToast({
title: '网络开小差了',
icon: 'none'
})
}
})
}
})
<!-- pages/pay/pay.vue -->
<template>
<view class="container">
<view class="order-info">
<text class="label">订单号:</text>
<text class="value">{{ orderId }}</text>
</view>
<view class="order-info">
<text class="label">金额:</text>
<text class="value price">¥{{ amount }}</text>
</view>
<button type="primary" @tap="goPay">立即支付</button>
</view>
</template>
<style>
.container {
padding: 40rpx;
}
.order-info {
display: flex;
justify-content: space-between;
padding: 20rpx 0;
border-bottom: 1rpx solid #eee;
}
.label {
color: #666;
}
.value {
color: #333;
font-weight: bold;
}
.price {
color: #ff6600;
font-size: 40rpx;
}
</style>
预期输出(HBuilderX 控制台):
订单页面加载,订单号: ORDER20240115001
开始支付,订单号: ORDER20240115001
支付接口返回: { data: { code: 500, message: '签名校验失败' } }
支付失败,错误码: 500
一句话解释:
通过
console.log和console.error打印关键节点,真机日志能帮你看到「哪一步」出了问题,就像查看外卖配送的实时轨迹。
项目 3(15 分钟):扫码联调 + 日志组合实战
目标:做一个「用户反馈收集器」,通过扫码联调实时调试表单提交流程。
完整代码:
// pages/feedback/feedback.js
Page({
data: {
feedbackType: '',
feedbackContent: '',
contact: '',
types: [
{ id: 'bug', name: '功能 Bug' },
{ id: 'suggest', name: '建议反馈' },
{ id: 'other', name: '其他' }
]
},
onLoad() {
console.log('反馈页面初始化')
},
// 选择反馈类型
selectType(e) {
const type = e.currentTarget.dataset.type
this.setData({
feedbackType: type
})
console.log('用户选择类型:', type)
},
// 提交反馈
submitFeedback() {
// 校验必填项
if (!this.data.feedbackType) {
console.warn('提交失败:未选择反馈类型')
uni.showToast({
title: '请选择反馈类型',
icon: 'none'
})
return
}
if (!this.data.feedbackContent) {
console.warn('提交失败:反馈内容为空')
uni.showToast({
title: '请输入反馈内容',
icon: 'none'
})
return
}
console.log('开始提交反馈:', {
type: this.data.feedbackType,
content: this.data.feedbackContent,
contact: this.data.contact || '匿名'
})
// 模拟提交
uni.request({
url: 'https://api.example.com/feedback',
method: 'POST',
data: {
type: this.data.feedbackType,
content: this.data.feedbackContent,
contact: this.data.contact || '匿名',
createTime: new Date().toISOString()
},
success: (res) => {
console.log('反馈提交结果:', res)
if (res.data.code === 200) {
uni.showToast({
title: '提交成功,感谢反馈!',
icon: 'success'
})
// 清空表单
this.setData({
feedbackType: '',
feedbackContent: '',
contact: ''
})
}
},
fail: (err) => {
console.error('反馈提交失败:', err)
uni.showToast({
title: '提交失败,请稍后重试',
icon: 'none'
})
}
})
}
})
<!-- pages/feedback/feedback.vue -->
<template>
<view class="feedback-page">
<view class="section">
<text class="section-title">反馈类型</text>
<view class="type-list">
<view
v-for="item in types"
:key="item.id"
:class="['type-item', { active: feedbackType === item.id }]"
@tap="selectType"
:data-type="item.id"
>
{{ item.name }}
</view>
</view>
</view>
<view class="section">
<text class="section-title">反馈内容(必填)</text>
<textarea
class="content-input"
v-model="feedbackContent"
placeholder="请详细描述您的问题或建议..."
maxlength="500"
/>
</view>
<view class="section">
<text class="section-title">联系方式(选填)</text>
<input
class="contact-input"
v-model="contact"
placeholder="手机号或邮箱,方便我们联系您"
/>
</view>
<button class="submit-btn" type="primary" @tap="submitFeedback">
提交反馈
</button>
</view>
</template>
<style>
.feedback-page {
padding: 30rpx;
}
.section {
margin-bottom: 40rpx;
}
.section-title {
display: block;
font-size: 28rpx;
color: #333;
margin-bottom: 20rpx;
font-weight: bold;
}
.type-list {
display: flex;
gap: 20rpx;
flex-wrap: wrap;
}
.type-item {
padding: 16rpx 32rpx;
background: #f5f5f5;
border-radius: 8rpx;
font-size: 26rpx;
}
.type-item.active {
background: #007AFF;
color: #fff;
}
.content-input {
width: 100%;
height: 240rpx;
padding: 20rpx;
background: #f5f5f5;
border-radius: 8rpx;
font-size: 28rpx;
box-sizing: border-box;
}
.contact-input {
width: 100%;
padding: 20rpx;
background: #f5f5f5;
border-radius: 8rpx;
font-size: 28rpx;
box-sizing: border-box;
}
.submit-btn {
margin-top: 60rpx;
}
</style>
预期输出(HBuilderX 控制台):
反馈页面初始化
用户选择类型: bug
开始提交反馈: { type: 'bug', content: '支付后没跳转', contact: '138****8888' }
反馈提交结果: { data: { code: 200, message: 'success' } }
一句话解释:
扫码联调让你改代码时手机实时刷新,而日志让你知道每一步用户做了什么、服务器返回了什么,就像有个助理在旁边给你汇报进度。
💪 进阶 20 分钟:常见坑 + 性能小贴士
坑 1:扫码连不上,WiFi 不是同一个
❌ 错误做法:
手机连着数据流量,电脑连着 WiFi,扫码后一直转圈圈。
✅ 正确做法:
1. 确保手机和电脑连的是同一个 WiFi 路由器
2. 确认防火墙没有挡住 3000 端口
3. 试试把 WiFi 断开重连
坑 2:iOS 基座提示「证书不受信任」
❌ 错误做法:
换个证书继续试,或者直接用 Android 代替。
✅ 正确做法:
1. 检查证书是否过期(一般 1 年)
2. 检查证书是否匹配 App 的 Bundle ID
3. 如果是自签名证书,需要在手机上「信任证书」:设置 → 通用 → 关于本机 → 证书信任设置
坑 3:真机日志看不到 console.log
❌ 错误做法:
疯狂加 console.log,觉得是代码问题。
✅ 正确做法:
1. 确认基座是「自定义基座」而不是「默认基座」
2. 检查 HBuilderX 控制台是否显示「设备已连接」
3. 手机端检查:基座菜单 → 设置 → 控制台 → 是否开启
坑 4:Android 打包后签名不一致
❌ 错误做法:
用 debug 签名打包发布,结果微信审核被拒。
✅ 正确做法:
1. 开发阶段和发布阶段用不同的 keystore
2. 微信开放平台登记的签名必须和正式包签名一致
3. 每次打包前核对签名指纹
坑 5:vConsole 在正式包没去掉
❌ 错误做法:
代码里写了 new vConsole(),发布后用户看到小绿标,影响体验。
✅ 正确做法:
用条件编译,只在开发模式开启:
// #ifdef H5 || MP-WEIXIN
// 仅在 H5 或微信小程序环境开启 vConsole
import vConsole from 'vconsole'
new vConsole()
// #endif
性能小贴士:日志也要「断舍离」
真机日志虽好,但太多日志会影响性能。在正式环境前,把 console.log 删掉或用条件编译包裹:
// 生产环境关闭日志
const isDev = process.env.NODE_ENV === 'development'
function log(...args) {
if (isDev) {
console.log(...args)
}
}
调试技巧:远程日志「快照」
遇到复现困难的 bug,可以让用户帮忙抓日志:
// 在出错的地方加「快照」
function captureSnapshot(data) {
const snapshot = {
time: new Date().toISOString(),
url: uni.getSystemInfoSync().platform,
data: data
}
// 可以上传到服务器
uni.request({
url: 'https://api.example.com/log',
method: 'POST',
data: snapshot
})
}
✏️ 练习题 + 作业题
练习题(10 分钟)
练习 1(2 分钟):找基座类型
- 输入:打开 HBuilderX,查看当前运行菜单
- 预期输出:列出「默认基座」和「自定义基座」的区别(用自己的话写 2-3 句)
- 提示:默认基座是通用的,自定义基座是定制的
练习 2(2 分钟):加一个日志
- 输入:在项目 2 的 goPay 函数开头加一行 console.log('准备发起支付请求')
- 预期输出:刷新页面后,HBuilderX 控制台出现这行日志
- 提示:用扫码联调方式,保存即刷新
练习 3(3 分钟):修复「类型未选」提示
- 输入:项目 3 中,用户没选类型就点提交,预期提示「请选择反馈类型」
- 预期输出:实际运行时看到 console.warn 输出
- 提示:检查 submitFeedback 函数里的校验逻辑
练习 4(3 分钟):换个反馈类型
- 输入:把项目 3 的反馈类型从 ['bug', 'suggest', 'other'] 改成 ['功能异常', '体验建议', '新功能需求', '其他']
- 预期输出:页面上显示中文选项,功能正常
- 提示:记得同时改 data-type 的值
练习 5(挑战题,5 分钟):分析日志报错
- 输入:假设你在真机调试时看到如下日志:
console.error: "支付失败,错误码:" 400
- 预期输出:分析可能的原因(至少说 2 种)
- 提示:400 通常是「参数错误」或「签名错误」
作业题(30 分钟 - 2 小时)
作业:做一个「真机调试实战工具」
需求描述:
做一个简易的「手机信息展示器」,让用户能看到自己手机的系统信息,同时这个页面要能在真机上正常展示。
功能点:
1. 展示手机品牌、型号、操作系统版本
2. 展示屏幕分辨率、像素密度
3. 展示当前网络状态(WiFi/4G/无网络)
4. 点击「刷新」按钮重新获取信息
5. 在控制台打印每一次刷新的日志
加分项:
1. 展示磁盘可用空间
2. 把信息保存到本地缓存,下次打开自动填充
验收标准:
- 能跑起来(在模拟器和真机都行)
- 手机信息正确显示
- 控制台有刷新日志输出
- 代码有适当注释
提交方式:评论区贴代码或 GitHub 链接
📚 总结 + 资源
这一章的核心 3 点
- 自定义基座 = 量身定制的运行环境,让你能用微信支付、第三方 SDK 等高级功能
- 扫码联调 = 电脑改代码、手机实时看效果,大大提高调试效率
- 真机日志 = 定位问题的「黑匣子」,电脑模拟器看不到的真相,真机日志告诉你
延伸学习资源
- UniApp 官方文档 - 自定义基座
- 微信支付接入指南
- 《移动端性能优化》- 推荐有一定基础后深入学习
你在真机调试时遇到过最奇葩的问题是什么? 是 iOS 证书报错?还是 Android 签名不一致?或者基座打包等了半小时?评论区聊聊,老粉优先回复!下一章我们要进入「综合实战:仿美团外卖小程序」,把前面学到的真机调试技能真正用起来,做一个能跑的小外卖点餐应用。

评论(0)