既然是經驗談,那麼可能會有不適用的場景,可以根據開發上的需要斟酌參考。
在開發的時候,總不可能都打 production 的 API(如果是的話可能要注意了...),而是會根據環境不同而設定不同的 API domain,例如在 dev 的時候就用 api.app-dev.com
在 staging 時用 api.app-staging.com
等等,這時候就很適合用下列的方式來管理你的 API:
const API_BASE_URLS = {
development: 'api.app-dev.com',
staging: 'api.app-staging.com',
production: 'api.app.com',
};
const getAPIBase = (env = process.env.NODE_ENV) => API_BASE_URLS[env] || API_BASE_URLS.development;
你可以搭配 webpack 的 DefinePlugin 在打包的時候自動根據環境變數來設定 API。
如果你每次在呼叫 API 的時候都直接用 fetch 或是 ajax 呼叫,當後端改變實作,例如原本 api 在同一個 domain,但因為後端想要搬到別的 domain 時,為了讓 cookie 順利運作,就需要額外加上 credenticals
的設定。
這時候如果你沒有額外包裝的話,就要到處去看每個用到 fetch API 的地方然後一個個修改,API 一多改起來就會很麻煩(雖然也可以直接尋找替代,但百密總有一疏)。這時候如果有統一包裝起來的話就比較方便,像 axios 可以用 axios.create()
來建立一個 client,管理上就會舒服許多。
當然你也可以自己提供選項來做包裝,不一定要用 axios,不過 axios 真的挺好用的,尤其是 interceptor 的功能,簡直是後端改 API 規格時的救星,值得參考一下。
這也是蠻建議將 API 包成比較通用的函數的原因之一,你可以在裡面做一次最基本的錯誤處理,例如跳個錯誤彈窗之類的。
fetch 或是 ajax 寫久了難免就直接偷懶不寫 catch 或 onerror 了,如果每個 API 都有自己再寫一次相同的錯誤處理邏輯又顯得有些麻煩。
const makeAPIClient = ({
baseURL,
retry,
timeout,
onRequestError,
headers,
credentials,
}) => ({
endpoint,
method,
body,
}) => {
return fetch(baseURL ? `${baseURL}/${endpoint}` : endpoint, {
headers,
credentials,
method,
body,
})
.then() // do your retry logic
.catch(onRequestError)
}
實作起來大概像這樣,不過在實作上可能會有更多細節需要參考。
如果有使用一些 error reporting 的平台(像是 sentry、rollbar 或是自家平台等),可以將錯誤記錄到後台方便追蹤,如果不知道選哪個蠻推薦 sentry 的,介面好看而且很好追蹤錯誤,數據一目瞭然。
最重要的就是注意後端的 SQL 有沒有寫好,像是:
分頁參數這點很重要,有時候後端會嫌麻煩又覺得資料量不大就索性不做了,一旦資料量增長,回應速度跟渲染多筆資料時就會有效能上的影響。
再來是 sortBy 與 order 的功能,你或許會覺得這在前端也可以做到,但要記住前端只能對**「已經存在瀏覽器的資料做排序」**,也就是除非拿到全部的資料,不然在前端正確實作 sortBy 與 order 是不可能的事。
如果需要實作無限滾動,要考量的問題就更多了,當資料量太大時,你可能需要實作 windowing 來增加效能,或是直接限制每次 render 的資料量,這點我們在進階篇會談到。
這三天我們一起探討了前端在實作 API 時可能需要考量的事,當然根據使用場景不同,或許不用做到那麼完美,可能也有許多沒有提到的事。但我盡量總結了在串接 API 時可能會遇到的事,希望大家可以少走一些冤枉路。