React性能優化的中流砥柱——Immutable數據流

編者薦語:

本文幫助你們從0瞭解什麼是immutable,以及它有什麼做用,是在什麼背景下應運而生的。前端

最重要的是它是咱們平常開發中經常使用的技術之一,與React配合使用的immutable能給咱們開發者帶來多大的便利,本文會依次講到。算法

immutable出現的背景

在咱們的平常開發中,我相信你們都遇到過一種困擾,那就是如何將後端返回的數據,深拷貝一份,再供咱們本身使用呢?後端

若是用過React框架開發過項目的小夥伴,也必定記得Redux中的reducer是基於純函數設計的,要求返回的狀態數據(對象或數組),須要先深拷貝一份(目的是:防止影響老狀態),再根據本身的開發需求對其拷貝後的值操做?數組

來看下面這樣一個例子:性能優化

let list = [
  { name'house'age18}
]

let newList = [...list];
newList[0].name = "xiaoming";

console.log(list[0].name); // "xiaoming"
複製代碼

顯然上面例子中的原數組list,被咱們不輕易間串改了,其實緣由很簡單,就是由於ES6中的展開運算符[...list]是一個淺拷貝,淺拷貝的意思就是隻複製對象或數組的第一級內容。markdown

在上面中,能夠發現通過展開運算符的淺拷貝,只複製了其內層引用類型的地址,當經過索引找到其引用地址,並改變它的時候,改的就是list原數組自己。數據結構

固然,有的小夥伴能夠想到:當訪問到對象的屬性值的時候,將屬性值再進行遞歸對比,這樣就達到了深層對比的效果,可是想一想一種極端的狀況,就是在屬性有一萬條的時候,只有最後一個屬性發生了變化,那咱們就不得已將一萬條屬性都遍歷。這是很是浪費性能的。框架

回到問題的本質,不管是直接用淺層比對,仍是進行深層比對,咱們最終想知道的就是原對象裏的屬性有無改變。函數

在這樣的條件下,immutable 數據應運而生。性能

什麼是immutable數據?

immutable數據一種利用結構共享造成的持久化數據結構,一旦有部分被修改,那麼將會返回一個全新的對象,而且原來相同的節點會直接共享。

每次修改一個 immutable 對象時都會建立一個新的不可變的對象,在新對象上操做並 不會影響到原對象的數據。

具體點來講,immutable 對象數據內部採用是多叉樹的結構,凡有節點被改變,那麼它和與它相關的全部上級節點更新

用一張動圖來模擬一下這個過程:

immutable修改節點更新引用過程
immutable修改節點更新引用過程

是吧!只更新了父節點,比直接比對全部的屬性簡直強太多,而且更新後返回了一個全新的引用,即便是淺比對也能感知到數據的改變。

所以,採用 immutable 既可以最大效率地更新數據結構,又可以和現有的 React中的 PureComponent (memo) 順利對接,感知到狀態的變化,是提升 React 渲染性能的極佳方案

不過,immutable 也有一些被開發者吐槽的點,首先是 immutable 對象JS 對象要注意轉換,不能混用,這個你們注意適當的時候調用 toJS 或者 fromJS 便可,問題並不大。

immutable優化性能的方式

immutable實現的原理是:持久化數據結構,也就是使用舊數據建立新數據時,要保證舊數據同時可用且不變。同時爲了避免deepCopy 把全部節點都複製一遍帶來的性能損耗

immutable使用了結構共享,即若是對象樹中一個節點發生變化,只修改這個節點和受它影響的父節點,其它節點則進行共享。

immutable性能優化體如今哪裏

immutable優化性能
immutable優化性能

與React中的 PureComponent(memo) 相結合,咱們知道PureComponent可以在內部幫咱們比較新props舊props新state舊state,若是值相等或者對象含有的相同的屬性、且屬性值相等,便肯定shouldComponentUpdate返回true或者false,從而判斷是否再次渲染render函數。

看上述代碼,咱們能夠看出來,當代碼中使用immutable第三庫的時候,能夠精確地深拷貝 a 對象,改a對象中的select屬性賦值給b以後,並不會影響原對象a,而bselect屬性變爲了新值。

若是上述select屬性給一個組件用,由於其值被改變了,致使shouldComponentUpdate應該返回true,而filter屬性給另外一個組件用,經過判斷,並沒有變化,致使shouldComponentUpdate應該返回false,故此組件就避免了重複的diff算法對比,大大提升了React中的性能優化。

這麼好用的第三方庫,咱們來看一下它的基本用法:

immutable中經常使用類型

(1)Map() 包裹對象

