背景
闲鱼作为国内最大的二手交易电商平台,有着验货宝/省心卖/优品等有着闲鱼特色的交易链路,而作为交易链路的闭环,一旦形成有效订单,就会有对应的订单详情页,订单详情页往往承载着复杂的交易状态的变化。以验货宝为例, 验货宝是闲鱼推出针对二手商品存在的质量/真伪的不确定性,提出的先验货后交易模式。
作为交易订单节点:买家拍下->付款->卖家发货->鉴定方收货->鉴定为真->发货给买家->买家收货等多个交易节点,一个订单详情页在不同流转状态下可能存在数十种细分状态,每种状态之间存在着有着文案/操作按钮/进度条等视觉交互上的差别, 如下图所示:
存在问题
订单详情页的不同的状态视图流转,依赖于接口返回的字段的改变,存在和服务端的沟通成本;
前端的直接 mock 的方式,像集团内的山海关,dummy 更多是通用的 mock 数据映射,本质还是要手动修改 mock 数据的 status 值;
在真机调试时,依赖的 Charles 的 pc 代理,单次修改成本高;
除了开发同学外,测试同学测试回归的成本高. UI 同学在视觉走查中难以覆盖全部,容易带问题到线上,引起线上舆情。
总结来说,面对此类订单的订单详情页的前端开发测试,我们现有的 mock 存在着以下问题:
•开发联调周期长,自测难以充分;
•测试/视觉回归成本高;
•线上样式问题定位还原周期长;
需求分析
针对以上的问题,能否让订单状态页面的 mock 方式更为易用,我们通过真实的开发体验,提出了针对此类页面的 mock 方式要达到好用,应该具备以下特点:
1.调试方便
mock 方式应该在本地 pc 端调试和真机调试都能够方便使用;
2.业务语义
不同状态的切换交互应该带有业务语义,能够方便开发和测试快速找到想要的订单状态,而不只是简单的修改接口的某个字段值;
3.代码解耦
mock 的方式本身应该和业务代码尽量解耦,即不会将 mock 的逻辑引入线上环境;
4.mock 数据精简
订单页面状态虽多,但是接口往往统一,接口字段,对 mock 数据的维护不应该每一种状态都是单独一份 mock 数据而是应该在一份主的 mock 数据上针对产生变化的字段进行单独 mock;
技术方案
方案总揽
基于以上需求,我们尝试设计开发更为贴近此类业务的 mock 方案。先别看广告,看疗效, 此方案的真实效果如下图所示:
从效果图可以看出我们通过按钮点击实现了状态视图的变更,也就实现了目标一、二,而这个按钮本身不会在线上环境所带入也就不实现了代码解耦;那么这套方案是如何实现的呢?如下图所示,我们将整体方案设计为三大模块:
胶水层:是整个方案的编译层基座,负责在编译层将视图层插件和 mock 层插件按照生产环境状态加载进入业务层,实现在本地和预发环境下具有 mock 能力,并负责在视图层进行状态切换时按照状态码重新生成对应 mock.json,利用胶水层的打包逻辑做到了与业务解耦的目标;
mock 层:负责将前端发起的 mtop 请求拦截,根据路由映射到本地 webserver 下的 mock.json 并模拟返回对应结果,利用 mock 层方式实现了 mock 数据精简,调试方便的目标;
视图层:负责页面处的整体 mock 状态切换的交互逻辑,我们将交互入口设计为类似于 eruda 调试工具的唤起方式,在需要的订单页面侧植入, 点击会唤起弹层,弹层展示所有可枚举的订单状态文案以及其他可枚举会影响订单视图的变量条件,点击对应状态进行页面重载展示对应状态的视觉; 视图层负责实现了业务语义的目标。
胶水层实现
在具体实现过程中,由于闲鱼前端开发使用集团提供的 rax 的前端方案,rax-app 也提供了在编译层可以定制的插件机制, rax-app 基于工程构建工具 build-scripts 封装,因此在插件能力上也完整继承了 build-scripts。除了通过插件定制工程能力以外,rax-app 还为插件扩展了运行时定制的能力, 我们定制了 @ali/build-plugin-rax-mock 和本地的 selfBuild 两个编译插件,分别对应 mock 层和胶水层的设计需求,以如下方式在项目的 build.json 里进行引入,很好地实现与业务层代码的解耦。
rax-app 的插件机制提供的针对 webpack 打包方案所提供的 onGetWebpackConfig api 将视图层组件植入业务页面, 如下方代码所示,会在打包过程中根据根据指定页面文件路径选择性地注入,并且判断编译环境在真实生产环境中不做任何 mock 模块的打包。
因此通过胶水层,我们可以快速将 mock 模块和视图层模块快速引入到工程方案中,并能按需引入,不对业务造成明显侵入痕迹。
mock 层实现
首先我们会根据状态合集所需订单详情接口生成一份 mock.json 数据的合集,这样就能涵盖所有状态下所需消费字段,一般接口格式如下所示:
如下图所示,mock 层既要更新视图层切换状态而组成新的 mock.json, 也要拦截页面侧发起的 mtop 网络请求定向到对应的 mock.json 文件;
为了真实能够把接口请求到本地,需要对 h5 页面侧发起的 mtop 请求进行拦截,这里利用了淘宝 mtop 库的运行机制会将 mtop 对象加载到页面全局的 window.lib 对象上,利用 Proxy 的代理机制监听 window.lib 对象的挂载 mtop 时机 set 进行 hook, 并根据所处的生产环境判断是否使用发送请求到本地的自定义 request 请求, 实现代码如下:
当挂载完成即可实现接口的 mock 功能:
当本地页面发起 mtop 请求,如:mtop.com.test.one;
请求被注入的插件代码 hold 住,当判断是在本地开发环境或者链接带上 mock query 时, 用本地的 mtopRquest 替换,请求以 http://127.0.0.1/_mtop_mock_/com.test.one 格式重新发起请求;
请求打到本地 webpack-dev-server 上,server 再去本地 mock 目录上找 com.test.one.js4.如果找到就执行 com.test.one.js 文件,将执行结果返回 5.如果未找到,则走原有的 mtop 请求;
视图层实现
视图层 UI 由页面侧直接可见的切换 icon 和弹层构成,icon 设置为 dragable 方便用户随时拖动, 弹层的渲染考虑到扩展性由树状节点组成。目前根据真实的业务需要,将节点层级分为 2 层(可扩展),一级节点代表:已下单,已验货等核心节点, 二级节点代表在一级节点下可能存在的正负向交易细节节点, 如:在已下单环节下存在取消订单的副状态;
单点数据结构为如下代码所示,包含有每个枚举状态的语义文案,每种状态对应接口字段的主副状态码,状态枚举值,子节点。
每个节点在渲染过程中采用树状递归渲染,视觉层展示 nodeName, 同时绑定点击回调 onItemChange。当弹窗的某一状态结点发生点击行为时,设置节点选中态样式,同时通过 Modal 组件通过 props 传入的函数方法向外层传递绑定在结点上的 status 和 subStatus 值,从而实现了点击单个选项能够修改对应的 mock.json 文件, 同时会触发页面的 window.reload()功能重新加载页面发起 mtop 请求,因此此时接口返回的数据已经是切换后的状态数据,因此页面也会呈现对应状态的视图。
业务应用
当将以上所述的整套方案串联完成后,业务侧开发只需要定义好属于自己订单详情的状态枚举数据结构,并将视图层组件和 mock 层插件引入项目工程,即可接入 mock 的能力,实现所选即所得的订单状态变更。
目前该方案已经在闲鱼验货宝、奢侈品寄卖等交易链路场景下接入, 从真实使用过程中,能够有效提升开发联调的效率,单次状态的切换时间成本从分钟级别下降到秒级, 并能帮助测试和视觉同学快速回归业务场景,定位线上问题,简单的视觉问题不再依赖于后端接口的状态切换,约能节省 30%以上的联调沟通成本。另外带来的彩蛋是该方案的切换语义特点, 在项目开发人员出现流动时,新承接人能够通过这一 mock 方式快速地理解业务,上手开发。上手快,所见即所得,算是针对此类页面维护开发的一门利器。
总结与展望
本章节主要介绍了闲鱼在前端开发典型交易场景-订单详情页时,面对多状态模式切换页面的一种开发体验提升的一种尝试,通过上述的方案设计基本实现了开篇所定义的业务 mock 的 4 个目标:调试方便、业务语义、代码解耦、mock 数据精简,不仅提升了在开发/测试同学的体验,通过语义化的方式也能帮助项目更好地进行维护升级。
伴随着业务系统的复杂度提升和功能迭代,如何能如何更低成本地去维护 mock 项目给我们提出了更高的要求,能否在比如状态码伴随业务升级,变更,增减的功能迭代过程中,联动式地快速变更枚举状态值配置,减少业务侧开发的维护成本是我们继续升级迭代该方案的方向。目前页面只针对单页面 mock, 如何去覆盖整体交易链路的 mock,比如一个完整订单链路的状态一致性,也是值得探索的方向。
本文转载自:闲鱼技术(ID:XYtech_Alibaba)
原文链接:针对多状态订单详情的前端mock方案
评论 1 条评论