原本想將有關於immutability-helper的博文放在一塊兒學React系列博文中,可是考慮到該插件不只僅在React中實用到,因此就單獨拿出來分兩期寫。git
immutability
意爲不變,不變性,永恆性
。至於該插件能作什麼,我想它的做者對它的標註已經很明確了mutate a copy of data without changing the original source
,意爲:在不改變原始來源的狀況下改變數據副本
。
同時在這裏筆者再推薦另外一個與之類似的插件,那就是Facebook出品的immutable-js (咱們能夠認爲immutability-helper是immutable-js的終極簡化版)。臉書對immutable-js的標註也很明確:Immutable persistent data collections for Javascript which increase efficiency and simplicity
,意爲:使得Javascript中的持久數據集合保持不可變,從而提升效率和簡單性
。github
仔細閱讀他們對兩個插件格子的描述,咱們能獲得一個關鍵字,那就是不可變
。爲何要不可變?在這裏筆者以爲從React角度來解釋可能會更加清晰。咱們都知道React組件自己會包含N個狀態,並且這些狀態會經過setState方法去更新,也就是說React組件的狀態是可變的,實際上本就是這麼一回事。可是做爲React的初學者可能會誤解狀態是可變的
這句話。怎麼說?由於對於React組件而言,狀態的變化是State的變化而不是State值的變化
,什麼?看不懂?那咱們繼續往下說。假如咱們初始化了這樣一個狀態:npm
this.state={ name:'xiaoming', hobbies:['qq','wx'] }
如今纔是解釋什麼叫State的變化而不是State值的變化
。假如咱們先這樣作:數組
this.setState({ name:'hanmeimei' })
通過這個操做之後,name
對應的DOM節點會獲得更新,而若是咱們這樣作:網絡
function changeHobby() { let hobbies = this.state.hobbies; hobbies.push('Coding'); this.setState({ hobbies: hobbies }) }
運行了這個方法之後會發生什麼?Nothing Happened, But why?
這個問題也是常常發生在React初學者身上,由於React在檢測狀態變化時候是一個淺檢測,換句話說就是隻關注name或者hobbies這兩個key對應的value有沒有變化,有變化就從新Render不然不作任何動做。那如何去檢測value有沒有變化,其實就是根據變量的地址。咱們都知道變量在內存中都有惟一的地址。拿剛剛的兩個例子來講,爲何更新了name和更新了hobbies這兩個狀態會有兩個不一樣的結果?那是由於在JavaScritp中,string就是被設計成不可變,而數組則可變。換句話說咱們沒法在保證字符串內存地址不變的狀況下改變字符串但卻能夠保證數組在內存地址不變的狀況下增長或者刪除其中的某一個元素。因此這就是爲何咱們更新hobbies這個狀態的狀況組件並無刷新的緣由,咱們用例子來證實下數組這一特性:app
let hobbies = ['qq', 'wx']; let hobbies_2 = hobbies; hobbies_2.push('Coding'); console.log(hobbies === hobbies_2);
能夠看出這兩個變量其實是同一個。
再舉個例子而且是在平常開發中確定會遇到過的。假如如今要將後臺的傳來的數據渲染成一個列表,數據以下:ide
[ {'id':0,name:'xiaoming'}, {'id':1,name:'lilei'}, {'id':2,name:'hanmeimei'}, ]
這是一個很簡單的操做。而後咱們作了一個更新的功能,當咱們將id爲1的那條的姓名改爲'Miss Li',那該怎麼辦才能保證在setState後讓該列表組件從新渲染?估計你們內心馬上有兩個方案,一個是從新獲取後臺數據而後從新渲染,另外一個是從新拷貝一份數據而後更改相應的地方後從新set給某個State完成從新渲染。可是咱們能夠看出,這兩個操做最終的效果仍然是建立另外一個與初始數組在內容上徹底相同的數組
,雖然相對繁瑣可是着實有效。不過這兩個方案各有缺點,好比第一個方案須要額外增長一次網絡請求,第二個是萬一數據量過於龐大就會形成內存的浪費。
既然這樣有什麼更好的解決方案呢?到這裏我相信你們就明白了該篇博客的用意了,由於這個immutability-helper在筆者近期項目中使用最頻繁,因此以爲有必要拿出來講一說。ui
使用方法很簡單,首先是安裝依賴this
npm install immutability-helper --save
而後是在有須要的地方引入便可spa
import update from 'immutability-helper';
這樣咱們可使用update這個方法作咱們想作的事情了。
那就有人問了,爲何它就能完美的解決問題呢?其實很簡單,就那剛剛的列表數據來講,immutability-helper會輸出一個全新的數組對象而且只會更新跟id=1有關的那條數據,剩下的經過
地址引用
的方式引入到新數組中,這樣會最大限度的使用數據和內存。
immutability-helper的工做方式也很簡單,經過在update方法在使用指令
就能夠實現對數據的修改。
immutability-helper的指令組成也很簡單:$+關鍵字
。並且這個關鍵字咱們仍然很熟悉,接着往下看。它支持的指令一共有:
這些指令關鍵字是否是很熟悉?由於咱們在平常開發中常用到這些。下面就開始一個一個介紹。
push,顧名思義和數組有關,其實就是向源數組中增長一個元素而且輸出一個內容想通了的新數組。看例子:
var update = require("immutability-helper"); const state1 = ["x"]; const state2 = update(state1, { $push: ["y"] }); // ['x', 'y'] console.log(state1, state2); console.log(`state1===state2: ${state1===state2}`);
輸出結果:
同時也來熟悉下update方法的使用:update方法接受兩個參數,第一個是源數據,這個很好理解;第二個是操做線
(筆者YY出來的),用來描述咱們如何去操做源數據,key是指令,value是指令所須要的數據
。
unshift的做用就是向源數組的開頭批量添加元素
var update = require("immutability-helper"); const state1 = ["x"]; const state2 = update(state1, { $unshift: ["y","Z"] }); console.log(state1, state2);
輸出結果:
splice的做用就是向源數組中添加/刪除元素。參數接受多個數組,每一個數組爲一組操做。每一組與實際splice方法的參數相同。
var update = require("immutability-helper"); const state1 = [0, 1, 2, 4]; const state2 = update(state1, { $splice: [ [3, 1, 3, 4, 5, 6, 7] ] }); console.log(state1, state2);
輸出結果:
set指令被用來改爲字面量對象中的某個key的值。
var update = require("immutability-helper"); const data = { 'id': 0, name: 'xiaoming' }; const data2 = update(data, { name: { $set: 'Miss Li' } }); console.log(data, data2);
輸出結果:
toggle意爲切換,該方法是用來對Boolean對象進行切換,好比True切換爲False
var update = require("immutability-helper"); const data = [true, false]; const data2 = update(data, { $toggle: [0] }); console.log(data, data2);
輸出結果:
使用方法如上,針對數組中的第1個Boolean值作切換處理。
本篇先介紹這5個指令方法。剩下的5個咱們下一篇繼續。
接下來咱們嘗試從上面這5個指令中找出相應指令去解決咱們前面提到的表格數據的問題:
首先是表格的數據:
[ {'id':0,name:'xiaoming'}, {'id':1,name:'lilei'}, {'id':2,name:'hanmeimei'}, ]
能夠分析出咱們須要對兩中對象進行處理,一個是數組,一個是字面量對象。
而後假如咱們修改了id=1的那條記錄的‘name’屬性,‘name=張偉’,該怎麼作?
首先第一步咱們要找到id=1的那條記錄的index,這裏是1;而後是肯定須要更改的字段和更改後的值。
var update = require("immutability-helper"); const data = [ { 'id': 0, name: 'xiaoming' }, { 'id': 1, name: 'lilei' }, { 'id': 2, name: 'hanmeimei' }, ] const data2 = update(data, { 1: { name: { $set: '張偉' } } }); console.log(data[1]['name'], data2[1]['name']); console.log(`data===data2: ${data===data2}`);
輸出結果:
這樣就實現了在源數據的基礎上更改了值而且輸出一個與之地址徹底不一樣數組。