const { Map } = require('immutable'); 
const map1 = Map({ a1b2c3 }); 
const map2 = map1.set('b'50);

console.log(map1.get('b')); // 2
console.log(map2.get('b')); // 50
複製代碼

(2)List() 包裹數組

const { List } = require('immutable');

const list1 = List([ 12 ]); 
const list2 = list1.push(345);  // [1,2,3,4,5]
const list3 = list2.unshift(0);    // [0,1,2,3,4,5]
const list4 = list1.concat(list2, list3); // [1,2,3,4,5,0,1,2,3,4,5]

//push, set, unshift or splice 均可以直接用,返回一個新的immutable對象
複製代碼

(3)merge() 鏈接對象 | concat() 鏈接數組

const { Map, List } = require('immutable');

const map1 = Map({ a1b2c3d4 });
const map2 = Map({ c10a20t30 });
const obj = { d100o200g300 };

const map3 = map1.merge(map2, obj);
// Map { a: 20, b: 2, c: 10, d: 100, t: 30, o: 200, g: 300 }

const list1 = List([ 123 ]);
const list2 = List([ 456 ]);

const list3 = list1.concat(list2, array);
// List [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
複製代碼

(4)toJS() 把immutable對象轉換爲js對象

const { Map, List } = require('immutable');

const deep = Map({ a1b2c: List([ 345 ]) });
console.log(deep.toObject());   // { a: 1, b: 2, c: List [ 3, 4, 5 ] }
console.log(deep.toArray());    // [ 1, 2, List [ 3, 4, 5 ] ]
console.log(deep.toJS());       // { a: 1, b: 2, c: [ 3, 4, 5 ] }
JSON.stringify(deep);           // '{"a":1,"b":2,"c":[3,4,5]}'
複製代碼

(5)fromJS() 包裹 js對象轉換爲immutable對象

const { fromJS } = require('immutable');

const nested = fromJS({ a: { b: { c: [ 345 ] } } });
// Map { a: Map { b: Map { c: List [ 3, 4, 5 ] } } }

const nested2 = nested.mergeDeep({ a: { b: { d6 } } });
// Map { a: Map { b: Map { c: List [ 3, 4, 5 ], d: 6 } } }

console.log(nested2.getIn([ 'a''b''d' ])); // 6
//若是取一級屬性 直接經過get方法,若是取多級屬性 getIn(["a","b","c"]])

// setIn 設置新的值
const nested3 = nested2.setIn([ 'a''b''d' ], "kerwin");
// Map { a: Map { b: Map { c: List [ 3, 4, 5 ], d: "kerwin" } } }

// updateIn 回調函數更新
const nested3 = nested2.updateIn([ 'a''b''d' ], value => value + 1);
console.log(nested3);
// Map { a: Map { b: Map { c: List [ 3, 4, 5 ], d: 7 } } }

const nested4 = nested3.updateIn([ 'a''b''c' ], list => list.push(6));
// Map { a: Map { b: Map { c: List [ 3, 4, 5, 6 ], d: 7 } } }
複製代碼

相對較全的immutable一些經常使用方法,都在這裏給你們總結了,你們在項目中常常用就能夠熟練掌握了。

immutable的通用性

一句話總結:immutable適用於任何框架開發,只要是能引入第三方庫的框架,均可以使用。

下面簡單列舉一下,我在React開發中reducer的簡單使用狀況:

immutable+Redux的開發方式

狀況1:未使用immutable

下面的代碼的狀況十分危險,不建議這樣用,由於一旦當newStateList中的類型較爲複雜(包含引用類型),且須要修改newStateList時,就會發生報錯,由於[...xxx, ...xxx]是淺拷貝,會影響原來的狀態。

狀況2:使用immutable

經過store中傳遞過來的老狀態prevState先轉化爲immutable對象,對深拷貝以後的對象,再進行修改等操做時,不會影響原狀態,最後再經過toJS()轉換爲js對象便可

本文總結

immutable並無深層比較,由於深層比較的開銷是很大的。

immutable數據調用set方法修改的時候僅僅修改本節點和它上面全部的相關上層節點,保證了不同的引用,也更新了數據。更重要的是,它避免了無關數據的比對,提升了性能。

看完三件事❤

若是你以爲這篇內容對你還蠻有幫助,我想邀請你幫我三個小忙:

  1. 點贊,轉發,有大家的『在看』,纔是我創造的動力。
  2. 關注公衆號 『前端時光屋』,不按期分享原創知識。
  3. 同時能夠期待後續文章ing🚀
相關文章
相關標籤/搜索