天天看點

針對多狀态訂單詳情的前端mock方案

作者:閑魚技術——樹城

背景

閑魚作為國内最大的二手交易電商平台,有着驗貨寶/省心賣/優品等有着閑魚特色的交易鍊路,而作為交易鍊路的閉環,一旦形成有效訂單,就會有對應的訂單詳情頁,訂單詳情頁往往承載着複雜的交易狀态的變化。以驗貨寶為例, 驗貨寶是閑魚推出針對二手商品存在的品質/真僞的不确定性,提出的先驗貨後交易模式。

作為交易訂單節點: 就有買家拍下->付款->賣家發貨->鑒定方收貨->鑒定為真->發貨給買家->買家收貨 等多個交易節點,一個訂單詳情頁在不同流轉狀态下可能存在數十種細分狀态,每種狀态之間存在着有着文案/操作按鈕/進度條等視覺互動上的差别, 如下圖所示:

針對多狀态訂單詳情的前端mock方案

存在問題

訂單詳情頁的不同的狀态視圖流轉,依賴于接口傳回的字段的改變,存在和服務端的溝通成本;

前端的直接mock的方式,像集團内的山海關,dummy更多是通用的mock資料映射,本質還是要手動修改mock資料的status值;在真機調試時,依賴的Charles的pc代理,單次修改成本高;

除了開發同學外,測試同學測試回歸的成本高. UI同學在視覺走查中難以覆寫全部,容易帶問題到線上,引起線上輿情。

總結來說,面對此類訂單的訂單詳情頁的前端開發測試,我們現有的mock存在着以下問題:

  • 開發聯調周期長,自測難以充分;
  • 測試/視覺回歸成本高;
  • 線上樣式問題定位還原周期長;

需求分析

針對以上的問題,能否讓訂單狀态頁面的mock方式更為易用,我們通過真實的開發體驗,提出了針對此類頁面的mock方式要達到好用,應該具備以下特點:

  1. 調試友善
mock方式應該在本地pc端調試和真機調試都能夠友善使用;
  1. 業務語義
不同狀态的切換互動應該帶有業務語義,能夠友善開發和測試快速找到想要的訂單狀态,而不隻是簡單的修改接口的某個字段值;
  1. 代碼解耦
mock的方式本身應該和業務代碼盡量解耦,即不會将mock的邏輯引入線上環境;
  1. mock資料精簡
訂單頁面狀态雖多,但是接口往往統一,接口字段,對mock資料的維護不應該每一種狀态都是單獨一份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裡進行引入,很好地實作與業務層代碼的解耦。

{
  "plugins": [
    [
      "build-plugin-rax-app",
      {
        "targets": [
          "web"
        ],
        "type": "mpa"
      }
    ],
    "@ali/build-plugin-rax-mock",
    "./selfBuild"
  ]
}           

​rax-app的插件機制提供的針對webpack打包方案所提供的onGetWebpackConfig api将視圖層元件植入業務頁面,

如下方代碼所示,會在打包過程中根據根據指定頁面檔案路徑選擇性地注入,并且判斷編譯環境在真實生産環境中不做任何mock子產品的打包。

if (api.context.command === 'build') return;
    api.onGetWebpackConfig('web', (config) => {
      config.entryPoints.values().forEach(entry => {
        const entrys = entry.values();
        const entryName = entrys[1];
        // 隻對訂單頁面注入
        if (!/pages\/Order\/index$/.test(entryName)) {
          return;
        }
 
        const prefixLoader = __filename;
        const debugOrderPath = path.resolve(__dirname, 'src/components/DebugOrder');
        const newOrderPage = `${prefixLoader}?debugOrderPath=${debugOrderPath}!${entryName}`;
     
        entry.clear();
        // 視圖層元件 
        entry.add(entrys[0]);
        entry.add(newOrderPage);
      });
    });
  }           

是以通過膠水層,我們可以快速将mock子產品和視圖層子產品快速引入到工程方案中,并能按需引入,不對業務造成明顯侵入痕迹。

mock層實作

首先我們會根據狀态合集所需訂單詳情接口生成一份mock.json資料的合集,這樣就能涵蓋所有狀态下所需消費字段,一般接口格式如下所示:

{
    "api": "mtop.a.order.info",
    "data": {
      "status": 0,
      "orderStatus": 1001,
        ...
      "trade": {
        "actions": [],
          ....
        "amount": "2189.00",
        "attributes": {
          "consis": "10",
            ...
          "ultronPP": "a_3_0@c",
         }
       }
     },
     "ret": [
      "SUCCESS:成功"
        ],
        "v": "1.0"
}           

如下圖所示,mock層既要更新視圖層切換狀态而組成新的mock.json, 也要攔截頁面側發起的mtop網絡請求定向到對應的mock.json檔案;

針對多狀态訂單詳情的前端mock方案

為了真實能夠把接口請求到本地,需要對h5頁面側發起的mtop請求進行攔截,這裡利用了淘寶mtop庫的運作機制會将mtop對象加載到頁面全局的window.lib對象上,利用Proxy的代理機制監聽window.lib對象的挂載mtop時機set進行hook, 并根據所處的生産環境判斷是否使用發送請求到本地的自定義request請求, 實作代碼如下:

