深耕業務——商品列表底部分銷商品資源廣告位

背景:有贊微商城後臺商品管理頁,底部增長供貨商品推薦資源位,根據不一樣的商家展現不一樣的商品!前端

做爲深耕的業務,咱們就從一個我遇到的複雜需求開始作個引子。栗子以下(可先看圖片過個眼癮):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

總結

「小公司寫代碼,大公司改代碼」好多年前就據說過這麼一句話,可是做爲程序猿來講,最大的痛點就是:別人不寫文檔,別人不寫註釋,以及寫文檔,寫註釋!因此在原有系統的維護當中,盡最大可能作到:

  • 代碼清爽,可讀
  • 邏輯清晰,便於理解,沒有隱諱
  • 每一行廢代碼(由於你寫的代碼,你不刪除,沒有人敢刪)
  • 不放過每個細節的優化
相關文章
相關標籤/搜索