小程序常见问题
小程序开发中的常见问题及解决方案
问题
小程序开发中有哪些常见问题?如何解决?
解答
1. setData 性能问题
小程序采用双线程架构,逻辑层和渲染层通过 setData 通信,频繁或大量数据传输会导致性能问题。
// ❌ 错误:频繁调用 setData
for (let i = 0; i < 100; i++) {
this.setData({ [`list[${i}]`]: data[i] })
}
// ❌ 错误:传输大量无关数据
this.setData({
userInfo: this.data.userInfo, // 未变化的数据
list: newList
})
// ✅ 正确:合并更新,只传必要数据
this.setData({
list: newList
})
// ✅ 正确:使用路径更新
this.setData({
'list[0].name': 'new name',
'userInfo.avatar': 'new url'
})
2. 页面栈限制
小程序页面栈最多 10 层,超出后 navigateTo 会失败。
// 获取当前页面栈
const pages = getCurrentPages()
console.log('当前页面栈深度:', pages.length)
// 页面跳转前检查
function navigateTo(url) {
const pages = getCurrentPages()
if (pages.length >= 10) {
// 使用 redirectTo 替换当前页
wx.redirectTo({ url })
} else {
wx.navigateTo({ url })
}
}
// 返回多级页面
wx.navigateBack({ delta: 2 })
// 跳转到 tabBar 页面
wx.switchTab({ url: '/pages/home/index' })
// 关闭所有页面,打开新页面
wx.reLaunch({ url: '/pages/index/index' })
3. 跨页面通信
// 方案一:EventBus
const eventBus = {
events: {},
on(name, callback) {
(this.events[name] || (this.events[name] = [])).push(callback)
},
emit(name, data) {
(this.events[name] || []).forEach(cb => cb(data))
},
off(name, callback) {
if (!callback) {
this.events[name] = []
} else {
this.events[name] = (this.events[name] || []).filter(cb => cb !== callback)
}
}
}
// 页面 A:监听
eventBus.on('updateList', (data) => {
this.setData({ list: data })
})
// 页面 B:触发
eventBus.emit('updateList', newList)
// 方案二:获取页面实例直接调用
const pages = getCurrentPages()
const prevPage = pages[pages.length - 2]
prevPage.setData({ needRefresh: true })
// 方案三:全局状态管理
const app = getApp()
app.globalData.userInfo = newUserInfo
4. 分包加载
主包限制 2MB,总包限制 20MB。
// app.json
{
"pages": [
"pages/index/index"
],
"subpackages": [
{
"root": "packageA",
"pages": ["pages/detail/index"]
},
{
"root": "packageB",
"pages": ["pages/list/index"],
"independent": true // 独立分包,可独立运行
}
],
"preloadRule": {
"pages/index/index": {
"network": "all",
"packages": ["packageA"] // 预加载分包
}
}
}
5. 生命周期执行顺序
// App 生命周期
App({
onLaunch() {}, // 小程序初始化,全局只触发一次
onShow() {}, // 小程序启动或从后台进入前台
onHide() {} // 小程序从前台进入后台
})
// Page 生命周期
Page({
onLoad() {}, // 页面加载,只调用一次
onShow() {}, // 页面显示,每次打开都调用
onReady() {}, // 页面初次渲染完成
onHide() {}, // 页面隐藏
onUnload() {} // 页面卸载
})
// Component 生命周期
Component({
lifetimes: {
created() {}, // 组件实例创建
attached() {}, // 组件进入页面节点树
ready() {}, // 组件布局完成
detached() {} // 组件离开页面节点树
},
pageLifetimes: {
show() {}, // 所在页面显示
hide() {} // 所在页面隐藏
}
})
6. 授权问题处理
// 检查授权状态
wx.getSetting({
success(res) {
if (res.authSetting['scope.userLocation']) {
// 已授权
wx.getLocation({ type: 'wgs84' })
} else if (res.authSetting['scope.userLocation'] === false) {
// 曾拒绝,需引导用户打开设置
wx.showModal({
title: '提示',
content: '需要获取位置权限',
success(res) {
if (res.confirm) {
wx.openSetting()
}
}
})
} else {
// 未请求过,发起授权
wx.authorize({
scope: 'scope.userLocation',
success() {
wx.getLocation({ type: 'wgs84' })
}
})
}
}
})
// 获取用户信息(需要 button 触发)
// <button open-type="getUserInfo" bindgetuserinfo="onGetUserInfo">授权</button>
7. 请求封装
// utils/request.js
const BASE_URL = 'https://api.example.com'
function request(options) {
return new Promise((resolve, reject) => {
wx.showLoading({ title: '加载中' })
wx.request({
url: BASE_URL + options.url,
method: options.method || 'GET',
data: options.data,
header: {
'Authorization': wx.getStorageSync('token'),
...options.header
},
success(res) {
if (res.statusCode === 200) {
resolve(res.data)
} else if (res.statusCode === 401) {
// token 过期,跳转登录
wx.navigateTo({ url: '/pages/login/index' })
reject(res)
} else {
wx.showToast({ title: '请求失败', icon: 'none' })
reject(res)
}
},
fail: reject,
complete() {
wx.hideLoading()
}
})
})
}
export const get = (url, data) => request({ url, data, method: 'GET' })
export const post = (url, data) => request({ url, data, method: 'POST' })
关键点
- setData 优化:减少调用频率,只传必要数据,使用路径更新
- 页面栈管理:注意 10 层限制,合理使用 redirectTo、reLaunch
- 分包加载:主包放核心页面,业务模块分包,配置预加载
- 跨页面通信:EventBus、getCurrentPages、globalData 三种方案
- 授权处理:区分未授权、已拒绝、已授权三种状态
目录