国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

React 的性能優化(一)當 PureComponent 遇上 ImmutableJS

plus2047 / 3329人閱讀

摘要:四是在年出的持久性數據結構的庫,持久性指的是數據一旦創建,就不能再被更改,任何修改或添加刪除操作都會返回一個新的對象。避免大量使用操作,這樣會浪費性能。盡量將設計成扁平狀的。

一、痛點

在我們的印象中,React 好像就意味著組件化、高性能,我們永遠只需要關心數據整體,兩次數據之間的 UI 如何變化,則完全交給 React Virtual DomDiff 算法 去做。以至于我們很隨意的去操縱數據,基本優化shouldComponentUpdate 也懶得去寫,畢竟不寫也能正確渲染。但隨著應用體積越來越大,會發現頁面好像有點變慢了,特別是組件嵌套比較多,數據結構比較復雜的情況下,隨便改變一個表單項,或者對列表做一個篩選都要耗時 100ms 以上,這個時候我們就需要優化了!當然如果沒有遇到性能瓶頸,完全不用擔心,過早優化是邪惡的。這里我們總結一個很簡單的方案來讓 React 應用性能發揮到極致。在下面一部分,我們先回顧一下一些背景知識,包括:JavaScript 變量類型和 React 渲染機制,如果你是老鳥可以直接跳過。

二、一些背景知識的回顧 1. 變量類型

JavaScript的變量類型有兩類:

基本類型:6 種基本數據類型, Undefined 、 Null 、 Boolean 、 NumberStringSymbol

引用類型:統稱為 Object 類型,細分為:Object 類型、 Array 類型、 Date 類型、 RegExp 類型、 Function 類型等。

舉個例子:

let p1 = { name: "neo" };
let p2 = p1;
p2.name = "dave";
console.log(p1.name); // dave

在引用類型里,聲明一個 p1 的對象,把 p1 賦值給 p2 ,此時賦的其實是該對象的在堆中的地址,而不是堆中的數據,也就是兩個變量指向的是同一個存儲空間,后面 p2.name 改變后,也就影響到了 p1。雖然這樣做可以節約內存,但當應用復雜后,就需要很小心的操作數據了,因為一不注意修改一個變量的值可能就影響到了另外一個變量。如果我們想要讓他們不互相影響,就需要拷貝出一份一模一樣的數據,拷貝又分淺拷貝與深拷貝,淺拷貝只會拷貝第一層的數據,深拷貝則會遞歸所有層級都拷貝一份,比較消耗性能。

2. React

React 中,每次 setState , Virtual DOM 會計算出前后兩次虛擬 DOM 對象的區別,再去修改真實需要修改的 DOM 。由于 js 計算速度很快,而操作真實 DOM 相對比較慢,Virtual DOM 避免了沒必要的真實 DOM 操作,所以 React 性能很好。但隨著應用復雜度的提升, DOM 樹越來越復雜,大量的對比操作也會影響性能。比如一個 Table 組件,修改其中一行 Tr 組件的某一個字段, setState 后,其他所有行 Tr 組件也都會執行一次 render 函數,這其實是不必要的。我們可以通過 shouldComponentUpdate 函數決定是否更新組件。大部分時候我們是可以知道哪些組件是不會變的,根本就沒必要去計算那一部分虛擬 DOM。

三、 PureComponent

React15.3 中新加了一個類PureComponent,前身是 PureRenderMixin ,和 Component 基本一樣,只不過會在 render 之前幫組件自動執行一次shallowEqual(淺比較),來決定是否更新組件,淺比較類似于淺復制,只會比較第一層。使用 PureComponent 相當于省去了寫 shouldComponentUpdate 函數,當組件更新時,如果組件的 propsstate

引用和第一層數據都沒發生改變, render 方法就不會觸發,這是我們需要達到的效果。

雖然第一層數據沒變,但引用變了,就會造成虛擬 DOM 計算的浪費。

第一層數據改變,但引用沒變,會造成不渲染,所以需要很小心的操作數據。

四、 Immutable.js

Immutable.js是 Facebook2014 年出的持久性數據結構的庫,持久性指的是數據一旦創建,就不能再被更改,任何修改或添加刪除操作都會返回一個新的 Immutable 對象??梢宰屛覀兏菀椎娜ヌ幚砭彺?、回退、數據變化檢測等問題,簡化開發。并且提供了大量的類似原生 JS 的方法,還有 Lazy Operation 的特性,完全的函數式編程。

import { Map } from "immutable";
const map1 = Map({ a: { aa: 1 }, b: 2, c: 3 });
const map2 = map1.set("b", 50);
map1 !== map2; // true
map1.get("b"); // 2
map2.get("b"); // 50
map1.get("a") === map2.get("a"); // true

可以看到,修改 map1 的屬性返回 map2,他們并不是指向同一存儲空間,map1 聲明了只有,所有的操作都不會改變它。

ImmutableJS 提供了大量的方法去更新、刪除、添加數據,極大的方便了我們操縱數據。除此之外,還提供了原生類型與 ImmutableJS 類型判斷與轉換方法:

