React優化-記憶化技術-使用閉包提高你的React性能

爲何要使用記憶性技術?

使用React開發的時候,咱們請求服務器拿回來一個複雜的數據,咱們在render裏去處理這個數據,可是state和props頻繁修改會觸發render,每次觸發render,數據都要去處理一次,每次處理都是對性能的損耗javascript

舉個例子:把大於18歲的人列出來java

class Example extends Component {
    ...
    render() {
        const { dataList } = this.props;
        const newDataList = dataList.filter((item) => item.age > 18);
        return (
            <div>
                {newDataList.map((item, i) =>
                    <p key={i}>{item.name}:{item.age}歲</p>
                )}
            </div>
        )
    }
    ...
}

從例子中咱們看到render中咱們處理數據,可是每次state和props的修改都會觸發render,都會去處理數據dataList,生成新的數據newDataList,每次處理都是對性能的損耗!git

什麼叫記憶性技術?

每次調用函數把你的傳參和結果記錄下來,遇到相同的傳參,就直接返回記錄緩存的結果,不用再去調用函數處理數據!github

memoize-one官方案例

import memoizeOne from 'memoize-one';

const add = (a, b) => a + b;
const memoizedAdd = memoizeOne(add);

memoizedAdd(1, 2); // 3

memoizedAdd(1, 2); // 3
// Add 函數並無執行: 前一次執行的結果被返回

memoizedAdd(2, 3); // 5
// Add 函數再次被調用,返回一個新的結果

memoizedAdd(2, 3); // 5
// Add 函數並無執行: 前一次執行的結果被返回

memoizedAdd(1, 2); // 3
// Add 函數再次被調用,返回一個新的結果

咱們能夠發現連續兩次相同傳參,第二次會直接返回上次的結果,每次傳參不同,就直接調用函數返回新的結果,會丟失以前的記錄,並非徹底記憶,這也是個不足點!緩存

在React中使用memoize-one

根據上的例子,咱們對那個例子進行修改,使用memoize-one提高React的性能服務器

import memoize from "memoize-one";

class Example extends Component {
    ...
    filter = memoize((dataList, age) => dataList.filter((item) => item.age > age))
    render() {
        const { dataList } = this.props;
        const newDataList = this.filter(dataList, 18)
        return (
            <div>
                ...
                {newDataList.map((item, i) =>
                    <p key={i}>{item.name}:{item.age}歲</p>
                )}
                ...
            </div>
        )
    }
    ...
}

memoize-one源碼解析

memoize-one是採用閉包來緩存數據的閉包

type EqualityFn = (a: mixed, b: mixed) => boolean;

const simpleIsEqual: EqualityFn = (a: mixed, b: mixed): boolean => a === b;

export default function <ResultFn: (...Array<any>) => mixed>(resultFn: ResultFn, isEqual?: EqualityFn = simpleIsEqual): ResultFn {
  let lastThis: mixed; // 用來緩存上一次result函數對象
  let lastArgs: Array<mixed> = []; // 用來緩存上一次的傳參
  let lastResult: mixed; // 用來緩存上一次的結果
  let calledOnce: boolean = false; // 是否以前調用過
  // 判斷兩次調用的時候的參數是否相等
  // 這裏的 `isEqual` 是一個抽象函數,用來判斷兩個值是否相等
  const isNewArgEqualToLast = (newArg: mixed, index: number): boolean => isEqual(newArg, lastArgs[index]);

  const result = function (...newArgs: Array<mixed>) {
    if (calledOnce &&
      lastThis === this &&
      newArgs.length === lastArgs.length &&
      newArgs.every(isNewArgEqualToLast)) {
      // 返回以前的結果
      return lastResult;
    }

    calledOnce = true; // 標記已經調用過
    lastThis = this; // 從新緩存result對象
    lastArgs = newArgs; // 從新緩存參數
    lastResult = resultFn.apply(this, newArgs); // 從新緩存結果
    return lastResult; // 返回新的結果
  };

  // 返回閉包函數
  return (result: any);
}

關於isEqual函數(memoize-one推薦使用loadsh.isEqual)

通常兩個對象比較是否相等,咱們不能用===或者==來處理,memoize-one容許用戶自定義傳入判斷是否相等的函數,好比咱們可使用lodash的isEqual來判斷兩次參數是否相等app

import memoizeOne from 'memoize-one';
import deepEqual from 'lodash.isEqual';

const identity = x => x;

const defaultMemoization = memoizeOne(identity);
const customMemoization = memoizeOne(identity, deepEqual);

const result1 = defaultMemoization({foo: 'bar'});
const result2 = defaultMemoization({foo: 'bar'});

result1 === result2 // false - 索引不一樣

const result3 = customMemoization({foo: 'bar'});
const result4 = customMemoization({foo: 'bar'});

result3 === result4 // true - 參數經過lodash.isEqual判斷是相等的

參考

https://github.com/alexreardo...ide

相關文章
相關標籤/搜索