immutability-helper 學習筆記 -1

本來想將有關於immutability-helper的博文放在一起學React系列博文中,但是考慮到該插件不僅僅在React中實用到,所以就單獨拿出來分兩期寫。

發現問題

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中的持久數據集合保持不可變,從而提高效率和簡單性

仔細閱讀他們對兩個插件格子的描述,我們能得到一個關鍵字,那就是不可變。爲什麼要不可變?在這裏筆者覺得從React角度來解釋可能會更加清晰。我們都知道React組件本身會包含N個狀態,而且這些狀態會通過setState方法去更新,也就是說React組件的狀態是可變的,實際上本就是這麼一回事。但是作爲React的初學者可能會誤解狀態是可變的這句話。怎麼說?因爲對於React組件而言,狀態的變化是State的變化而不是State值的變化,什麼?看不懂?那我們繼續往下說。假如我們初始化了這樣一個狀態:

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這個狀態的情況組件並沒有刷新的原因,我們用例子來證明下數組這一特性:

let hobbies = ['qq', 'wx'];
let hobbies_2 = hobbies;
hobbies_2.push('Coding');

console.log(hobbies === hobbies_2);

可以看出這兩個變量實際上是同一個。
再舉個例子並且是在日常開發中肯定會遇到過的。假如現在要將後臺的傳來的數據渲染成一個列表,數據如下:

[
    {'id':0,name:'xiaoming'},
    {'id':1,name:'lilei'},
    {'id':2,name:'hanmeimei'},
]

這是一個很簡單的操作。然後我們做了一個更新的功能,當我們將id爲1的那條的姓名改成'Miss Li',那該怎麼辦才能保證在setState後讓該列表組件重新渲染?估計大家心裏立刻有兩個方案,一個是重新獲取後臺數據然後重新渲染,另一個是重新拷貝一份數據然後更改相應的地方後重新set給某個State完成重新渲染。但是我們可以看出,這兩個操作最終的效果仍然是創建另一個與初始數組在內容上完全相同的數組,雖然相對繁瑣但是着實有效。不過這兩個方案各有缺點,比如第一個方案需要額外增加一次網絡請求,第二個是萬一數據量過於龐大就會造成內存的浪費。
既然這樣有什麼更好的解決方案呢?到這裏我相信大家就明白了該篇博客的用意了,因爲這個immutability-helper在筆者近期項目中使用最頻繁,所以覺得有必要拿出來說一說。

immutability-helper使用方法

使用方法很簡單,首先是安裝依賴

npm install immutability-helper --save

然後是在有需要的地方引入即可

import update from 'immutability-helper';

這樣我們可以使用update這個方法做我們想做的事情了。

那就有人問了,爲什麼它就能完美的解決問題呢?其實很簡單,就那剛剛的列表數據來說,immutability-helper會輸出一個全新的數組對象並且只會更新跟id=1有關的那條數據,剩下的通過地址引用的方式引入到新數組中,這樣會最大限度的使用數據和內存。

immutability-helper工作方式

immutability-helper的工作方式也很簡單,通過在update方法在使用指令就可以實現對數據的修改。
immutability-helper的指令組成也很簡單:$+關鍵字。而且這個關鍵字我們仍然很熟悉,接着往下看。它支持的指令一共有:

  • {$push: array} push() all the items in array on the target.
  • {$unshift: array} unshift() all the items in array on the target.
  • {$splice: array of arrays} for each item in arrays call splice() on the target with the parameters provided by the item. Note: The items in the array are applied sequentially, so the order matters. The indices of the target may change during the operation.
  • {$set: any} replace the target entirely.
  • {$toggle: array of strings} toggles a list of boolean fields from the target object.
  • {$unset: array of strings} remove the list of keys in array from the target object.
  • {$merge: object} merge the keys of object with the target.
  • {$apply: function} passes in the current value to the function and updates it with the new returned value.
  • {$add: array of objects} add a value to a Map or Set. When adding to a Set you pass in an array of objects to add, when adding to a Map, you pass in [key, value] arrays like so: update(myMap, {$add: [['foo', 'bar'], ['baz', 'boo']]})
  • {$remove: array of strings} remove the list of keys in array from a Map or Set.

這些指令關鍵字是不是很熟悉?因爲我們在日常開發中經常使用到這些。下面就開始一個一個介紹。

指令使用

$push

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}`);

輸出結果:

clipboard.png

同時也來熟悉下update方法的使用:update方法接受兩個參數,第一個是源數據,這個很好理解;第二個是操作線(筆者YY出來的),用來描述我們如何去操作源數據,key是指令,value是指令所需要的數據

$unshift

unshift的作用就是向源數組的開頭批量添加元素

var update = require("immutability-helper");
const state1 = ["x"];
const state2 = update(state1, { $unshift: ["y","Z"] });

console.log(state1, state2);

輸出結果:

clipboard.png

$splice

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);

輸出結果:
clipboard.png

$set

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);

輸出結果:

clipboard.png

$toggle

toggle意爲切換,該方法是用來對Boolean對象進行切換,比如True切換爲False

var update = require("immutability-helper");
const data = [true, false];
const data2 = update(data, { $toggle: [0] });
console.log(data, data2);

輸出結果:
clipboard.png
使用方法如上,針對數組中的第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}`);

輸出結果:

clipboard.png

這樣就實現了在源數據的基礎上更改了值並且輸出一個與之地址完全不同數組。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章