import { fromJS, isImmutable } from "immutable";
const obj = fromJS({
  a: "test",
  b: [1, 2, 4]
}); // 支持混合類型
isImmutable(obj); // true
obj.size(); // 2
const obj1 = obj.toJS(); // 轉換成原生 `js` 類型

ImmutableJS 最大的兩個特性就是: immutable data structures(持久性數據結構)與 structural sharing(結構共享),持久性數據結構保證數據一旦創建就不能修改,使用舊數據創建新數據時,舊數據也不會改變,不會像原生 js 那樣新數據的操作會影響舊數據。而結構共享是指沒有改變的數據共用一個引用,這樣既減少了深拷貝的性能消耗,也減少了內存。比如下圖:

左邊是舊值,右邊是新值,我需要改變左邊紅色節點的值,生成的新值改變了紅色節點到根節點路徑之間的所有節點,也就是所有青色節點的值,舊值沒有任何改變,其他使用它的地方并不會受影響,而超過一大半的藍色節點還是和舊值共享的。在 ImmutableJS 內部,構造了一種特殊的數據結構,把原生的值結合一系列的私有屬性,創建成 ImmutableJS 類型,每次改變值,先會通過私有屬性的輔助檢測,然后改變對應的需要改變的私有屬性和真實值,最后生成一個新的值,中間會有很多的優化,所以性能會很高。

五、 案例

首先我們看看只使用 React 的情況下,應用性能為什么會被浪費,代碼地址:https://github.com/wulv/fe-ex... ,這個案例使用 create-react-app,檢測工具使用 chrome 插件:React Perf。執行

git clone https://github.com/wulv/fe-example.git
cd fe-example/react-table
yarn
yarn start

可以打開頁面,開始記錄,然后隨便對一列數據進行修改,結束記錄,可以看到我們僅修改了一行數據,但在 Print Wasted 那一項里,渲染 Tr 組件浪費了5次:

無論是添加,刪除操作,都會浪費 n-1render ,因為 App 組件的整個 state 改變了,所有的組件都會重新渲染一次,最后對比出需要真實 DOM 的操作。我們把 Table 組件和 Tr 繼承的 Component 改成 PureComponent ,那么, Tr 組件每次更新都會進行一次 shallowEqual 比較,在記錄一次,會發現修改操作沒有了浪費,然而這個時候添加和刪除操作卻無效了,分析一下添加的操作是:

 add = () => {
    const  { data } = this.state;
    data.push(dataGenerate())
    this.setState({
      data
    })
  }

data.push 并沒有改變 data 的引用,所以 PureComponentshallowEqual 直接返回了 true ,不去 render 了。這并不是我們想要的,所以如果使用 Component 必定帶來性能浪費,使用 PureComponent 又必須保證組件需要更新時,propsstate 返回一個新引用,否則不會更新 UI

這個時候, ImmutableJS 就可以顯示出它的威力了,因為它可以保證每次修改返回一個新的 Object,我們看看修改后的例子:代碼地址:https://github.com/wulv/fe-ex... ,執行上面例子同樣的操作,可以看到:

添加,刪除,修改操作,沒有一次浪費。沒有浪費的原因是所有的子組件都使用了 PureComponent, ImmutableJS 保證修改操作返回一個新引用,并且只修改需要修改的節點(PureComponent 可以渲染出新的改動),其他的節點引用保持不變(PureComponent 直接不渲染)??梢钥闯觯?PureComponentImmutableJS 簡直是天生一對啊,如果結合 redux ,那就更加完美了。因為 reduxreducer 必須每次返回一個新的引用,有時候我們必須使用 clone 或者 assign 等操作來確保返回新引用,使用 ImmutanleJS 天然保證了這一點,根本就不需要 lodash 等函數庫了,比如我使用redux + immutable + react-router + express 寫了一個稍微復雜點的例子:https://github.com/wulv/fe-ex...,可以看到 pageIndexstore 的狀態是:

{
  loading: false,
  tableData: [{
    "name": "gyu3w0oa5zggkanciclhm2t9",
    "age": 64,
    "height": 121,
    "width": 71,
    "hobby": {
      "movie": {
        "name": "zrah6zrvm9e512qt4typhkt9",
        "director": "t1c69z1vd4em1lh747dp9zfr"
      }
    }
  }],
  totle: 0
}

如果我需要快速修改 width 的值為90,比較一下使用深拷貝、 Object.assignImmutableJS 三種方式的區別:

// payload = { name: "gyu3w0oa5zggkanciclhm2t9", width: 90 }
// 1. 使用深拷貝
 updateWidth(state, payload) {
    const newState = deepClone(state);
    return newState.tableData.map(item => {
      if (tem.name === payload.name) {
        item.width = payload.width;
      }
      return item;
    });
  }
// 2. 使用Object.assign
 updateWidth(state, payload) {
    return Object.assign({}, state, {
      tableData: state.state.map(item => {
        if (item.name === payload.name) {
          return Object.assign({}, item, { width: payload.width });
        }
        return item;
      })
    })
  }
