#每日一記#前端與後端交互 數據狀態設計 最佳實踐

每日一記 - 但並不日更

在前端頁面開發中,大部分的時間都是在與後端進行數據交互:獲取數據、計算並渲染。而頁面上又有大量的元素狀態須要維護,顯示、隱藏、變化。這些均可能讓咱們焦頭爛額,而後在一週後看不懂本身的代碼。javascript

無奈

因此項目開發的過程當中須要一個規範來約束代碼的走向,讓代碼能按照統一的、最高效的方式運行(還有讓別人閱讀)。這裏介紹一個前端對接後端接口數據的一個最佳實踐。html

先讓咱們看看反例,不知道你是否是用過這樣的 app:前端

  • 點了一個按鈕沒有反應(???這按鈕壞了),可是忽然頁面像爆炸了同樣不停的刷新(-,-啊救命)
  • 進入一個頁面,是個純白的(???網卡了?程序報錯了?),返回再進仍是純白,讓你搞不清楚到底發生了什麼。

這裏的例子說明:若是前端開發中不能把異常描述清楚、涵蓋全面,數據狀態的糟糕反饋就會直接影響用戶體驗。java

問題分析

咱們先從最簡單的狀況入手,一個頁面使用一個接口。這種狀況下一般是:後端

  • 全量獲取列表
  • 獲取主頁詳情
  • 發佈一張圖片
  • 搜索關鍵詞
  • ···

這樣的狀況又分兩種,服務器

  • 進入頁面時獲取數據 -> 渲染頁面
  • 進入頁面後進行操做 -> 獲得反饋 -> 渲染頁面。

不管是哪種狀況,咱們都只在一個頁面裏處理一個接口,這是最簡單的狀況。那麼咱們來看一下下面的圖片,並把它看成一個開發任務思考一下你會怎麼處理。網絡

老闆來了個需求

若是你只想到了**「調用接口」「渲染頁面」裏,那你這篇文章就是爲你寫的(笑)。其實上面的圖只向你展現了兩個狀態**:「初始狀態」「理想結果狀態」,我用了「理想結果」這個詞來描述這個狀態,是由於這是咱們在一切操做都完美的狀況下獲得的理想狀態。app

而一般在項目裏你只會從別人手裏獲得這兩張圖,我說的對嗎?(產品經理和設計師都默認你瞭解他們須要的一切)。函數

若是咱們但願作一個優秀的前端,咱們就須要馬上發現這裏還缺乏了三張圖(三個狀態)(有些交互裏並不須要這麼多狀態,這裏只討論最全面的狀況)優化

數據獲取中狀態 無數據狀態 數據異常狀態

一個接口的5個狀態

需求分析

從調用一個接口到渲染頁面咱們大體分爲一下幾部

調用接口 -> 獲得數據 -> 處理數據 -> 渲染

初始狀態

接下來咱們來編寫一些代碼,來對接接口而且管理數據和狀態。爲了使代碼更加聚合,用一個字面量對象 SeaerchInput 來維護狀態。而後咱們模擬一個接口的調用。

// 以上面搜索爲例

// 建立頁面對象
let SearchInput = {}

// 模擬一個接口
function API () {
  return new Promise(function (resolve, reject) {
    setTimeout(function () {
      let result = [{
        name: '李三'
      }];
      resolve(result);
    })
  })
}
複製代碼

理想結果狀態

接下來咱們在SearchInput中用data字段保存數據,用getSearchResult()方法綁定數據,調用接口並直接綁定數據,那麼咱們將獲得的「理想結果狀態」。

let SearchInput = {
  data: null,

  getSearchResult() {
    API.then(
      (res) => {
        this.data = res; // 綁定數據
      }
    )    
  }
}

SearchInput.getSearchResult();  // 獲取數據

function API () {
  return new Promise(function (resolve, reject) {
    // ...
  })
}
複製代碼
// html 的語法將使用 angular 指令去表達
<div>
  <!-- 渲染結果 -->
  <p ng-repeat="result in SearchInput.data"></p>
</div>
複製代碼

這樣的代碼是十分脆弱的,由於咱們已經默認數據會瞬間返回而且沒有任何問題。

數據獲取中狀態

function API () {
  return new Promise(function (resolve, reject) {
    setTimeout(function () {
      let result = [{
        name: '李三'
      }];
      resolve(result);
    }, 3000)  // 爲接口增長3秒的延時
  })
}
複製代碼

一旦給API增長點延時,就會發現頁面會在純白狀態下停留好久,由於頁面沒有任何提示,因此用戶根本沒法知道發生了什麼事情,是等待仍是返回?

爲此咱們須要管理從接口發起請求(request)到接收響應(response)這段時間的狀態,在SearchInput中用hasDone來保存接口的響應狀態,null表明這個接口還在初始化狀態,false表明已經發出請求但未收到響應,true表明已經收到響應。

let SearchInput = {
  data: null,
  hasDone: null, // 初始化

  getSearchResult() {
    this.hasDone = false; // 發起請求時置爲 false

    API.then(
      (res) => {
        this.hasDone = true; // 收到響應時置爲 true
        this.data = res;
      }
    )    
  }
}

SearchInput.getSearchResult();

function API () {
  return new Promise(function (resolve, reject) {
    setTimeout(function () {
      // ...
    }, 3000)  // 爲接口增長3秒的延時
  })
}
複製代碼
<!-- 數據獲取中狀態 -->
<div ng-if="SearchInput.hasDone === false">
  loading
</div>

<div ng-if="SearchInput.hasDone">
  <!-- 渲染結果 -->
  <p ng-repeat="result in SearchInput.data"></p>
