React 性能優化:避免重複計算

這是我參與 8 月更文挑戰的第 12 天,活動詳情查看:8月更文挑戰react

什麼是 Selector ?

在介紹 Reselect 的以前,咱們首先要知道 Selector 這個概念,那麼什麼是 Selector 呢?我看到一種很形象的說法:想象一下你去便利店買可樂,你給店員說要可口可可,這時候店員就去給你拿一罐可口可樂,這時候,店員其實就是充當了 Selector 的角色,店員知道如何從各類商品裏拿到你要的可口可樂,具體來講 Selctor 有如下特色:git

  • Selector 知道從哪裏,以及如何去獲取數據的子集
  • Selector 會返回數據的子集 用代碼來表示就是

什麼是 Reselect ?

Reselect 做爲一個配合 Redux(或其餘)的 使用的一款輕量型的狀態選擇庫:github

  • 能夠計算派生數據,從而使初始狀態的存儲儘量少。
  • 是高效的,只要選擇器的參數不發生改變,計算就不會發生。
  • 可組合的,選擇器能夠做爲參數傳給其餘選擇器。

爲何須要 Reselector ?

React 有一個原則:可以計算獲得的狀態必定要計算得到,state 中存儲最原始的數據。這樣的原則可能會帶來沒必要要的 render 並伴隨大量複雜的重複計算(Selector),從而產生了性能的損耗,而 ReselectSelector 提供了緩存的能力,避免了重複計算,從而提高性能。npm

開始

安裝

# npm
npm install reselect

# yarn
yarn add reselect
複製代碼

官方入門例子

簡易的價格計算器,須要緩存的計算屬性有:商品總價(subtotalSelector)、稅費(taxSelector)和總價(totalSelector)。商品數量(shopItemsSelector)和納稅(taxPercentSelector)比例不變,就不會發生從新計算。數組

createSelector 傳入的 inputSelector 返回值會做爲最後一個函數參數的傳參,以 subtotalSelector 爲例子,shopItemsSelector 的返回值會做爲 items 傳入。緩存

import { createSelector } from 'reselect';

const shopItemsSelector = (state) => state.shop.items;
const taxPercentSelector = (state) => state.shop.taxPercent;

const subtotalSelector = createSelector(shopItemsSelector, (items) =>
  items.reduce((subtotal, item) => subtotal + item.value, 0),
);

const taxSelector = createSelector(
  subtotalSelector,
  taxPercentSelector,
  (subtotal, taxPercent) => subtotal * (taxPercent / 100),
);

const totalSelector = createSelector(
  subtotalSelector,
  taxSelector,
  (subtotal, tax) => ({ total: subtotal + tax }),
);

const exampleState = {
  shop: {
    taxPercent: 8,
    items: [
      { name: 'apple', value: 1.2 },
      { name: 'orange', value: 0.95 },
    ],
  },
};

console.log(subtotalSelector(exampleState)); // 2.15
console.log(taxSelector(exampleState)); // 0.172
console.log(totalSelector(exampleState)); // { total: 2.322 }
複製代碼

應用到項目

拉出咱們以前的 列表項目 進行一下改造。當前頁面上的數據是經過 state.itemsstate.byId 計算獲得的一組對象數組,頁面上的任何狀態(state.search)發生變化,都會引發列表數據的重計算(get data source),看動圖 ⬇️性能優化

reselect-1.gif

那麼,就這點來優化一下。markdown

安裝 reselect,引入 { createSelector }網絡

import { createSelector } from 'reselect';
複製代碼

建立 dataSourceSelector 函數,監聽 itemsbyIdantd

const getItems = (state) => state.items;
const getById = (state) => state.byId;

const dataSourceSelector = createSelector(getItems, getById, (items, byId) => {
  console.log('reselect: get data sourc!');
  if (!items) return [];
  return items.map((id) => byId[id]);
});
複製代碼

在組件和 Redux 鏈接時,傳入新屬性 dataSource,並取代原來的 getDataSource 方法。

const mapStateToProps = function (state) {
  return {
    ...state.table,
    dataSource: dataSourceSelector(state.table),
  };
};
複製代碼

完成 ✅,除了 itemsbyId 外的其餘狀態的變化,再也不從新計算列表數據。

ezgif.com-gif-maker (1).gif

完整代碼,在這裏

React 性能優化

相關文章
相關標籤/搜索