开发人员聚焦“功能”,简单来说,是对照着交互稿或设计稿,根据需求口述实现功能为主。显然,以功能为导向的前端开发和以业务为导向的前端开发是不同的。前者关注快速实现,快速迭代,满足用户需要,提升用户体验,多面向 toC 产品。而后者关注准确性,稳定性,满足客户需要,保证逻辑没有丝毫差错,多面向 toB 产品,甚至是内部系统。业务开发也讲究快速迭代,但是和面向C端用户的产品迭代不同。业务开发也存在随机突发性的快速迭代。
业务系统开发的其他特性还包括“性能并不那么重要”、“多迭代并行”、“数据准确,业务逻辑准确更重要”、“功能可以先上,后续再优化”这些特性。比如,没必要强调“秒开”体验和高并发。只有当业务系统需要对接业务团队外部人员时,才需要提供一个能够承受特大流量的服务,但实际上,这种场景,相当于在业务系统的基础上,部署一个大众性产品服务,也就是基于B端产品的C端产品,理念上已经横跨了两个领域,不能因此而否定前面对 B 端产品的结论。
业务型产品和功能型产品的区分,前者是持续合作的企业级产品,后者是一锤子买卖的普众型产品。后者是利用自己的资源、体验吸引用户使用,搞各种增值服务策略,让用户掏钱。业务型产品的盈利能力和C端产品孰高孰低,在产业互联网时代尚无定论。
准确性和数据
前端数据治理的重要指标是准确性和数据。在业务系统中,一个数据集合包括数据值和其他元数据。
某接口返回的数据结构:
{
data: [
{
key: 'some',
value: 1111,
data_type: 'int', // 数据本身的类型
display_formatter: '2f', // 数据在当前接口用来展示的类型,表示展示的时候要体现2位小数
}
]
}
而不单单是只有数据值:
/** 不好的数据结构 */
{
data: {
some: 1111,
another: 222,
}
}
在开始功能开发之前,需要建立一套准确的领域模型,将属性、操作、事件抽象为独立统一体,这样才能保证开发人员首先对业务有一定的理解和认知,然后才是业务流程、功能、交互的开发。
前端数据治理
在前端也存在复杂的业务数据管理问题。由于前端的数据不会自动产生,而是需要从服务器端拉取,所以,本质上,前端的数据全部是运行时的,虽然前端也可以采用一些持久化技术实现数据存储,但是总体而言,前端仍然是在动态地使用内存即时的消费来自其他来源的数据。
以React为例,从后端拉取的数据,往往需要转化为组件或应用的 state,再由 React 消费state,完成界面渲染和更新。中间处理数据的环节很多,保证数据的准确性极其关键。
前端数据的即时性、流动性、多态性,特别是在业务系统中既要求准确,又要求适应多变的需求,单纯靠数据管理是无法完成的。
数据治理是数据管理的高级阶段。即要满足不管 React 层面怎么实现,业务对象的内在关联永远保持,无论运行时状态怎么变化流动,都遵循着业务的逻辑描述,从数据的产生到消亡,都在按照某种约束运行而不会超出这个范围,同时数据质量、数据安全也因这种约束得以保障,即使出错,也有明确的告警,这就是数据治理。
“数据治理涵盖了从前端业务系统、后端业务数据库再到业务终端的数据分析,从源头到终端再回到源头,形成的一个闭环负反馈系统。“
前端数据治理是一个狭义的概念,它虽然会涉及和后端的交互,但不应该侵入后端,也不需要从系统整体层面去设计,而是需要站在前端本身角度,重新审视功能开发的惯性思维,找到一种适合不同场景下的前端开发方式。
数据上报方式梳理
image
通过将采集的数据拼接在图片请求的后面,向服务端请求一个 1*1 px 大小的图片(gif)实现的,设置它的 src 属性就可以发送数据。这种方式简单且天然可跨域,又兼容所有浏览器,没有阻塞问题,是目前比较受欢迎的前端数据上报方式。但由于是 get 请求,对上报的数据量有一定的限制,一般为 2~8 kb。代码示例如下:
/** 客户端 */
var img = new Image();
img.width = 1;
img.height = 1;
img.crossOrigin = "anonymous";
img.src = '/sa.gif?project=default&data=xxx';
/* 服务端 */
set('Access-Control-Allow-Origin', '*');
设置允许跨域也可以避免 canvas 会识别出资源的跨域问题而造成画布污染。
ajax
通过 XMLHttpRequest 的 send 方法以post的方式发送 data 给服务端,可以发送大量的数据,默认发送方式是异步,不会阻塞页面,但会占用一定的客户端资源,且需要特殊处理跨域限制。XMLHttpRequest 的跨域请求默认不携带 cookie。要允许跨域携带cookies,首先浏览器设置中,没有关闭第三方 cookie 功能,而且进行以下配置:
/** 客户端 */
xhr.credentials=true; // 默认值是 false
/** 服务端 */
set('Access-Control-Allow-Credentials', true)
//不能设为星号,必须指定明确的、与请求网页一致的域名。
set('Access-Control-Allow-Origin', getRequestHeader('origin'))
beacon
navigator.sendBeacon API,是指浏览器通过异步的 post 方式发送数据到服务端。具体使用方法如下:
/**
* @params url:数据将要被发送到的网络地址
* data:将要发送的 ArrayBufferView、Blob、DOMString 或者 FormData 类型的数据
* @Return {boolean} 用户代理是否成功把数据加入传输队列
*/
navigator.sendBeacon(url, data);
其特点很明显:
- 在浏览器空闲的时候(跳转、刷新、关闭页面时)异步发送数据,不影响页面诸如 JS、CSS Animation 等执行;
- 页面在非加载状态下,也会异步发送数据,不阻塞页面刷新、跳转和卸载等操作;
- 可以保证数据发送不易丢失,浏览器会对其进行调度以保证数据有效送达。
- 能够被客户端优化发送,尤其在 Mobile 环境下,可以将 beacon 请求合并到其他请求上一起处理;
- 不受跨域限制,浏览器兼容性较好,可以支持除 IE 之外的几乎所有浏览器。
- 当数据是 65536 字符长度时,异步请求进入浏览器发送队列失败,代表数据大小是有限制,不一样的浏览器应该有所差异。
- 缺陷是只能判断出是否放入浏览器任务队列,不能判断是否发送成功。
小结
综上,面对不同的场景,选择适合的方式发送上报数据:
- 如果发送数据量较小,比如采集用户在 Web 页面的页面浏览、元素点击、视区停留等行为事件,采用 image 方式上报给服务端更合适;
- 如果发送数据量较大,比如获取后端所有数据用于前端渲染,ajax 方式更合适;
- 如果需要进行精确统计,比如点击支付按钮、视频播放时长、页面跳转或关闭等行为时,选择 beacon 方式能最大程度保证数据成功率。