</div>
複製代碼

這下好了,若是接口很慢頁面也會顯示 loading,用戶不會爲此不知所措了。

數據異常狀態

儘管如今網絡和服務器已經十分穩定,不多會出現異常,可是不管是網絡、服務器或代碼哪個出現異常而沒有考慮,那都會形成用很差的用戶體驗

function API () {
  return new Promise(function (resolve, reject) {
    setTimeout(function () {
      let error = '服務器異常';
      reject(error);  // 接口返回了異常
    })
  })
}
複製代碼

如今咱們假設咱們的API返回了異常,頁面又會變爲純白了,沒有任何數據顯示也沒有任何提示。

爲此咱們須要一個狀態來管理接口返回的狀態,在SearchInput中用hasSuccess來保存接口的返回狀態,null表明還在初始化狀態,false表明接口返回失敗,true表明接口成功返回數據。(你甚至能夠先判斷數據的格式、數量等是否知足你的要求,若是不知足要求,即便接口返回了數據,你同樣能夠將hasSuccess設置爲false,由於這裏的 success 表明了你獲得了能夠正確使用的數據,而不只僅是獲得了數據)

let SearchInput = {
  data: null,
  hasDone: null, 
  hasSuccess: null, // 初始化

  getSearchResult() {
    this.hasDone = false;

    API.then(
      (res) => {
        this.hasDone = true;
        this.hasSuccess = true; // 獲得數據置爲 true
        this.data = res;
      },
      (err) => {
        this.hasDone = true; // 此時咱們也要更新 hasDone
        this.hasSuccess = false; // 發生異常置爲 false
      }
    )    
  }
}

SearchInput.getSearchResult();

function API () {
  return new Promise(function (resolve, reject) {
    // ...
  })
}
複製代碼
<!-- 數據獲取中狀態 -->
<div ng-if="SearchInput.hasDone === false">
  loading
</div>

<!-- 數據異常狀態 -->
<div ng-if="SearchInput.hasDone && SearchInput.hasSuccess === false">
  數據異常
</div>

<div ng-if="SearchInput.hasDone && SearchInput.hasSuccess">
  <!-- 渲染結果 -->
  <p ng-repeat="result in SearchInput.data"></p>
</div>
複製代碼

如今咱們會在hasDone === true後知道數據是否正常,而且給出了錯誤的提示。

無數據狀態

最後一個狀態也是咱們要考慮的,當用戶嘗試搜索一個詞卻什麼都沒返回,又變成了可惡的純白界面,咱們還須要考慮一下當獲取數據時什麼都沒有的狀況。

function API () {
  return new Promise(function (resolve, reject) {
    setTimeout(function () {
      let result = [];  // 如今沒有任何結果
      resolve(result);
    })
  })
}
複製代碼

咱們須要一個狀態來管理數據的狀態,在SearchInput中用hasData來保存數據狀態,null表明還在初始化中,false表明數據爲空,true表明數據不爲空。

let SearchInput = {
  data: null,
  hasDone: null, 
  hasSuccess: null, 
  hasData: null, // 初始化

  getSearchResult() {
    this.hasDone = false;

    API.then(
      (res) => {
        this.hasDone = true;
        this.hasSuccess = true; 
        this.hasData = res.length > 0; // 有置爲 true,沒有數據置爲 false
        this.data = res;
      },
      (err) => {
        this.hasDone = true;
        this.hasSuccess = false; 
        this.hasData = false; // 失敗確定沒有數據了
      }
    )    
  }
}

SearchInput.getSearchResult();

function API () {
  return new Promise(function (resolve, reject) {
    // ...
  })
}
複製代碼
<!-- 數據獲取中狀態 -->
<div ng-if="SearchInput.hasDone === false">
  loading
</div>

<!-- 數據異常狀態 -->
<div ng-if="SearchInput.hasDone && SearchInput.hasSuccess === false">
  數據異常
</div>

<!-- 無數據狀態 -->
<div ng-if="SearchInput.hasDone && SearchInput.hasSuccess && SearchInput.hasData === false">
  數據異常
</div>

<div ng-if="SearchInput.hasDone && SearchInput.hasSuccess && SearchInput.hasData">
  <!-- 渲染結果 -->
  <p ng-repeat="result in SearchInput.data"></p>
</div>
複製代碼

如今上面的代碼基本上就是你所須要的了,它能夠幫你應對各類狀況,讓頁面展現的更加完美。

實踐分析

這一大段代碼就是對應一個簡單接口五個狀態的設計,也是我目前項目中使用的模式,雖然看上去比較繁瑣,可是相比後期再不停的補充和修改,一次性考慮全面帶來不少好處。

若是一個接口是爲了實現分頁加載,那麼狀態的數量又會有所提高,這篇文章再也不闡述。

若是一個頁面使用了多個接口,數據和狀態之間產生了交叉,爲了使狀態邏輯清晰應該合理利用字面量對象來聚合代碼邏輯。

在多人協做方面,因爲你們使用同一套規範,對代碼的閱讀速度有顯著提升。

這裏列出的代碼以普及爲主,不少實現細節方面均可以再去優化,提煉。甚至寫一個構造函數也是很方便的選擇。

感謝閱讀

羅小黑寫寫文字

若是喜歡文章 請留下一個贊~ 若是喜歡文章 分享給更多人~

掘金中關注我 在簡書中關注我

自由轉載-非商用-非衍生-保持署名(創意共享3.0許可證) 轉載時請保留原文連接 以保證可及時獲取對文章的訂正和修改

相關文章
相關標籤/搜索