美团外卖小程序的探索和实践(演讲内容整理)丨掘金开发者大会

avatar
美团小编 @美团

2017年1月9日,微信官方在2017微信公开课Pro上发布的小程序正式上线,开创了小程序开发的时代。我们的美团外卖的业务也逐步加入到小程序开发的队伍中。小程序有着无需安装、触手可及、用完即走、无须卸载的特点,属于“轻”量级的应用。

但是这样的“轻”量级应用却承载我们非常复杂的外卖业务,对于我们外卖团队来说,也面临着很多新的机遇和挑战。本文将详细介绍我们外卖小程序的解决方案与经验,希望能够对大家有所启发。本次分享主要由以下四个部分展开:

第一部分:技术架构

小程序发布时,其定位主要是功能比较简单的轻应用,所以原生框架设计相对比较简单,而对于业务相对复杂的中大型互联网企业,小程序产品支持还不够完善,项目较难进行管理和维护。

而美团外卖小程序按照业务、主流程、营销、广告等划分了多个团队共同进行开发,如何保证多团队高效协同地开发小程序就是一个难题;同时在美团外卖小程序中,主流程要求稳定增量更新,营销活动则需要快速迭代多渠道支持,针对不同的业务场景需求,如何去做好支持工作也是非常重要的一环。那么如何应对这些挑战呢?

业务场景

针对业务场景的多样性,我们采用了较开放的框架策略:项目既支持原生框架开发,又支持自由引入第三方框架,以满足各业务场景的需求。

  • 原生框架开发:对主流程等相对独立且稳定性要求高的业务采用了原生框架进行开发。

    • 一方面,我们考虑小程序原生框架不够完善,随着基础版本库的升级,存在需要处理的兼容性问题,避免引入第三方框架增加调试难度或引入新的问题。
    • 另一方面,小程序代码体积限制比较严格,避免引入第三方框架在编译过程中引入较大的框架代码。
  • 支持mpvue(第三方框架):营销业务需要支持Web页、小程序页等多种渠道,通过引入mpvue,可以使各渠道复用Vue组件,进而提升开发效率。

通用组件

针对我们面对的业务场景,我们将各业务通用的基础功能进行了梳理,抽离成一些组件来进行统一管理和维护。高复用性组件能够有效提升开发效率,而且研发质量也可以得到很好的保障。

协同开发

多团队合作开发小程序的模式下,我们将核心的主流程业务放在主包,其他各业务都存放在各自子包当中。这样做的目的,是确保每个包中的业务相关性较强,避免了用户使用中会有频繁的子包模块加载过程,也能保证各业务团队之间的隔离,避免出现业务冲突。

我们的通用组件和各业务子包都托管在npm上,然后进行模块化的管理。并通过对构建脚本的改造,将npm引入到小程序主包中,进一步保证各业务间的隔离性,如下图所示:

同时我们在小程序开发周期中,制定了版本管理、准入流程、发布流程等,规范化小程序各环境版本的使用,进一步确保各团队间的顺畅合作。整体架构图如下图所示:

我们的系统架构,最底层是微信小程序的原生API,中层是各业务通用的核心组件,如登陆、WebView封装组件、监控、数据上报等,均由统一的团队建设和维护。对编译构建工具进行插件化改造,可以自由引入第三方框架进行开发。最上层是各业务方通过拆包将业务隔离开来。

第二部分:流程与工具

  • 工具规范方面:我们提供了子包项目、组件创建脚手架。比如登陆、WebView等通用组件以及业务组件都使用我们的组件脚手架来进行创建,简化并较低成本规范了子包项目搭建和构建过程,提升项目搭建效率与合作效率。
  • 测试方面:我们为组件接入了单元测试。小程序整个项目的单元测试和UI的自动化测试也在建设中。我们在小程序开发周期中,制定了版本管理、准入流程、发布流程等,规范化小程序各环境版本的使用,进一步确保各团队间的顺畅合作。

开发过程

在本地开发过程的整个构建流程如下图所示。开发者在本地工作目录执行操作,通过gulp构建目标文件到本地工作目录中,再通过微信开发者工具对生成的代码进行调试和发布。同时我们的构建支持Mock服务,模拟后端服务器接口,提高联调效率。

发布过程

我们的发布规范过程如下图所示。和一般的前端发布过程类似,差异点在于必须经过微信开发工具才能上传代码进行微信方的审核与发布。

而我们的期望和后期规划是将整个CI构建和发布过程一体化。如下图所示:

近期微信开发者工具提供了命令行工具和HTTP方式来支持小程序的预览和上传,我们正在将整个流程进行整合与改造。通过在服务器中安装微信开发者工具,将整个过程使用CI连接起来,减少人工操作的过程,来提升发布流程的效率和质量。

不过在实现完全自动化发布的道路上依然面临着一些问题:

  • 开发者工具的登陆,公众后台的提交审核和发布都需要人工介入。
  • 由于微信开发者工具目前仅有macOS和Windows两个版本,而CI服务器大多是在Linux系统上,还需要额外启动服务器来部署开发者工具,改造过程也相对复杂。

第三部分:组件化

原生组件化演进

小程序原生框架对于模块和组件的支持也处在不断完善的过程中。最早小程序框架支持CommonJS规范,可以使用require、module关键词定义和引入js模块,支持通过@import来引入样式文件,支持在布局文件wxml中支持定义template模板,可以通过include、import和wxs等标签引用外部文件。

而实现一个组件,常常wxs逻辑,wxss样式和wxml布局三个文件都需要进行定义,这就意味着引用一个组件时,需要在三个文件中同时引入。组件独立性差,与页面文件高耦合,不利于开发和维护,使用起来非常不便。

