從合併請求角度談性能優化

從合併請求角度談性能優化

  性能優化是前端非常重要的一塊,可下手的地方有很多,比如圖片層次、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,性能提高。

Tags: