深耕业务——商品列表底部分销商品资源广告位

背景:有赞微商城后台商品管理页,底部增长供货商品推荐资源位,根据不一样的商家展现不一样的商品!前端

做为深耕的业务,咱们就从一个我遇到的复杂需求开始作个引子。栗子以下(可先看图片过个眼瘾):node

image.png | left | 747x282

image.png | left | 747x642

需求列表以下

基本要求:react

  1. 基本 UI 实现
  2. BlockHeder,点击换一批切换至下一页,点击更多跳转至分销市场
  3. 商品卡片
  4. 曝光埋点数据:当商品底部出现用户视口发送曝光数据
  5. window resize 商品卡片数据适配

更高要求:程序员

  1. 性能优化:resize 获取数据减小 ajax 数据请求的次数;节流
  2. 数据缓存:当 size 小于等于 goodList.length 则不予以 ajax 请求数据,减小没必要要的带宽
  3. 非正常分页:更加不一样的 size 获取不一样的数据,以及分页
  4. 代码逻辑:VM 避免超级繁琐,代码的逻辑和归类清晰明了
  5. 低耦合:达到更松散的控制,对于之后的拆分和开发更加敏捷
  6. 错误隔离:当前分销广告位挂掉,不能影响到整个商品列表页!

当你们看到产品的 PRD ,看了这个需求和要求以后,每一个人根据本身的程序开发经验和设计经验上,每一个人都能给出不一样的解决方案。其实呢,每一个解决方案都是一种方式,只是在不一样的角度上实施以及设计的思惟上不一样。So,我想分享给你们的,也是通过个人思考后以及完善的一种解决方案,拿出来仅供参考。ajax

到底什么是业务

分享实施方案前,先讨论一下到底什么是业务;而程序员写代码都是服务于业务的么? 在老板眼中,业务就是赚钱的工具;在销售员眼中,业务就是必须完成的指标;在产品的眼中,业务就是须要实现完成的需求。。。每一个人对业务的理解都不同,可是,有谁考虑过,在前端开发工程师眼中的业务究竟是什么??后端

下面是我站在前端的角度去理解业务,以下:浏览器

image.png | center | 588x644

So,在个人理解里,前端所写的业务拆分红为6大部分:缓存

  1. 业务数据:负责获取业务数据
  2. 业务逻辑:实现产品所定义的规则
  3. 逻辑数据:经过一系列规则所产出的逻辑数据
  4. 视图数据:经过逻辑数据转换成视图数据(不将逻辑和视图直接绑定)
  5. 视图展现:经过视图数据,直接驱动视图层展现对应视图
  6. 视图功能:经过视图展现组装成的需求功能

在简单的业务需求中,可能我拿到的后端数据,就直接能够渲染视图层,而后就完善功能。从开发的成本和复杂度考量上,是不值得去作业务拆分。因此,在复杂的业务需求中以及兼顾拆分和维护中,这种业务方法论就能够大展手脚了。如下就是我拿开头的例子,详细解析围绕业务的 6 大部分的设计。性能优化

具体实现步骤

第一步:基本组件的划分

结构划分:本次组件划分的规则是先上下、再左右、由外至内;划分结果是工具

BackHeader

image.png | left | 747x47

GoodsList

image.png | left | 747x270

GoodsCard

image.png | center | 241x385

功能划分: 顶级容器:错误隔离,灰度控制,埋点数据及控制,ajax 请求。 视图展现:分发数据,组件回调

第二步:实施

:::info 提示:由react16 提供 componentDidCatch 进行错误隔离,也可使用 try...catch 实现错误隔离 :::

react 中,最核心、也就最灵活的处理视图变化的方式呢,就将驱动视图的数据作成可配的;因此第一步,根据结构的划分,将需求作成数据的配置,代码以下:

render() {
    const { goodsList, pageIndex, size } = this.state;

    if (!goodsList.length) {
      return <div ref={node => this.fenxiaoRef = node} />;
    }

    return (
      <div className="fenxiao-recommend " ref={node => this.fenxiaoRef = node}>
        <BlockHeader onGoodsChange={this.handlerClick} />
        <GoodsListContainer size={size} pageIndex={pageIndex} goodsList={goodsList} />
      </div>
    );
  }
复制代码

顶级容器组件在 DidMount 时,作灰度控制,ajax 获取数据驱动视图。BlockHeader 接收 props onGoodsChange 回调,请求数据,再分发数据。

计算当前可展现的商品数量 size ;获取顶层容器 ref 获取容器获取容器宽度。代码以下:

/** * 计算展现多少卡片 * 返回值 * size */
computedStyle = () => {
  const { fenxiaoRef } = this;
  if (!fenxiaoRef) {
    return;
  }
  const { width } = fenxiaoRef.getBoundingClientRect();
  let size = Math.floor(width / 144);
  this.setState({ size });
}
复制代码

根据 size 获取商品的个数 goodsList  ,并监听 window resize ;其中如下集中状况则不予以请求

  • size <= goodsList.length 时;
  • 点击换一批不予以从新计算 size
  • 节流 300ms

fenxiao-size.gif | center | 747x438

埋点数据:曝光 曝光埋点的基本需求是,当商品出如今浏览器的视口中后(即用户的视口),发送黄金令箭!曝光一个商品发送一支令箭!

那么问题来了,如何判断当前分销商品列表广告位(分销商品 DOM 元素)出如今用户的视口呢?

我采用了一种比较 low ,也是最常规的解决方案:以浏览器视口左下角为原点坐标,向上为正,向下为负; 获取当前 DOM 距离页面顶部的距离,即 const { top } = node.getBoundingClientRect() ,获取视口高度 document.ocumentElement.clientHeight

// 以页面右下角为原点,计算分销商品是否 曝光

export function isExposure(node) {
  if (!node) {
    return null;
  }
  // 视口高度
  const clientHeight = ddcocument.documentElement.clientHeight;
  const { top } = node.getBoundingClientRect();
  return clientHeight > top;
}

复制代码

fenxiao-log.gif | center | 747x552

总结

“小公司写代码,大公司改代码”好多年前就据说过这么一句话,可是做为程序猿来讲,最大的痛点就是:别人不写文档,别人不写注释,以及写文档,写注释!因此在原有系统的维护当中,尽最大可能作到:

  • 代码清爽,可读
  • 逻辑清晰,便于理解,没有隐讳
  • 每一行废代码(由于你写的代码,你不删除,没有人敢删)
  • 不放过每个细节的优化
相关文章
相关标签/搜索