從合併請求角度談性能優化
從合併請求角度談性能優化
性能優化是前端非常重要的一塊,可下手的地方有很多,比如圖片層次、JS層次、webpack工程層次、CSS層次、CDN層次等等,其中關於請求的優化是整個項目運行中非常重要的一塊,所以這次我們來講一下請求優化的一個小方法。
來看需求
最近在一個React項目中有看見一個類似結構的程式碼,其中reqData、reqListData、reqProData是三個函數,內部分別有三個不同的Promise請求,請求獲得數據後,將數據保存至this.state中。
componentDidMount() {
this.reqData();
this.reqListData();
this.reqProData();
}
/**
* 第一個請求
**/
reqData() {
ajax.getData().then(res => {
// do something with this.setState({})
})
}
/**
* 第二個請求
**/
reqListData() {
ajax.getListData().then(res => {
// do something with this.setState({})
})
}
/**
* 第三個請求
**/
reqProData() {
ajax.getProData().then(res => {
// do something with this.setState({})
})
}
事實上,這段程式碼是沒有問題的,畢竟在這個需求中,這三個返回的數據都是互不干擾互不聯繫的,但是在我們開發的過程中,我們需要有想到可能日後在每個地方都會有變動的地方,假如在後續開發中,我們在第二個請求的時候,需要根據第一個請求返回的數據進行判斷再插入this.state中呢?這是一個非常有可能性的需求。
而且值得注意的是,這是一個React項目,使用this.setState的時候,會觸發render方法重新渲染你的介面(這裡不考慮其他的,如shouldComponentUpdate等情況),那麼這樣來構建程式碼,會產生非常多次的無必要的render,嚴重影響性能。
那麼我們的需求就很清晰了,我們需要將多次的render縮減為一次,並使得請求返回的數據能夠互相有業務邏輯來往,所以在這裡,我們選擇使用Promise.all
Promise.all
Promise.all大家應該不陌生,作為ES6中作為Promise的伴生靜態方法,他有一個顯著功效:將多個Promise對象實例包裝,生成並返回一個新的Promise實例。
使用這個方法,你可以將多個的Promise以數組參數的形式傳入,當所有Promise實例全部(注意是全部)變為resolve後,該方法才會返回,並可在then方法中獲得一個數組形式的結果,你可以使用數組解構的方法輕鬆獲取這些參數
// 使用方法
Promise.all([p1, p2, p3]).then(function (results) {
let [res1, res2, res3] = results;
});
下面的是部落客給大家寫的示例,讓大家簡單的了解一下這個Promise.all的使用方法。
// 示例
let promise1 = new Promise(function (resolve, reject) {
if (true) {
resolve(1);
} else {
reject(false);
}
});
let promise2 = new Promise(function (resolve, reject) {
if (true) {
resolve(2);
} else {
reject(false);
}
});
let promise3 = new Promise(function (resolve, reject) {
if (true) {
resolve(3);
} else {
reject(false);
}
});
Promise.all([promise1, promise2, promise3]).then(function (results) {
let [res1, res2, res3] = results;
console.log(res1);
console.log(res2);
console.log(res3);
if (res2 === 2) {
console.log(res3 * 3);
}
// do something with this.setState({})
});
解決
OK那麼接下來方向就很明確了,於是我將componentDidMount中的程式碼修改成了以下的結構。
componentDidMount() {
// ajax此處的ajax只是自己的封裝,實際上ajax.reqData()等參數還是 Promise 格式
Promise.all([ajax.getData(), ajax.getListData(), ajax.getProData()]).then((results) => {
const [res1, res2, res3] = results;
// 將結果作為參數傳入,根據內部邏輯返回需要setState的數據
const { data } = this.reqDataFormat(res1);
const { list } = this.reqListDataFormat(res2);
const { pro } = this.reqProDataFormat(res3);
if (list) {
// pro = list.res;
// Do something with side effects
}
this.setState({ data, list, pro })
});
}
合併請求後根據測試,耗時不增反而略有下降,可讀性上升,減少三次rander,性能提高。