2018 年 11 月25 日,由又拍云举办的 Open Talk 丨2018 小程序开发者沙龙系列活动杭州站圆满落幕,本次会议吸引了 200 多人次报名到场率高达 90%,同时也为数千线上直播观众提供了一场关于小程序开发的饕餮大餐。大搜车前端开发工程师夏心雨在活动作了《新人如何在大搜车写小程序》的分享。
“2018 小程序开发者沙龙”是又拍云 Open Talk 继“2018 音视频技术沙龙”后推出的重磅活动,与大部分偏重营销、流量的小程序活动不同,本系列活动更热衷于分享小程序开发过程的种种有趣经历和有益的经验。
夏心雨目前在大搜车做前端开发,包括移动端和小程序开发,她认为技术栈和实践方案的选择,适合自己业务的才是好的。大搜车的网约车小程序项目选择了原生框架,并基于这个项目的开发分享了一些实践经验,包括生命周期的使用、小程序路由、setData 数据驱动耗时等问题的解决思路。
以下是分享实录
选择适合自己的技术栈
微信小程序的推出带来了热潮,市面上也出现了很多小程序的应用,大搜车在支付宝小程序、微信小程序、百度智能小程序以及快应用上都开发过业务。从用户流量的角度上来看,大搜车更偏向于支付宝和微信小程序。在宏观上对这两个小程序做一个对比,从服务类型来看,支付宝小程序凭借芝麻信用延伸出来的商业类型的小程序占比会比较大,而微信小程序凭借用户量以及用户活跃度,在社交和内容推广等方面有着较大的优势。从开发技术的角度来看,微信小程序和支付宝小程序的基础语法非常相似,开发者可以将微信小程序的代码进行转化为支付宝小程序的代码。另一方面,支付宝小程序是对第三方的服务商以及企业开发者开放权限,而微信小程序增加了个人开发者的权限,也给开发者有更多实践的机会。
在大搜车正在运行的交互业务线上,统计出支付宝小程序在交易的转化率上要比微信小程序更高,不过当前的业务需要依赖于用户进行推广和招募,所以选择用户活跃度更高的微信小程序更适合大搜车当前的业务。
微信小程序的框架有很多,除了常用的 WePY、mpVue 框架,还有京东推出的 Taro 框架和网易考拉的 megalo 框架,这些框架都是将项目代码编辑打包成支持原生框架的代码,都是往多端统一开发这个方向开发的。腾讯官方推出的 WePY 框架采用了类 Vue 的语法,可以给使用类 Vue 的开发者降低开发成本。而且 WePY 框架对原生的大部分原生的异步 API 进行了处理,帮助开发者避开回调地问题。同时,WePY 框架也对数据绑定和事件绑定进行了优化,但同样也没有实现数据的双向绑定能力,而且 WePY 框架是对原生框架的二次封装,语法中还是支持原生语法的。
拿生活中的例子来对比一下 WePY 和原生框架,比如说妈妈让你去买酱油,首先你得知道超市里什么东西是酱油,也就是开发者起码要掌握开发小程序的基本知识;出行方式你可以选择步行或者骑车,选择骑车是快一点,但是前提是你得会骑车,也就是说开发者曾经使用过或者学习过 Vue 框架。而在这个过程中,它的安全系数是没有办法来保证的,就是你使用任何一个框架,都会有它自己的一些规则,会遇到一些不一样的坑,都需要大家自己去体验一下才能感受得到。所以在实际开发中,我们要考虑多方面的因素来选择适合自己项目的框架。
大搜车原生小程序框架实践
微信小程序将应用分为了视图层和逻辑层,分别运行在两种独立的线程中。上图可以理解为每个页面都有自己的 webView 线程,所有的逻辑脚本则运行在同一个 JSCore 线程中,因此逻辑脚本上没有浏览器对象,不能直接对 DOM 进行操作。另外,同一个逻辑脚本的线程会造成一些衍生问题,比如在页面中的定时器在跳转到其他页面的时候,并不会被自动清除,需要开发者去手动清除这些定时器,避免造成非预期的问题,类似于 SPA 的开发模式。
小程序的独立线程的运行模式方式在渲染上也相应会增加一些额外的开销,它需要视图线程和逻辑线程的交叉工作,页面进行初次渲染的时候,视图层会对页面进行初始化,并等待逻辑层传递过来的初始数据,接收到数据后将初始数据和节点树进行解析整合渲染到视图层,这样就完成了页面的初始渲染。在页面初始渲染的过程中,视图层会根据页面的节点来渲染成一个类似于 DOM 树的结构,所以页面的节点数量也会影响着页面渲染的速度。我们可以对这个节点的使用进行优化,比如减少一些不必要节点的使用,减少富文本标签 text 的使用,在条件渲染、列表渲染的时侯,多选择使用不会被渲染到节点树的 block 标签代替 view 标签。此外初始渲染的过程中,数据会有一个从逻辑层到视图层的跨线程的耗时问题,我们可以利用缓存数据进行数据初始化,再异步进行数据的请求,减少用户等待的时间。当页面初始渲染完成以后,视图层的数据需要更新时,页面会进行重渲染,重渲染的过程是由 setData 数据驱动引起的,我们要优化使用setData来减少跨线程开销。比如每次可以尽量更新小单位的数据,甚至可以将一些更新频率较大的内容提取成组件,从而实现页面的局部更新。
小程序的页面渲染和它的生命周期有着不可分割的关联。在小程序的实例中,我们会对小程序的数据进行初始化,以及解决用户登陆授权的问题。如果在你的项目中,每一个接口都需要对用户的登陆态进行判断的话,为了避免首屏页面接口的异步请求造成多次的用户登录,我们可以不在 onLaunch 的生命周期中进行用户的登陆,而是在数据封装请求的方法中,对用户的登陆态进行判断,做一个接口的同步处理,等待用户登陆成功后,再以链式的方式来请求其他数据。另外,我们可以巧妙地利用 onLaunch 生命周期中可以获取参数这个功能,在小程序码中拼接包含服务器环境的参数来进行项目环境的判断以及切换,比如一些测试环境和预发环境。
页面实例的生命周期使用会比较频繁,页面的 onLoad 生命周期只会在页面 onLoad 的情况下才会被执行,比如页面的重定向事件触发页面的卸载。如果用户的需求是每次进入到页面的时候都需要对数据进行刷新,可以在 onShow 函数中进行数据请求,或者使用页面实例的下拉刷新钩子函数,让用户手动来进行刷新数据。分享一个关于上拉加载钩子函数的小坑,可以使用元素伪类解决设置容器 padding 的问题。另外,逻辑层初始化时data的加载和页面的 onLoad、onShow 生命周期是异步进行的。如果我们直接获取本地缓存数据进行 data 数据的赋值,我们并不能在页面初始渲染的时候,及时地将数据渲染到初始化的页面上。
小程序的页面跳转分为两种类型:tabbar 的跳转和其他类型的跳转。tabbar 只能使用 switchTab 进行跳转,一般情况下我们会在跳转 url 上进行一些参数的拼接,但这种方法在小程序的 switchTab 和 navigateBack 方法中都不能使用,不过可以使用全局变量或者本地缓存的方式进行参数的传递。而且小程序的页面栈是有上限的,虽然官方已经把这个上限从 5 增加到了 10,但是大家在使用路由的时候还是需要进行合理的优化。比如用户多次点击跳转造成多次的页面跳转,会浪费页面栈的内存。可以通过控制一个全局的布尔变量,结合定时器的使用,阻止用户在一定时间内的多次点击事件。类似的我们需要对用户多次点击提交表单操作进行控制,可以借助官方提供的提示框功能,在用户点击时触发 showLoading 方法,显示加载提示框,阻止用户对页面进行交互。并在数据请求完成之后,调用 hideLoading 方法将提示框关闭掉。
上文提到的使用 setData 数据驱动耗时问题,以及数据绑定的 mustache 语法不能使用 JS 函数的问题,我们可以合理使用 WXS 脚本,这个脚本的功能类似于我们平常使用的 Filters。WXS 与 HTML 运行在同一个线程上,会减少跨线程的开销,可以代替 JS 文件中的一些处理数据方法。因为 WXS 只提供 5 个基础库,而且对 ES6 的语法不太支持,不能使用 let 来定义变量。我在开发的过程中遇到了使用 try、catch 语法会终止编译却不会有编译报错的情况,所以大家在使用的时候要多加注意。另一方面,WXS 和 JS 性能差异在不同系统上也有所不同,在 iOS 上的使用比在 android 上使用性能优化的更多。
关于微信提供的几个原生组件的使用,像 input、Textarea 客户端原生的表单组件会存在样式问题,需要我们对默认样式进行覆盖,而且它们在视图上有较高层级,需要我们使用cover-view 组件来进行补救。幸运的是,近期官方已经对 input 组件进行了同层处理,并在后期也会逐渐对其他原生组件进行同层处理。
Vue 技术栈结合小程序开发
目前大搜车所有的前端项目都是基于 Vue 技术栈开发的,除了一些使用 Vue1 的老项目,其他项目都使用 Vue2 框架。在这个基础上,大搜车依赖于 Vue 技术做出了大量的技术沉淀,比如各端的 UI 组件库,各种插件例如 AB Test 插件,形成了一套相对成熟的代码结构统一框架,另外还有 CI/CD 和支持全部前端项目的脚手架服务。
因为 Vue 技术栈在大搜车前端的比重相对较大,所以上文提到的京东基于 React 技术栈的 Taro 框架就不太适合我们了。因此大搜车规划了一套基于 Vue 技术栈的小程序开发解决方案,采用原生结合 H5 的混合开发模式,借助原生框架提供的 WebView 组件,以及它提供的一些 SDK 中的通信 API,对不同平台的 API 进行统一的封装,在底层封装的时候对项目的平台进行判断,再使用原生语法开发各个应用平台的原生插件加以支持,保证一个 H5 的项目可以和不同平台的原生框架之间正常进行通信,这样就形成了一套跨平台的小程序开发解决方案,可以大幅度减少小程序项目的开发成本。
这个方案的使用方法,就是我们在不同的 H5 项目中引用插件,保证跟原生框架的正常通信,再嵌入到原生框架中进行维护,这样我们就可以以 H5 的开发迭代来推动小程序的开发迭代。
在这个的基础上,结合目前已经有的 H5 的技术沉淀,对这个方案进行延伸赋能。比如为小程序的项目提供脚手架服务,同时这个方案可以继承一些我们现有的例如 UI 组件库、各种插件、多环境切换、自动部署等技术沉淀。
上图是在创建项目时可以选择小程序的类别、渠道以及其他的一些配置项,可以一键生成一个自定义的小程序项目。
在 H5 项目中可以手动选择一个小程序的通信插件使这个项目可以在小程序原生框架中使用。
夏心雨提到开发者们在实际的开发过程中,应该多关注官方社区的动态,及时的跟进官方的迭代以保证自己的项目能得到及时维护,多去看看其他开发者遇到的问题,大家一起学习进步。虽然这种填坑经验并不算是真正的基础知识,也并不是基于对底层的理解,但是经验积累多了,总会有一些质的进步,这就需要大家多去亲自实践体验,至于技术栈和实践方案的选择,适合自己业务的才是好的。