因此在基础库1.6.3版本中,小程序开始支持自定义组件,只需要通过标签就可以很方便的引入自己开发的组件。但早期的自定义组件版本,只允许绑定JSON兼容格式的数据,并不支持回调函数,在使用上存在很大局限性。直到2.0.9版本才开始支持函数传入。

近期,小程序原生支持了npm,但基础版本库要求在2.2.1及以上,并且需要通过微信开发者工具进行一遍构建。总体来说,小程序框架在不断完善对组件的支持,但如果考虑低版本用户的兼容性,开发者开始有较多工作要做。

组件分类

我们将组件划分为三种类型,页面组件、UI组件、功能组件。

  • 页面组件:功能相对比较独立的页面,如用于内嵌H5的WebView封装页面等;
  • UI组件:页面中局部功能较独立的UI部分,比如页面中嵌入的登陆组件,商家列表等;
  • 功能组件指:无UI和交互,纯JS的模块。

通用组件关注方向

我们基于微信组件的演化也在扩充和完善我们的通用组件过程中,同时也相同存在着一些局限性,之后针对通用组件的关注方向主要在以下两方面:

  • 小程序组件原生功能的补充和完善。
  • 通用基础及业务功能统一封装。

Storage组件

大小约100k随机对象读写清空缓存各100次,小程序Storage与Web LocalStorage 耗时比较如下图所示:

可见小程序的性能远低于Webview的LocalStorage,所以针对这样的现状,我们Storage组件的封装与设计必须重点考虑性能问题的解决与规避。

Storage组件优化

针对小程序Storage的读写性能差,且存储量有限的情况下,我们Storage的设计有以下特点:

  • 内存高性能读写数据:利用内存存储数据替代大量数据存在小程序Storage中,从而规避Storage读写操作的性能瓶颈,同时减轻存储量的占用率。
  • 文件持久化存储:采用内存与Storage相结合的形式,保证缓存数据的可用性。
  • 数据同步:由于持久化的策略,所以需要有完整并保证准确性的流程来同步内存数据与Storage数据。

整个流程如下图所示:

防止误调用底层API导致数据不同步:

  • 写入文件时key追加特殊前缀
  • 编译时进行语法检查,误调用及时告警

第四部分:展望与规划

目前小程序开发工具也在不断完善中,最近增加了很多诸如npm 支持、命令行调用、HTTP调用、Git版本管理、云开发、体验评分等功能,工具的完善为开发者带来了一定便利。

小程序的特殊性导致小程序开发人员与微信提供的开发工具的强粘黏性,可以感知到微信小程序开发工具的设计是期望实现一个小程序开发的闭环。

但这样的一个“黑盒”工具也存在着一定问题——无法满足不同团队和业务的一些需求。所以我们也希望,未来小程序开发工具能提供一些工具开放功能的API,我们可以对开放功能进行改造实现,最终满足各个团队不同的需求。

同时小程序在最初的定位(功能比较简单的轻应用)和设计下,性能不会太高。如果承载较大型复杂的业务,势必会遇到一些性能问题,所以性能问题的关注是比较重要的。官方对于性能问题在开发上的建议是控制setData 的数据体积大小、控制setData的频率。但真实的业务场景中有很多是无法避免频繁setData的,如对滑动操作(较高频的动作)后的一些特殊需求、复杂页面点击快速响应等。

美团外卖在基于这些问题的规划包括:

  • 小程序性能的测试指标定制:及时发现项目的性能瓶颈,进行有针对性的优化。经过测试小程序渲染数据达到100Kb时卡顿明显,尤其在安卓手机,所以其中就包括对渲染数据大小制定一定指标。
  • 性能分析工具:开发一些工具对性能进行分析。
  • 框架优化:从框架层来针对性规避遇到的一些问题。
  • 长列表组件的实现:使用官方提供的长列表组件。

此外,与性能并存的一个问题是小程序体积的限制(不超过2M,包含子包的主包,总包不超过8M)。这样的限制有一定道理,因为小程序渲染前,需要经过下载整个小程序的包体、后端请求数据、对数据进行渲染这样相对较长的周期,包体过大必然影响用户感知渲染的时间,影响用户体验。

减小体积的规划采取方案包括:

  • npm依赖优化:npm包之间内部的依赖包会存在重复的情况,重复的部分都占用包体积,采取对代码预解析简化npm包代码来缩小包体。
  • 图片部署CDN:图片体积占包较大,非关键图片部署到CDN。
  • 非关键页面迁移子包:采用子包、独立子包的方式减少对主包体积的占用。
  • 页面动态下发:小程序不支持动态下发功能,目前我们正在探索和考虑。

这两大重点问题在我们的业务场景和当前架构设计下,都是未来长期需要关注和解决的问题。

总结

我们复杂的业务在开发小程序时虽然面临着这样一些问题:“轻”量应用的小程序对于较大型的应用和较复杂的业务场景存在着一定的局限性,“轻”量理念的原生框架稍简单,不足以完美支撑我们较大型和复杂的业务;多团队合作如何保证高效性;如何更友好地满足不同的业务场景。

最终我们通过在技术框架层面优化设计和规避一些小程序局限性问题,制定更合理的流程和建设更强大的工具来提高工作效率,基于微信小程序组件化演进建设我们的组件化生态,来解决我们所面临的问题。同时也对微信小程序工具、性能、体积等方面进行展望和规划我们的后续进程,保持我们的深度探索和实践。