# 开发最佳实践与常见坑 > 综合官方文档与社区经验 ## 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) { 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