7.3 KiB
7.3 KiB
开发最佳实践与常见坑
综合官方文档与社区经验
setData 性能优化
setData 是小程序性能的关键瓶颈,因为数据需要从逻辑层(JS 线程)序列化后传输到视图层(渲染线程)。
原则
- 减少数据量:只传需要更新的字段
// ❌ 错误:传整个列表
this.setData({ list: this.data.list })
// ✅ 正确:只更新变化的项
this.setData({ 'list[2].name': 'new name' })
- 减少调用频率:合并多次 setData
// ❌ 错误:多次调用
this.setData({ a: 1 })
this.setData({ b: 2 })
this.setData({ c: 3 })
// ✅ 正确:合并为一次
this.setData({ a: 1, b: 2, c: 3 })
- 后台页面不要 setData
// ❌ 错误:页面隐藏后仍在 setData(如定时器)
onShow() {
this._timer = setInterval(() => {
this.setData({ time: Date.now() })
}, 1000)
},
// ✅ 正确:页面隐藏时停止
onHide() {
clearInterval(this._timer)
}
- 避免在 onPageScroll 中 setData
// ❌ 错误
onPageScroll(e) {
this.setData({ scrollTop: e.scrollTop })
}
// ✅ 正确:用 WXS 响应事件或节流
onPageScroll: throttle(function(e) {
if (this._needUpdate) {
this.setData({ isTop: e.scrollTop < 100 })
}
}, 100)
- 大列表用纯数据字段
Component({
options: { pureDataPattern: /^_/ },
data: {
displayList: [], // 用于渲染
_rawList: [] // 纯数据,不传输到视图层
}
})
分包加载
基本分包
// app.json
{
"pages": [
"pages/index/index",
"pages/logs/logs"
],
"subpackages": [
{
"root": "packageA",
"name": "pack-a",
"pages": [
"pages/cat/cat",
"pages/dog/dog"
]
},
{
"root": "packageB",
"name": "pack-b",
"pages": [
"pages/apple/apple"
]
}
]
}
限制:
- 整个小程序所有分包大小不超过 20MB(使用分包时)
- 单个分包/主包大小不超过 2MB
- tabBar 页面必须在主包
独立分包
不依赖主包即可运行,适合独立功能页面(如活动页)。
{
"subpackages": [
{
"root": "packageIndependent",
"pages": ["pages/activity/activity"],
"independent": true
}
]
}
注意:独立分包中不能使用主包的公共资源(js/组件/样式)。
分包预下载
{
"preloadRule": {
"pages/index/index": {
"network": "all",
"packages": ["packageA"]
},
"pages/logs/logs": {
"network": "wifi",
"packages": ["packageB"]
}
}
}
小程序与 H5 的关键差异
| 特性 | H5 | 小程序 |
|---|---|---|
| DOM 操作 | 支持 document/window | ❌ 不支持 |
| BOM | 支持 | ❌ 不支持 |
| 路由 | URL hash/history | 页面栈(最多 10 层) |
| 样式 | 完整 CSS | WXSS(部分 CSS 不支持) |
| 脚本 | 完整 JS + Web API | JS(无 DOM API) |
| 渲染 | 单线程 | 双线程(逻辑层 + 视图层) |
| 网络请求 | fetch/XMLHttpRequest | wx.request(需配置域名) |
| 本地存储 | localStorage | wx.setStorage(10MB) |
| Cookie | 支持 | ❌ 不支持(需自行管理) |
| 动态创建元素 | 支持 | ❌ 不支持 |
| eval / new Function | 支持 | ❌ 不支持 |
| SVG | 支持 | 部分支持(image src 可用) |
常见迁移坑
- 没有 Cookie:登录态需要自行通过 header 传递 token
- 没有 DOM:不能用 jQuery、不能
document.getElementById - 不支持动态执行代码:
eval()、new Function()都不可用 - 样式差异:
- 不支持
*通配符选择器 - 不支持
>+~等关系选择器(部分版本已支持) - 不支持
@media的部分写法 - 不支持
position: fixed在某些场景下的表现
- 不支持
- 页面栈限制:最多 10 层,超过后 navigateTo 会失败
- 包大小限制:主包 2MB,总包 20MB
- 网络请求域名白名单:必须在管理后台配置
- 不支持 npm 直接引入:需要通过开发者工具构建 npm
TypeScript 支持
小程序原生支持 TypeScript:
// tsconfig.json(项目根目录)
{
"compilerOptions": {
"strict": true,
"target": "ES2017",
"module": "ESNext",
"moduleResolution": "Node",
"lib": ["ES2017"],
"typeRoots": ["./typings"]
},
"include": ["**/*.ts"],
"exclude": ["node_modules"]
}
// 页面 .ts 文件
Page({
data: {
msg: 'Hello' as string,
list: [] as Array<{ id: number; name: string }>
},
onLoad(options: Record<string, string | undefined>) {
const id = options.id
}
})
// 组件 .ts 文件
Component({
properties: {
title: { type: String, value: '' }
},
data: {
count: 0 as number
},
methods: {
increment() {
this.setData({ count: this.data.count + 1 })
}
}
})
npm 支持
- 在小程序根目录执行
npm install - 在开发者工具中:工具 → 构建 npm
- 构建后会生成
miniprogram_npm目录 - 使用:
const dayjs = require('dayjs')
注意:
- 不是所有 npm 包都能在小程序中使用(不能依赖 Node.js 内置模块或浏览器 API)
- 每次
npm install后都需要重新构建 npm
自定义 tabBar
// app.json
{
"tabBar": {
"custom": true,
"list": [
{ "pagePath": "pages/index/index", "text": "首页" },
{ "pagePath": "pages/mine/mine", "text": "我的" }
]
}
}
在根目录创建 custom-tab-bar/ 组件:
custom-tab-bar/
├── index.js
├── index.json
├── index.wxml
└── index.wxss
骨架屏
开发者工具支持自动生成骨架屏:
- 在模拟器中预览页面
- 点击模拟器右下角「...」→「生成骨架屏」
- 会自动生成
页面名.skeleton.wxml和页面名.skeleton.wxss
// 页面 json 中引用
{
"initialRenderingCache": "static"
}
常见审核被拒原因
- 功能不完整:提交审核时确保所有功能可用
- 测试账号未提供:需要登录的小程序必须提供测试账号
- 类目不符:选择的服务类目与实际功能不匹配
- 诱导分享/关注:不能强制用户分享或关注公众号才能使用
- 虚拟支付:iOS 不允许虚拟商品使用微信支付(需走 IAP)
- 内容违规:UGC 内容需要内容安全检测
- 隐私协议:需要配置隐私保护指引
- 授权滥用:不能在首页就弹出授权请求,需要在使用时才请求
调试技巧
// 真机调试日志
const log = wx.getRealtimeLogManager()
log.info('info message')
log.warn('warn message')
log.error('error message')
// 性能监控
const performance = wx.getPerformance()
const observer = performance.createObserver((entryList) => {
console.log(entryList.getEntries())
})
observer.observe({ entryTypes: ['render', 'script', 'navigation'] })
在线查询
- 性能优化:https://developers.weixin.qq.com/miniprogram/dev/framework/performance/tips.html
- 分包加载:https://developers.weixin.qq.com/miniprogram/dev/framework/subpackages.html
- npm 支持:https://developers.weixin.qq.com/miniprogram/dev/devtools/npm.html
- 自定义 tabBar:https://developers.weixin.qq.com/miniprogram/dev/framework/ability/custom-tabbar.html