微信小程序开发军规

907 阅读10分钟

Yahoo 的前端性能优化之35条军规,是前端开发社区中多年以来被奉为经典的技术规范之一,给无数前端开发者提供了技术优化上的有效方案支持,也成为每位前端初学者必知必背的功课。

在微信小程序的业务代码编写中,事实上也需要一套类似的规范来为团队的代码产出质量做保障,能够通过各个方面的规范约束和最佳实践建议,对小程序应用的可维护性、稳定性、性能、体验等方面进行优化。

鲸灵前端团队通过长期以来在电商业务小程序应用的开发中不断踩坑和沉淀,最终总结出来一批通用型较强的小程序开发常用规范,按不同影响分类共有以下三种:

  • A类:容易导致业务故障

  • B类:影响代码可维护性

  • C类:影响系统体验性能

1、数据部分

1.1、慎用 setData() 方法(C类问题)

1、每次 setData 都传递大量新数据

  1. 首屏 setData 太多屏数据,导致初始化渲染很慢

推荐方式:拆分首屏数据接口,将首屏内容接口和其他内容接口分开,非首屏接口延迟请求渲染

临时方案:在后端接口无法快速支持的情况下,可以暂时手动将第二次 setData 延迟 300ms,首次渲染控制在两屏以内

  1. 分页数据,整个大数组传入

推荐方式:使用二维数组,第一维对应每一页的数据,第二维对应当前页中的每一条数据,当分页数据需要插入时,只传递变化部分数据

wx.setData({[`list[${index}]`]: array })

2、频繁的去调用 setData

  1. 未变化的 data 仍然调用 setData 处理

推荐方式:setData 前先判断值是否变化,相当于业务逻辑中手动做了 diff

  1. input、scroll 事件中频繁调用

推荐方式:在频繁触发的用户事件回调用进行节流、防抖处理,避免频繁调用 setData

3、后台态页面进行 setData(例如后台态页面定时器未清理)

推荐方式:在 onHide、onUnload 等生命周期内手动清理定时器

微信官方文档:

小程序的视图层目前使用 WebView 作为渲染载体,而逻辑层是由独立的 JavascriptCore 作为运行环境。在架构上,WebView 和 JavascriptCore 都是独立的模块,并不具备数据直接共享的通道。

当前,视图层和逻辑层的数据传输,实际上通过两边提供的 evaluateJavascript 所实现。即用户传输的数据,需要将其转换为字符串形式传递,同时把转换后的数据内容拼接成一份 JS 脚本,再通过执行 JS 脚本的形式传递到两边独立环境。而 evaluateJavascript 的执行会受很多方面的影响,数据到达视图层并不是实时的。

1.2、减少事件传递参数大小(C类问题)

原理同1.1,WXML元素上绑定的dataset数据(data-xxx),在事件响应中同样会涉及到一次从视图层到逻辑层的数据传输

推荐方式:dataset 数据需要严格控制大小,仅保留必要数据,避免将整个大对象直接传入

1.3、与页面渲染无关,在 wxml 不会用到的数据,放在自定义的 extraData 中维护(C类问题)

1、小程序启动时,会把所有调用 Page() 方法的 object 存在一个队列里。每次页面访问的时候,微信会重新创建一个新的对象实例,即深拷贝一个 page 对象。

推荐方式:应该尽量减少默认 data 的大小,以及减少 page 对象内的自定义属性

2、非视图数据变更,setData 引起不必要的重新生成 vdom、diff 和通信等

推荐方式:将与视图渲染无关的数据置于自定义的 extraData 中

1.4、页面展示数据处理方法放在 wxs 里操作(B、C类问题)

微信官方文档:

分析:由于运行环境的差异,在 iOS 设备上小程序内的 WXS 会比 JavaScript 代码快 2 ~ 20 倍。在 android 设备上二者运行效率无差异。

推荐方式:一些与页面展示的不涉及复杂逻辑数据放在 wxs 里处理,例如时间格式化,数字格式化,商品名过滤等

1.5、交易金额等关键数据展示(A、B类问题)

前后端需要对金额类等重要数据接口的字段制定规范(例如:金额数据统一使用分为单位),避免维护者不知情直接使用或重复转换,造成展示错误

推荐方式:视图层可以统一使用规范展示模版,例如: <template is="money" />

1.6、页面 data 默认值设置为与渲染相关的初始数据(B、C类问题)

防止页面因默认数据不存在显示异常,页面中展示 undefined、NaN 等,用户体验较差

微信官方资料:

性能与体验 /体验评分 /评分方法与规则 /性能

性能与体验 /体验评分 /评分方法与规则 /体验

性能与体验 /体验评分 /评分方法与规则 /最佳实践

2、资源部分

2.1、小型 icon 图片使用雪碧图(C类问题)

使用雪碧图合并小图,减少图片资源发起过多 HTTP 请求

推荐方式:webpack 中可以使用 spritesmith 这样的插件去自动化生成雪碧图

2.2、图片优化(C类问题)

  1. 图片尺寸:与设计稿宽高保持 1:1,避免加载不必要的大资源

  2. 图片格式:透明底用 Png,否则用 Jpg,节约资源,或者选择性地使用 Webp 格式的图片

  3. 使用渐进式加载:首屏首次加载低质量图片,随后加载正常规格图片(但存在重复渲染的性能压力)