lib = window.lib;
// 攔截Mtop對象的request方法挂載
const getMtop = (originValue) => 
new Proxy(originValue, {
  set(target, p, v, r) {
    if (p === 'request' 
    || p === 'H5Request') {
      Reflect.set(target, p, getRequest(v), r);
    } else {
      Reflect.set(target, p, v, r);
    }

    return true;
  }
});

// 攔截window.lib對象挂載mtop
if (!lib) {
    lib = new Proxy({}, {
    set(target, p, v, r) {
      if (p.toLowerCase() === 'mtop') {
        Reflect.set(target, p, v,r);
      } else {
        Reflect.set(target, p, v, r);
      }
      return true;
    }
  });
} else if (!lib.mtop) {
lib.mtop = getMtop({});
} else {
lib.mtop.request = getRequest(lib.mtop.request);
lib.mtop.H5Request = getRequest(lib.mtop.H5Request);
}

// 根據運作環境選擇加載對應的request請求
function getRequest(originRequest) {
  return async function () {
    if (getMockSwitch()) {
      //
    }

    return originRequest();
  }
}           

當挂載完成即可實作接口的mock功能:

  1. 當本地頁面發起 mtop 請求,如:mtop.com.test.one;
  2. 請求被注入的插件代碼 hold 住,當判斷是在本地開發環境或者連結帶上mock query時, 用本地的mtopRquest替換,請求以 http://127.0.0.1/ _mtop_mock_/com.test.one 格式重新發起請求;
  3. 請求打到本地 webpack-dev-server 上,server 再去本地 mock 目錄上找 com.test.one.js
  4. 如果找到就執行 com.test.one.js 檔案,将執行結果傳回
  5. 如果未找到,則走原有的 mtop 請求;

視圖層實作

視圖層UI由頁面側直接可見的切換icon和彈層構成,icon設定為dragable友善使用者随時拖動, 彈層的渲染考慮到擴充性由樹狀節點組成。目前根據真實的業務需要,将節點層級分為2層(可擴充),一級節點代表:已下單,已驗貨等核心節點, 二級節點代表在一級節點下可能存在的正負向交易細節節點, 如:在已下單環節下存在取消訂單的副狀态;

單點資料結構為如下代碼所示,包含有每個枚舉狀态的語義文案,每種狀态對應接口字段的主副狀态碼,狀态枚舉值,子節點。

// 狀态資料結構
export interface ClassifyDataItem {
  [key: string]: any;
  /**
   * 節點名稱
   */
  nodeName: string;
  /**
   * 節點枚舉值
   */
  node?: StatusEnum;
  /**
   * 對應的協定主狀态
   */
  status?: string;
  /**
   * 對應的協定副狀态
   */
  subStatus?: string;
  childNode?: ClassifyDataItem[];
}

// 真實的狀态資料枚舉
export const classifyData: ClassifyDataItem[] = [
  {
    nodeName: '已下單,等待順豐上門取件',
    status: '1',
    subStatus: '10',
    node: StatusEnum.BUYER_CREATE,
    childNode: [
     ...classifyData
    ]
  }
  ...
];           

每個節點在渲染過程中采用樹狀遞歸渲染,視覺層展示nodeName, 同時綁定點選回調onItemChange。當彈窗的某一狀态結點發生點選行為時,設定節點選中态樣式,同時通過Modal元件通過props傳入的函數方法向外層傳遞綁定在結點上的status和subStatus值,進而實作了點選單個選項能夠修改對應的mock.json檔案, 同時會觸發頁面的window.reload()功能重新加載頁面發起mtop請求,是以此時接口傳回的資料已經是切換後的狀态資料,是以頁面也會呈現對應狀态的視圖。

業務應用

目前該方案已經在閑魚驗貨寶、奢侈品寄賣等交易鍊路場景下接入, 從真實使用過程中,能夠有效提升開發聯調的效率,單次狀态的切換時間成本從分鐘級别下降到秒級, 并能幫助測試和視覺同學快速回歸業務場景,定位線上問題,簡單的視覺問題不再依賴于後端接口的狀态切換,約能節省30%以上的聯調溝通成本。在項目開發人員出現流動時,新人能夠通過這套方案方式快速地了解業務,上手開發。

針對多狀态訂單詳情的前端mock方案

總結與展望

本章節主要介紹了閑魚在前端開發典型交易場景-訂單詳情頁時,面對多狀态模式切換頁面的一種開發體驗提升的一種嘗試,通過上述的方案設計基本實作了開篇所定義的業務mock的4個目标:調試友善、業務語義、代碼解耦、mock資料精簡,不僅提升了在開發/測試同學的體驗,通過語義化的方式也能幫助項目更好地進行維護更新。

伴随着業務系統的疊代,目前方案隻針對單頁面mock, 如何去覆寫整個交易鍊路的mock,比如一個完整訂單鍊路的狀态一緻性,也是值得探索的方向。