// 3. 使用ImmutableJS
 updateWidth(state, payload) {
  return state.update("tableData", list => list.update(
      list.findIndex((item) => item.get("name") === payload.name),
    item => item.set("width", payload.width)));
  }

使用深拷貝是一個昂貴的操作,而且引用都改變了,必然造成 re-render, 而 Object.assign 會淺復制第一層,雖然不會造成 re-render,但淺復制把其他的屬性也都復制了一次,在這里也是很沒有必要的,只有使用 ImmutableJS 完美的完成了修改,并且代碼也最少。

六、 優勢與不足

可以看出, ImmutableJS 結合 PureComponent 可以很大程度的減少應用 re-render 的次數,可以大量的提高性能。但還是有一些不足的地方:

獲取組件屬性必須用 getgetIn 操作(除了 Record 類型),這樣和原生的.操作比起來就麻煩多了,如果組件之前已經寫好了,還需要大量的修改。

ImmutableJS 庫體積比較大,大概56k,開啟 gzip 壓縮后16k。

學習成本。

難以調試,在 redux-logger 里面需要在 stateTransformer 配置里執行 state.toJS()。

七、 最佳實踐

其實,重要的是編程者需要有性能優化的意識,熟悉 js 引用類型的特性,了解事情的本質比會使用某個框架或庫更加重要。用其他的方法也是完全可以達到 ImmutableJS 的效果,比如添加數據可以使用解構操作符的方式:

 add = () => {
    const  { data } = this.state;
    this.setState({
      data: [...data, dataGenerate()]
    })
  }

只不過如果數據嵌套比較深,寫起來還是比較麻煩。以下有一些小技巧:

還有兩個輕量庫可以實現不可變數據結構:seamless-immutable或者immutability-helper,只不過原理完全不一樣,效率也沒那么高。

避免大量使用 toJS 操作,這樣會浪費性能。

不要將簡單的 JavaScript 對象與 Immutable.JS 混合

結合 redux 的時候,要使用import { combineReducers } from "redux-immutablejs";,因為 reduxcombineReducers 期望 state 是一個純凈的 js 對象。

盡量將 state 設計成扁平狀的。

展示組件不要使用 Immutable 數據結構。

不要在 render 函數里一個 PureComponent 組件的 props 使用 bind(this) 或者 style={ { width: "100px" } },因為 shallowEqual 一定會對比不通過。

八、 參考鏈接

Immutable.js, persistent data structures and structural sharing

immutable.js is much faster than native javascript

Immutable 詳解及 React 中實踐

本文首發于有贊技術博客。

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/88786.html

相關文章

  • 【騰訊Bugly干貨分享】React 移動 web 極致優化

    摘要:數據管理及性能優化統一管理數據這一部份算是重頭戲吧。重復渲染導致卡頓這套的東西在家校群頁面上用得很歡樂,以至于不用怎么寫都沒遇到過什么性能問題。但放到移動端上,我們在列表頁重構的時候就馬上遇到卡頓的問題了。列表頁目前的處理辦法是將值換成。 本文來自于騰訊bugly開發者社區,非經作者同意,請勿轉載,原文地址:http://dev.qq.com/topic/57908... 最近一個季度...

    suosuopuo 評論0 收藏0
  • 2017-09-29 前端日報

    摘要:前端日報精選被譽為神器的我是怎樣讓網站用上的性能優化一當遇上異步隊列實現及拓展技術大會震撼登陸,明星團隊講師傾城而出中文秋招前端工程師百度阿里網易騰訊全面經之自定義頭像功能實現掘金中支持簡書發布了字符串轉數字陷阱示例眾成翻譯性 2017-09-29 前端日報 精選 被譽為神器的requestAnimationFrame我是怎樣讓網站用上HTML5 ManifestReact 的性能優化...

    coordinate35 評論0 收藏0
  • 使用React.memo()來優化函數組件性能

    摘要:函數組件上面我們探討了如何使用和的方法優化類組件的性能。它的作用和類似,是用來控制函數組件的重新渲染的。其實就是函數組件的。 原文鏈接: Improving Performance in React Functional Component using React.memo 原文作者: Chidume Nnamdi 譯者: 進擊的大蔥 推薦理由: 本文講述了開發React應用時如...

    BetaRabbit 評論0 收藏0
  • 精讀《React 多態性》

    摘要:引言本周精讀的文章是,看看作者是如何解釋這個多態性含義的。讀完文章才發現,文章標題改為的多態性更妥當,因為整篇文章都在說,而使用場景不局限于。更多討論討論地址是精讀的多態性如果你想參與討論,請點擊這里,每周都有新的主題,周末或周一發布。 1 引言 本周精讀的文章是:surprising-polymorphism-in-react-applications,看看作者是如何解釋這個多態性含...

    tabalt 評論0 收藏0

發表評論

0條評論

最新活動
閱讀需要支付1元查看
<