1
This commit is contained in:
304
_DEL/wechat-miniprogram/steering/best-practices.md
Normal file
304
_DEL/wechat-miniprogram/steering/best-practices.md
Normal file
@@ -0,0 +1,304 @@
|
||||
# 开发最佳实践与常见坑
|
||||
|
||||
> 综合官方文档与社区经验
|
||||
|
||||
## setData 性能优化
|
||||
|
||||
setData 是小程序性能的关键瓶颈,因为数据需要从逻辑层(JS 线程)序列化后传输到视图层(渲染线程)。
|
||||
|
||||
### 原则
|
||||
|
||||
1. **减少数据量**:只传需要更新的字段
|
||||
```javascript
|
||||
// ❌ 错误:传整个列表
|
||||
this.setData({ list: this.data.list })
|
||||
|
||||
// ✅ 正确:只更新变化的项
|
||||
this.setData({ 'list[2].name': 'new name' })
|
||||
```
|
||||
|
||||
2. **减少调用频率**:合并多次 setData
|
||||
```javascript
|
||||
// ❌ 错误:多次调用
|
||||
this.setData({ a: 1 })
|
||||
this.setData({ b: 2 })
|
||||
this.setData({ c: 3 })
|
||||
|
||||
// ✅ 正确:合并为一次
|
||||
this.setData({ a: 1, b: 2, c: 3 })
|
||||
```
|
||||
|
||||
3. **后台页面不要 setData**
|
||||
```javascript
|
||||
// ❌ 错误:页面隐藏后仍在 setData(如定时器)
|
||||
onShow() {
|
||||
this._timer = setInterval(() => {
|
||||
this.setData({ time: Date.now() })
|
||||
}, 1000)
|
||||
},
|
||||
// ✅ 正确:页面隐藏时停止
|
||||
onHide() {
|
||||
clearInterval(this._timer)
|
||||
}
|
||||
```
|
||||
|
||||
4. **避免在 onPageScroll 中 setData**
|
||||
```javascript
|
||||
// ❌ 错误
|
||||
onPageScroll(e) {
|
||||
this.setData({ scrollTop: e.scrollTop })
|
||||
}
|
||||
|
||||
// ✅ 正确:用 WXS 响应事件或节流
|
||||
onPageScroll: throttle(function(e) {
|
||||
if (this._needUpdate) {
|
||||
this.setData({ isTop: e.scrollTop < 100 })
|
||||
}
|
||||
}, 100)
|
||||
```
|
||||
|
||||
5. **大列表用纯数据字段**
|
||||
```javascript
|
||||
Component({
|
||||
options: { pureDataPattern: /^_/ },
|
||||
data: {
|
||||
displayList: [], // 用于渲染
|
||||
_rawList: [] // 纯数据,不传输到视图层
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## 分包加载
|
||||
|
||||
### 基本分包
|
||||
|
||||
```json
|
||||
// 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 页面必须在主包
|
||||
|
||||
### 独立分包
|
||||
|
||||
不依赖主包即可运行,适合独立功能页面(如活动页)。
|
||||
|
||||
```json
|
||||
{
|
||||
"subpackages": [
|
||||
{
|
||||
"root": "packageIndependent",
|
||||
"pages": ["pages/activity/activity"],
|
||||
"independent": true
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**注意**:独立分包中不能使用主包的公共资源(js/组件/样式)。
|
||||
|
||||
### 分包预下载
|
||||
|
||||
```json
|
||||
{
|
||||
"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 可用) |
|
||||
|
||||
### 常见迁移坑
|
||||
|
||||
1. **没有 Cookie**:登录态需要自行通过 header 传递 token
|
||||
2. **没有 DOM**:不能用 jQuery、不能 `document.getElementById`
|
||||
3. **不支持动态执行代码**:`eval()`、`new Function()` 都不可用
|
||||
4. **样式差异**:
|
||||
- 不支持 `*` 通配符选择器
|
||||
- 不支持 `>` `+` `~` 等关系选择器(部分版本已支持)
|
||||
- 不支持 `@media` 的部分写法
|
||||
- 不支持 `position: fixed` 在某些场景下的表现
|
||||
5. **页面栈限制**:最多 10 层,超过后 navigateTo 会失败
|
||||
6. **包大小限制**:主包 2MB,总包 20MB
|
||||
7. **网络请求域名白名单**:必须在管理后台配置
|
||||
8. **不支持 npm 直接引入**:需要通过开发者工具构建 npm
|
||||
|
||||
## TypeScript 支持
|
||||
|
||||
小程序原生支持 TypeScript:
|
||||
|
||||
```json
|
||||
// tsconfig.json(项目根目录)
|
||||
{
|
||||
"compilerOptions": {
|
||||
"strict": true,
|
||||
"target": "ES2017",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Node",
|
||||
"lib": ["ES2017"],
|
||||
"typeRoots": ["./typings"]
|
||||
},
|
||||
"include": ["**/*.ts"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
```
|
||||
|
||||
```typescript
|
||||
// 页面 .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 支持
|
||||
|
||||
1. 在小程序根目录执行 `npm install`
|
||||
2. 在开发者工具中:工具 → 构建 npm
|
||||
3. 构建后会生成 `miniprogram_npm` 目录
|
||||
4. 使用:`const dayjs = require('dayjs')`
|
||||
|
||||
**注意**:
|
||||
- 不是所有 npm 包都能在小程序中使用(不能依赖 Node.js 内置模块或浏览器 API)
|
||||
- 每次 `npm install` 后都需要重新构建 npm
|
||||
|
||||
## 自定义 tabBar
|
||||
|
||||
```json
|
||||
// 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
|
||||
```
|
||||
|
||||
## 骨架屏
|
||||
|
||||
开发者工具支持自动生成骨架屏:
|
||||
1. 在模拟器中预览页面
|
||||
2. 点击模拟器右下角「...」→「生成骨架屏」
|
||||
3. 会自动生成 `页面名.skeleton.wxml` 和 `页面名.skeleton.wxss`
|
||||
|
||||
```json
|
||||
// 页面 json 中引用
|
||||
{
|
||||
"initialRenderingCache": "static"
|
||||
}
|
||||
```
|
||||
|
||||
## 常见审核被拒原因
|
||||
|
||||
1. **功能不完整**:提交审核时确保所有功能可用
|
||||
2. **测试账号未提供**:需要登录的小程序必须提供测试账号
|
||||
3. **类目不符**:选择的服务类目与实际功能不匹配
|
||||
4. **诱导分享/关注**:不能强制用户分享或关注公众号才能使用
|
||||
5. **虚拟支付**:iOS 不允许虚拟商品使用微信支付(需走 IAP)
|
||||
6. **内容违规**:UGC 内容需要内容安全检测
|
||||
7. **隐私协议**:需要配置隐私保护指引
|
||||
8. **授权滥用**:不能在首页就弹出授权请求,需要在使用时才请求
|
||||
|
||||
## 调试技巧
|
||||
|
||||
```javascript
|
||||
// 真机调试日志
|
||||
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
|
||||
Reference in New Issue
Block a user