2.3、cdn 图片资源(B、C类问题)

  1. 图片上传前可通过 tinypng 这类工具进行压缩
  2. 图片资源上传到 cdn 上后可以设置所需的缓存策略

推荐方式:图片上传 cdn 可以有效地减小小程序包体积,并且方便及时替换活动图片。特例:主流程中稳定存在的 icon 图片无需上传

2.4、视频资源页面中只默认加载首屏图片(C类问题)

推荐方式:点击观看视频时才去加载真实视频资源,减少资源请求以提升页面初始渲染速度

3、容错部分

3.1、注意数组、对象取值空指针问题(A类问题)

推荐方式:使用 lodash.get、可选链操作符(@babel/plugin-proposal-optional-chaining)等方式,避免空指针异常

3.2、setStorage 失败容错(A类问题)

推荐方式:使用异步方法,在错误回调中进行容错处理

3.3、判断 API 可用性(A类问题)

推荐方式:对有兼容性问题的API作检测(canIUse / 真值检测)、ES6 方法兼容

4、样式部分

4.1、对原生组件样式、行为的特殊处理(A、B、C类问题)

诸如 video、canvas、textarea 等组件层级会比较高,会出现一些遮盖问题

推荐方式:需要用真机观察,可能针对场景需要做显示/隐藏的操作等

4.2、根结点设置样式(B类问题)

推荐方式:规范样式命名空间,避免样式渗透,避免污染其他节点样式

4.3、页面使用通用样式(B类问题)

推荐方式:抽离通用样式可以减少代码量,加快开发效率

4.4、宽高避免使用含小数点单位的样式(A类问题)

部分机型会丢失属性或者去掉小数点,导致与预期样式不一致

4.5、css样式(A类问题)

小程序兼容要求:Android >= 5.0.0, iOS >= 8

推荐方式:对于低版本系统手机(安卓5,IOS 8、9),需要做兼容处理,并针对该机型进行兼容性测试

五、组件部分

5.1、业务组件(B类问题)

业务组件主要指包含业务逻辑,有些也包括一些与后端接口通信的组件。当业务组件被主包业务依赖时,应将其归入主包中,便于复用,否则应该放在该业务所在的包中

5.2、UI组件(B类问题)

UI 组件主要指不包含任何业务相关逻辑,可以被轻松复用的组件。当一个 UI 模块有频率较高的复用场景,应该将其抽离出来作为一个 UI 组件放在主包中

六、性能部分

6.1、使用懒加载(C类问题)

频繁 setData,接口请求频繁、大图片资源是目前已知造成卡顿、白屏主要原因。

推荐方式:长页面的接口、图片进行懒加载,视图滚动到对应模块再请求对应接口和 setData 调用

6.2、分包和分包预加载(B、C类问题)

  1. 小程序除了一个主包,还可以包含任意数量分包。需要合理把页面放到对应包里面,做到分类管理

  2. 将业务主流程外的页面都放分包,减少主包大小,加快首屏加载

  3. 分包预加载可以在主包页面提前加载分包代码,用户进入分包会更快

6.3、业务迭代产生的无用代码清理(B、C类问题)

1)清理无用代码,减少包大小 2)无用代码会让项目越来越臃肿,无法维护。 3)好的代码清理方式,会重新组织代码结构,添加必要注视,让代码变得更加易于维护

6.4、减少get/setStorageSync使用,公共流程尽量不使用(C类问题)

get/setStorageSync 会与 bridge 通信,延迟较长,积少成多

推荐方式:若非必要,都使用get/setStorage异步加载。

七、请求部分

7.1、网络异常兜底处理(C类问题)

推荐方式:使用公共的页面状态组件来承接异常状态下的页面展示

7.2、所有服务接口按业务场景进行容错兜底处理(A、C类问题)

  1. 影响主流程的接口请求失败,触发页面状态组件错误状态展示

  2. 可隐藏模块的接口请求失败进行隐藏处理,不提示

  3. 不可隐藏模块的接口请求失败显示局部错误状态,或进行 toast 提示

  4. 登陆请求失败跳错误页面

7.3、减少http请求数量(C类问题)

  1. 要求服务端接口去除无用的数据返回,减少报文大小

  2. 减少 cdn 资源的请求,例如合并雪碧图

  3. 需要调用接口的组件要根据当前调用页是否有所需数据来判断是否需要组件内去调用接口

  4. 对于一些监听事件(如滚动、拖拽、输入等)引起的请求做函数节流

7.4、前后端边界(A、B、C类问题)

推荐方式:遵循 “重渲染、轻逻辑” 的原则,复杂业务逻辑和计算尽量放在服务端,前端重点关注渲染以及性能体验

八、总结

以上整理的内容都是从我负责的团队日常业务开发实际场景中不断总结沉淀得来的,并不意味能够覆盖到所有的小程序开发场景,每个不同的团队都需要根据自身的业务场景和实际问题,去总结归纳,用规范、最佳实践,以及相应的工程化、自动化技术方案等辅助,最终产出一套适用于自己团队“接地气的”的开发规范。