Files
Neo-ZQYY/_DEL/wechat-miniprogram/steering/best-practices.md
2026-03-15 10:15:02 +08:00

7.3 KiB
Raw Permalink Blame History

开发最佳实践与常见坑

综合官方文档与社区经验

setData 性能优化

setData 是小程序性能的关键瓶颈因为数据需要从逻辑层JS 线程)序列化后传输到视图层(渲染线程)。

原则

  1. 减少数据量:只传需要更新的字段
// ❌ 错误:传整个列表
this.setData({ list: this.data.list })

// ✅ 正确:只更新变化的项
this.setData({ 'list[2].name': 'new name' })
  1. 减少调用频率:合并多次 setData
// ❌ 错误:多次调用
this.setData({ a: 1 })
this.setData({ b: 2 })
this.setData({ c: 3 })

// ✅ 正确:合并为一次
this.setData({ a: 1, b: 2, c: 3 })
  1. 后台页面不要 setData
// ❌ 错误:页面隐藏后仍在 setData如定时器
onShow() {
  this._timer = setInterval(() => {
    this.setData({ time: Date.now() })
  }, 1000)
},
// ✅ 正确:页面隐藏时停止
onHide() {
  clearInterval(this._timer)
}
  1. 避免在 onPageScroll 中 setData
// ❌ 错误
onPageScroll(e) {
  this.setData({ scrollTop: e.scrollTop })
}

// ✅ 正确:用 WXS 响应事件或节流
onPageScroll: throttle(function(e) {
  if (this._needUpdate) {
    this.setData({ isTop: e.scrollTop < 100 })
  }
}, 100)
  1. 大列表用纯数据字段
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.setStorage10MB
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

// 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 支持

  1. 在小程序根目录执行 npm install
  2. 在开发者工具中:工具 → 构建 npm
  3. 构建后会生成 miniprogram_npm 目录
  4. 使用: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

骨架屏

开发者工具支持自动生成骨架屏:

  1. 在模拟器中预览页面
  2. 点击模拟器右下角「...」→「生成骨架屏」
  3. 会自动生成 页面名.skeleton.wxml页面名.skeleton.wxss
// 页面 json 中引用
{
  "initialRenderingCache": "static"
}

常见审核被拒原因

  1. 功能不完整:提交审核时确保所有功能可用
  2. 测试账号未提供:需要登录的小程序必须提供测试账号
  3. 类目不符:选择的服务类目与实际功能不匹配
  4. 诱导分享/关注:不能强制用户分享或关注公众号才能使用
  5. 虚拟支付iOS 不允许虚拟商品使用微信支付(需走 IAP
  6. 内容违规UGC 内容需要内容安全检测
  7. 隐私协议:需要配置隐私保护指引
  8. 授权滥用:不能在首页就弹出授权请求,需要在使用时才请求

调试技巧

// 真机调试日志
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'] })

在线查询