【第2119期】浏览器端录制技术的探索与实践
前言
曾经也看过一个录制视频的开源项目,挺有意思的。今日早读文章由阿里@钱泽伟授权分享。
@钱泽伟,练习时长三年的前端练习生,目前任职于阿里巴巴CBU体验技术部~
正文从这开始~~
如何探索、利用浏览器端录制回放技术来解决B类复杂交互产品的困境
背景:B类复杂交互产品的困境
我们团队负责的业务是1688大企业采购,是一个典型的正处于高速发展期的B类业务,产品的交互比较复杂、链路往往很长。
而产品能力快速迭代、客户数量持续增长的同时,团队不同角色都遇到了很多难题:
客户反馈渠道原始:业务同学还是在线下/钉钉群进行客户反馈的收集与处理,各渠道收集到的信息,经过几次或文字、或口头的传达之后,总是会出现不同程度的失真,完成处理之后回复用户往往也不够及时。
产品优化困难:产品同学想了解客户使用产品的真实过程,主要方式还是线下客户走访,成本非常高的同时,形成的产品优化/迭代周期也很长,客户的问题往往经过几个月甚至一年才能得到有效解决,影响客户满意度和续约率,对SaaS产品来说是很致命的。
问题难以定位:技术同学当时接到的工单,是客满的客户电话转录形成,或是运营同学手动填写的,上下文信息严重缺乏,有时候只有寥寥几句文字,只能靠猜或者远程协助去来定位问题,效率低下,很影响产品的整体质量。
而常规客户反馈、客户走访、远程桌面等解决方案,各自都有明显的局限性,无法从根源上解决这些难题。
因此,我们真正需要的是 高还原度、低成本、无软件依赖 的解决方案,也就是 浏览器端的录制回放能力(已于2018.04申请专利)。
破局:录制回放 + 用户反馈 = 录制反馈
基于浏览器端录制回放技术,我建立了反馈驱动的产品优化闭环,逐一解决了在上一章节中提到的难题:
用户提交录制反馈:客户在使用我们产品的过程中遇到了BUG/疑问,无需借助任何软件,就可以直接通过右下角常驻的反馈入口上报包括文字、截图、操作录制的反馈
查看回放、分析问题:产品经理、技术、用研同学,足不出户就可以在后台查看所有客户提交的录制反馈,并且可以查看客户的操作“录像”,非常高效的定位到产品优化点/BUG
需求开发:我们也和集团内的需求开发、工单提报工具做了对接,反馈可以被一键创建为需求/工单,方便后续跟进开发解决
处理结果回馈客户:在问题被成功解决之后,可以在后台直接向客户推送通知,以此形成对客户的回馈和激励作用,鼓励客户更有动力提交更高价值的反馈
整个流程的演示视频如下:
Step 1 客户提交录制反馈
Step 2 在后台查看客户操作“录像”
成果:数据&案例
录制反馈工具运作两年多以来,共计服务了130个应用/项目,收集录制反馈28000+条。以下是两个比较有代表性的案例:
【产品体验优化】一键设置税率
这个案例发生在1688企业采购的报价单中,报价单一个比较核心的功能区是“物料行”,而税率这个信息是每一行都需要选择的,因此在物料非常多的时候,一行一行选择起来非常麻烦。因此产品在设计之初就内置了“一键设置多行税率”的能力,但是还是有很多客户都反馈应该提供一个一键设置税率的能力,大家都非常疑惑。
在查看用户操作录制之后我们发现,“一键设置多行税率”这个功能的触发点并不在用户的常规操作路径上,因此很多客户根本发现不了这个功能= =
下图中的折线为鼠标移动路径,红点为客户点击操作,红框内为“一键设置多行税率”的触发点,可以说是完美避开了操作路径
因此我们增加了气泡提示,客户的鼠标只要进入物料行就会触发这个提示,之后便再也没有出现过关于“一键设置多行税率”的客户反馈。
【问题定位】商家后台性能优化(结合Redux的实践)
这个案例发生在ICBU商家后台中,当时整个产品刚刚经过一次较大的改版升级,前端技术方案也经历了大换血(切换到了React+Redux),上线之初有些客户反馈特别卡顿。
于是我们结合实际技术方案,在录制流程中增加了对Redux事件的采集,结合本就采集的Ajax请求信息,通过查看用户操作“录像”成功定位到了问题所在,通过对接口性能和Redux事件流的整合优化解决了卡顿问题。
原理:?+ ?+ ?= 录制回放
那么如果要从零实现一个录制回放MVP,拢共分几步呢?(标题已经出卖了答案)
Step 1 基石:基于 Mutation Observer 的Dom变化采集与反演
第一步就是最关键的一步,基于 Mutation Observer 将Dom(界面)的变化过程采集、保存并反演。通过 Mutation Observer 采集Dom变化
在Dom树上发生的绝大多数变化,都可以通过Mutation Observer这个API捕获到,共有如下三种变化类型:(摘自MDN)
如果是属性变化,则返回 "attributes";
如果是 characterData 节点变化,则返回 "characterData";
如果是子节点树 childList 变化,则返回 "childList"
比如上图中“添加物料”这个动作就会触发两种Mutation Record类型:
第一是整个区块的style属性中的height发生了变化,因此触发了"attributes"类型的Mutation Record
第二是物料行区块新增了一个子元素,因此触发了"childList"类型的Mutation Record
转换Node/NodeList为可存储数据类型
但是Mutation Record中有很多Node、NodeList类型的字段,无法存储,如果粗暴地全部转换为HTML String,整个采集数据的冗余率将非常高。并且HTML String也不是唯一的,回放时无法通过HTML String去判断这个Mutation Record到底发生在哪个target上。
因此我们需要给这些Node数据做一层转换,既要降低冗余率方便存储,又要在回放时能够做到数据完整。这里采用了给每个dom节点都设置一个id的方式,这个id生成方法并没有特别的限制,只要是唯一的就可以。
采集开始时我们会对初始Dom树进行一轮遍历,给所有的Dom节点都设置一个唯一id(对于之后在addedNodes中出现的新增节点也是同样),并保存包含这些id信息的初始Dom树的HTML String(初始帧),同时维护一个Dom节点和id的对应关系Map,这样就可以将Mutation Record中除addedNodes之外的Node/NodeList字段都转换为其id。addedNodes由于都是新增的节点,只能转换为带其id信息的HTML String。
最后,还需要给每一个Mutation Record都附上发生时间,这样才是可反演的。
这样初始帧的html快照以及后续MutationObserver产生的变化队列就都被转换为了可以存储的数据类型,可以选择合适的方案来进行存储(OSS、DB等等)。
特殊元素补丁
但是还是有些界面变化是无法直接通过Mutation Observer捕获到的,篇幅所限下面列出的是两种比较典型的:
至此,我们就完成了界面变化过程的采集。在回放时,我们需要准备一个disable所有script的iframe,将初始地Dom快照写入进去,然后根据时间顺序,在Dom树上将所有的变化过程逐个反演即可(增删节点、变换属性、改变表单元素的value等),最终效果如下:
PS:采集界面变化过程的必要性
在录制回放方面业界其实还有另一种方案,就是不采集界面的变化过程,只记录url、用户的点击滚动等操作,回放时直接访问url并在页面中模拟这些操作。
但在需要登陆、静态资源覆盖式发布等场景,这个方案就完全不可行了。
Step 2 注入灵魂 :用户操作 & 上下文信息
在上一步实现完成之后,可以看到的效果是网页自己就动起来了,看起来倒挺像提线木偶,而不是用户当时访问过程的真实还原。
因此我们还需要注入灵魂,也就是将用户操作也采集、复原出来,并带入必要的上下文信息。
用户的操作过程是指诸如滚动、点击、缩放、按键这样的操作事件,通过浏览器能力可以比较容易的获取到,这里就不铺开细节了。值得一提的是,在回放的时候,用户的滚动是直接通过浏览器API反演出来的,而用户的鼠标移动、点击其实只是在iframe上层铺了一层canvas,在上面画出了鼠标及其移动轨迹和点击点。
而上下文信息则是指UA、来源地址url、控制台错误、请求信息、redux事件等辅助信息,采集并在回放时展示这些事件可以帮助我们更好地定位问题。
总结来说,可反演的用户访问过程录制就是下面这张大图:
Step 3 隐私无小事:流程合规化 & 敏感数据的处理
至此我们已经完成了用户访问过程的录制和回放,但是最后一步同样重要,采集的过程中我们很可能会触及到用户的隐私数据,必须告知用户并征得同意,同时对于隐私数据需要做识别和主动过滤。
至此,一个五脏俱全的录制回放MVP就顺利完成了,撒花🎉。
最后,推荐一个项目:
TimeCat 是一个网页录屏工具,利用独特的录制方式,提供超高性能与高压缩比的网页无损视频录制与回放。Github:https://github.com/oct16/TimeCat
关于本文 作者:@钱泽 原文:https://www.yuque.com/binfe/cquxg7/vn9i0d
为你推荐
【第1580期】rrweb:打开 web 页面录制与回放的黑盒子
欢迎自荐投稿,前端早读课等你来