动态

详情 返回 返回

RSM:超實用的多場景請求管理方案 - 动态 详情

各位前端er們,用了那麼久的再熟悉不過的接口請求,有沒有覺得不妥???你可能會覺得,這有什麼不妥,不就axios.get一下,fetch一下,請求就發出了嘛,然後再處理返回的數據,完事。真有這麼簡單嗎?問題來了,針對不同場景下的請求需求,真的可以統一這樣處理就完事嗎?我們來聊聊請求這事兒!

聊聊前端請求的場景

我們先來聊聊前端請求的各種場景,以下是一些做請求時遇到的高頻場景。

  1. 什麼時候發出請求,是一進入頁面,還是點擊某個按鈕再觸發,還是某個數據改變後就觸發,還是...?
  2. 是否要在頁面上展示請求狀態?
  3. 某個請求是否要封裝成一個函數,以便重複調用,比如翻頁請求;
  4. 用户可能會高頻請求的接口是否做數據緩存,比如在數秒內會重複查看的數據?
  5. 處理完一些數據後需要跨頁面或模塊操作數據,要怎麼做更方便?總不能都放在全局狀態內吧;
  6. 離線了還能安全提交數據嗎,比如編輯器需要頻繁自動保存草稿,突然離線了怎麼辦?
  7. 其他請求場景...

這些請求場景在不同項目中,或相同項目的不同的位置都需要做不同的處理,如果我們還只是單純的用請求庫發起請求,那就會有失性能和優雅度。

對請求場景進行管理

因為上面這些問題,就產生了一個概念,叫請求場景管理(RSM,Request scene management),下面我們來聊聊這個概念,先上圖,英文圖,自行翻譯。

image.png

和Flux概念一樣,它僅僅只是提出了一套流程規範,然後各開發者可以根據它進行參考改造變形一通形成自己獨有的思路,再然後編碼實現它。

我的理解是它更像是給請求庫安上了機械臂、噴漆桶什麼的,把請求庫武裝起來了,就像鋼鐵俠一樣。

123.png

在這裏,這個規範提出了4個流程,分別如下:

請求時機

描述在什麼時候需要發出請求。

  • 初始化展示數據,如剛進入某個界面或子界面;
  • 人機交互觸發CS交互,需要變更數據重新發出請求,如翻頁、篩選、排序、模糊搜索等;
  • 預加載數據,如分頁內預先加載下一頁內容、預測用户點擊某個按鈕後預先拉取數據;
  • 操作服務端數據,需發出增刪改查請求,如提交數據、刪除數據等;
  • 同步服務端狀態,如數據變化較快的場景下輪詢請求、操作了某個數據後重新拉取數據;

請求行為

描述以怎樣的方式處理請求。

  • 佔位請求,請求時展示loading、骨架圖、或者是上次使用的真實數據;
  • 緩存高頻響應,多次執行請求會使用保鮮數據;
  • 多請求串行與並行;
  • 對頻繁的請求進行防抖,避免前端數據閃動,以及降低服務端壓力;
  • 重要接口重試機制,降低網絡不穩定造成的請求失敗概率;
  • 靜默提交,當只關心提交數據時,提交請求後直接響應成功事件,後台保證請求成功;
  • 離線提交,離線時將提交數據暫存到本地,網絡連接後再提交;

請求事件

表示攜帶請求參數發送請求,獲得響應,這就是我們最常用到的axiosfetchXMLHttpRequest等,看!這裏我們要重新認識上面這些請求方案的定位了,它只是作為請求場景管理的環節之一。

響應數據管理

顧名思義,就是統一管理響應數據,任何位置都可以對響應數據進行操作,這部分基本是結合reactvue等MVVM框架的狀態機制來使用的,就好像有個專門用來管理響應數據的reduxvuex,你可以跨模塊操作這些狀態化的響應數據,而不用什麼事件機制。

  • 移除緩存響應數據,再次發起請求時將從服務端拉取;
  • 更新緩存響應數據,可更新任意位置響應數據,非常有利於跨頁面更新數據;
  • 刷新響應數據,可重新刷新任意位置的響應數據,也非常有利於跨頁面更新數據;
  • 自定義設置緩存,在請求批量數據時,可手動對批量數據一一設置緩存,從而滿足後續單條數據的緩存命中;

alova,一個RSM實現庫

alova是我發現的較為簡易上手的RSM實現庫,因為它的設計真的很像axios,新手也能很快上手,同時它也可以和任意的請求庫進行協作。

簡單演示下alova的使用方法,終於要開始實操上代碼了,在使用alova之前需要先創建一個實例,假設我們在vue項目中使用。

import { createAlova } from 'alova';
import VueHook from 'alova/vue';
import GlobalFetch from 'alova';

const alovaInstance = createAlova({
    // 請求接口前綴
    baseURL: 'https://api.alovajs.org',
    
    // 用於給mvvm庫創建狀態化響應數據
    // vue項目傳入VueHook,react項目傳入ReactHook
    statesHook: VueHook,
    
    // 傳一個請求適配器,GlobalFetch是我們提供的fetch api適配器
    // 你想用axios也可以自定義一個適配器
    requestAdapter: GlobalFetch(),
    
    // 是不是有熟悉的味道
    beforeRequest(config) {
        config.headers.token = 'tokenxxx';
    },
    async responsed(response) {
      const json = await response.json();
      if (json.code !== 200) {
        throw new Error(json.message);
      }
      return json.data;
    },
});

以Todo為例,發起todo詳情請求

// 先定義一個請求函數,該函數返回的是一個請求對象,表示一次請求的信息,但還不會實際發出請求
// 它的用法很接近axios
const getTodoDetail = id => alovaInstance.Get('/todo', {
    params: {
        id
    },
    
    // 本地緩存50000毫秒,再次請求時將會命中緩存,而不會再次發起請求
    localCache: 50000,
});

// 發起請求
const {
    // loading是加載狀態值,當加載時它的值為true,結束後自動更新為false
    // Vue3環境下,它是一個Ref類型的值,你可以通過loading.value訪問它,或直接綁定到界面中
    loading,
    

    // 狀態化的響應數據
    data: todoDetail,

    // 請求錯誤對象,請求錯誤時有值,否則為undefined
    error,

    // 成功回調綁定
    onSuccess,

    // 失敗回調綁定
    onError,

    // 完成回調綁定
    onComplete,
    
} = useRequest(getTodoDetail(this.$params.todoId)); // 將請求對象傳入即可發送請求
onSuccess(todoListRaw => {
  console.log('請求成功,這裏也可以訪問響應數據:', todoListRaw);
});
onError(error => {
  console.log('請求失敗,錯誤信息為:', error);
});
onComplete(() => {
  console.log('請求完成,不管成功失敗都會調用');
});

你可以直接使用useRequest返回的狀態綁定到界面上

<div v-if="loading">Loading...</div>
<div v-else-if="error" class="error">{{ error.message }}</div>
<template v-else>
    <div class="todo-title">{{ todoDetail.title }}</div>
    <div class="todo-time">{{ todoDetail.time }}</div>
</template>

提交數據

// 創建提交數據的請求對象
const createTodo = newTodo => alovaInstance.Post('/create-todo', newTodo);

const {
    loading,
    data,
    error,

    // 手動發送器請求的函數,調用後發送請求
    send: addTodo,
} = useRequest(newTodo => createTodoPoster(newTodo), {
    // 當immediate為false時,默認不發出
    immediate: false
});

// 假設這是我們需要提交的數據
const newTodo = {
    title: '新的todo項',
    time: new Date().toLocaleString()
};

// 用於提交成功後刷新todo列表數據
const { fetch } = useFetcher(alovaInstance);

// 手動發送請求
const handleAddTodo = async () => {
    try {
        cosnt result = await addTodo(newTodo);
        console.log('新增todo項成功,響應數據為:', result);
        
        // 提交成功後可能需要刷新todo列表數據,可以在這裏調用fetch函數
        fetch(getTodoList());
    } catch(error) {
        console.log('新增todo項失敗,錯誤信息為:', error);
    }
};

在界面中點擊按鈕發起請求

<!-- 忽略其他... -->
<button @click="handleAddTodo">創建todo</button>

總結

因為篇幅的原因,今天的分享就先到這啦,這裏也只是演示了一點點alova的功能,它的強大還不止於此,它也給了我們很多很實用的請求處理方案,不管是在項目中提升性能,提升代碼優越度,亦或是降低服務端壓力,都有很大的幫助。有興趣的小夥伴可以去github上詳細瞭解瞭解!

各位,你們覺得還有哪些請求場景的例子嗎?

user avatar honwhy 头像 Leesz 头像 alibabawenyujishu 头像 guochenglong 头像 nihaojob 头像 razyliang 头像 leexiaohui1997 头像 huajianketang 头像 zero_dev 头像 solvep 头像 dunizb 头像 febobo 头像
点赞 184 用户, 点赞了这篇动态!
点赞

Add a new 评论

Some HTML is okay.