近些年,整个前端领域发展迅速,效率型的前端框架也层出不穷,每个团队选择的技术解决方案都不太一致,因为互联网的特性及中国自身的特色,各个产品对于多端的投放的需求是一致的。像小程序这种跨端场景和现有的开发方式也不一样,为了满足业务的需求技术人员在日常开发中会因为投放平台的不一样而进行多套代码的维护,效率较低且成本也高。跨端框架应景而生(这里就不过多介绍各个跨端的框架了)。
在我看来大前端是趋势,跨端是趋势的第一步,而对于技术人员来说在不影响功能体验的情况下能解决维护多套代码的痛点非常重要,改编一下比较流行的一句话:「lean once,write once,run anywhere」
经过将近两年的发展,小程序已经深入用户的日常生活,小程序应用数量超过了百万量级,覆盖200多个细分的行业,日活用户达到两个亿。与此同时,像支付宝、百度、头条、手Q等等都开始了自家的小程序生态,百家争鸣应景而生。青桔单车作为便民的出行工具,对于用户使用方式上也是成本越低体验越友好,即用即走的小程序已然成为平台选择的趋势。
高效、稳定、多入口就是业务现在的要求,青桔单车是日活相对较高的小程序(目前在阿拉丁小程序 TOP 榜前10),这也要求我们对小程序的性能(加载、渲染、响应的时间)、稳定及安全有较高的标准。
同时业务也需要在各个平台上获得更多的入口,这就直接导致我们在选择框架,业务开发时要求比较严谨。
从用户角度出发,为了减少用户使用成本(下载安装或更新 APP ),我们选择了市场上比较符合单车特性的平台作为入口。那么这时候对于研发来说就会有很多问题,我们在选择框架时,会对以下点有较高的期望或要求:
Chameleon 不仅仅是跨端解决方案,让开发者高效、低成本开发多端原生应用。基于优秀的前端打包工具 Webpack,吸收了业内多年来积累的最有用的工程化设计,提供了前端基础开发脚手架命令工具,帮助端开发者从开发、联调、测试、上线等全流程高效的完成业务开发。
从chameleon框架的架构设计图看:
综合来看 chameleon 的设计模式比较适合我们做跨端项目的开发,提升我们的效率,不维护多套重复的代码…
青桔单车简单的业务流程图:
青桔单车业务相对复杂,包括登录、认证、电子围栏、宣教、状态扭转等超过 30 个页面(不包括各种 H5 实现),用户主动打开/微信扫码进入小程序,完成登录后开始扫码开锁,开锁成功后 === 发单成功,用户开始骑行,骑行结束后完成支付整个流程结束(这里只提到核心流程),因为业务需要,我们需要维护微信、支付宝、高德(快应用、百度接入中)等众多小程序,对于研发来说最快的是 copy 一套代码然后针对性进行修改,当进行新功能开发的时候就痛苦了。
基于业务和多端的差异抹平方案考虑,最终CML青桔单车小程序技术方案设计图如下:
从青桔单车现在模块看,为了能真正的实现跨端开发,需要解决各平台间的差异问题:
由于各端组件化的实现方式不一样 ( 微信 webcomponents 、百度模拟的组件化、支付宝是 react 框架),多端界面的一致性是一个比较麻烦的事情,从 cml 本身的设计及实际的体验来说,不论样式的单位换算还是组件的统一封装都做了较好的统一。
来点 gif 图,预览一下青桔单车小程序基于 cml 改版后三端的效果吧:
我们按照业务场景拆分了几个公用的模块包括用户相关、发单、硬件/蓝牙通信、订单管理、营销等,每个模块都单独 store、action 及暴露的 commonApi,配合各个页面逻辑实现整个产品功能,针对差异化我们列举一个登录的例子通过多态方式来兼容微信、支付宝登录接口。我们在项目中 src/componets 中建立一个 API 的空间作多态管理 API,针对 login 我们按照 cml 的规范建立一个 login.interface 文件。
实现如下:
这里想着重提一个非常容易被忽略的问题:interface 中一定要定义输入输出,规范各端实现规范,这是为了避免在修改某个端的方法入参(增加或减少)却没有考虑其他端的实现,如果全量测试会浪费很大人力且也不能保证都能覆盖到测试,为了可维护性,建议从一开始就坚持写多态组件的 interface,在程序层面上亡羊补牢有时候真的为时已晚……
接口定义后就可以用统一的方式进行调用:
import login from '../../components/Api/login/login.interface';
export const login = function ({commit}) {
return new Promise((resolve, reject) => {
login.login()
.then(({code}) => {
commit(types.SET_USER_CODE, {code});
resolve({code});
})
.catch(e => {
reject();
});
});
};
再比如,针对蓝牙通信的 API 微信需要端来做 ArrayBuffer 到 HexString 的转换而支付宝不需要,这里我们也采用多态进行接口方式抽离,微信进行转换,支付宝直接 return 原数据,整个 BLE 的过程非常复杂,为了提高连接,通信的成功率我们做了很多优化,如果直接在代码中 hack 会影响整个流程甚至造成整个蓝牙动作的不稳定,影响开锁率,做这层多态封装既不影响原有逻辑,改动也相对较少成本很低。
另一方面,PM 如果在某一端提个需求,例如针对支付宝用户在登录时候加一些特殊功能,我们能在不影响其他流程比进行改造,同时这是在物理上进行了隔离,可以发布单独 npm 包,可维护性比较高。
工程化是使用软件工程的技术和方法对项目的开发、上线和维护进行管理,因为前端过程式的开发比较低效,可以通过模块化、组件化、本地开发、上线部署自动化来提高研发效率。
编译全部
,cml wx dev 编译微信
,启动开发模式,监听文件变化动态打包 cml build编译全部
,cml wx dev 编译微信
, 构建生产环境前端开发的过程中,数据 mock 是一个比较重要的功能,在验证逻辑、研发效率上及线上线下环境环境切换都起着很重要的作用。cml 这里也提供数据 mock 的功能。
import cml from "chameleon-api";
cml.get({
url: '/api/getUserInfo'
})
.then(res => {
// ...省略部分实现逻辑
cml.setStorage('user', ...res)
},
err => {
cml.showToast({
message: JSON.stringify(err),
duration: 2000
})
});
调用方法的参数 url 中只需要写 api 的路径。那么本地 dev 开发模式如何 mock 这个 api 请求以及 build 线上模式如何请求线上地址,就需要在配置文件中配置 apiPrefix。
// 设置api请求前缀
const testApiPrefix = 'http://test.api.com';
const apiPrefix = 'http://prod.api.com';
cml.config.merge({
wx: {
dev: {
apiPrefix: testApiPrefix
},
build: {
apiPrefix
}
}
})
cml 支持本地 mock,使用方式是在 /mock/api/ 文件夹下创建 mock 数据的js文件,启动dev模式后(apiPrefix留空表示本地ip+端口),可以直接实现本地mock。
针对首页的消息流卡片我们会有登录、认证、订单、骑行卡等等不同的形式,我们封装了一个组件来处理,静态效果如下:
相对来说这个组件比较通用,我们大致分为 2 种类型,通知型与动作型,如上图 2 就是一个纯显示的消息,其他的是带有按钮的消息,在组件设计上我们根据调用组件时传的type 不一样来区分,组件使用上用 props 进行 data 传递。
通知型:TipsCard
动作型:ActionCard
下面是动作型的实现方式:
// ...
调用上,我们动态传递 component is 中的type来指定不同的消息流类型。
在自定义事件的处理上,我们通过 c-bind:action 绑定了一个 componentAction,通过 EventBus 事件来传递执行
// ...
基于青桔单车日活相对较高的特性,高效、稳定、多入口就是业务现在的要求,这要求我们对小程序的性能(加载、渲染、响应的时间)、稳定及安全有较高的标准。
性能上,cml 做了 array diff,因为小程序的主要运行性能瓶颈是 webview 和 js 虚拟机的传输性能,cml 尽可能 diff 出修改的部分进行传输,性能相对会更好,贴一下源码。
源码实现比较简单,但带来实际的用处还是比较大的,单车小程序有不少列表页面,当对于列表 data 进行重新赋值先进入 diff 函数过滤出实际改变的每一项,再进行 view-render,性能上会提高不少
“苦尽甘来” 是青桔单车小程序接入 chameleon 跨端框架的总体感受。
**苦:**这里的”苦“我们到现在看其实是因为一种为未来做铺垫而需要做更多工作的“苦”,原本只需要考虑一端的 CASE 但现在要自己抽象出来一系列例如接口参数,通用组件,以及需要掌握更多的平台技术方案的“苦”。
**甘:**当我们通过 chameleon 将青桔单车小程序上线后,应对业务抛出来的新需求,我们再也不用维护多套代码了,也把因为维护多套代码导致可能某一端不一致性的风险给彻底排除了。不仅仅是 RD 同学受益,QA 同学验收的时候因为是一套代码逻辑所以可以节省一半的时间。本来3天要完成的需求现在1.5天就可以完成也极大的满足了业务方的需求,我们认为这就是技术驱动业务发展的一个例子,技术不能只是一个工具而是要从业务角度出发去实现才有真正的价值。
cml 做了 DSL 编译转化从根本上实现了各端的统一,引入组件多态、方法多态并抽象各端的配置可以更灵活抹平差异,方便配置、扩展。通过深入了解 chameleon 框架及项目实践,我们基于 cml 可以再抽象出一些公共层的组件、接口,当然这里是指的跨平台的组件、接口,因为只有这样才能更大化提升开发的效率,给业务带来更多的可能。
我们在选择框架的时候要考虑不仅要考虑其本身的性能、稳定、安全,包大小等,还需要看一下复用性及发展,比如是否能基于框架本身扩展出适合业务甚至公司级别的通用组件、API,是否方便随着业务的变化而能做到及时响应式的扩展、变动。
到这里青桔单车 chameleon 跨端实践的介绍结束啦,有表述不好的希望多多包含并提出建议我们及时改进,谢谢。