Immutable.js學(xué)習(xí)筆記

JavaScript數(shù)據(jù)修改的問題

看一段大家熟悉的代碼

const state = {
  str: '教育',
  obj: {
    y: 1
  },
  arr: [1, 2, 3]
}
const newState = state
?
console.log(newState === state) // true

由于js的對象和數(shù)組都是引用類型。所以newState的state實際上是指向于同一塊內(nèi)存地址的, 所以結(jié)果是newState和state是相等的。

嘗試修改一下數(shù)據(jù)

const state = {
  str: '教育',
  obj: {
    y: 1
  },
  arr: [1, 2, 3]
}
const newState = state
?
newState.str = '教育H5學(xué)院'
?
console.log(state.str, newState.str)

可以看到,newState的修改也會引起state的修改。要解決這個問題,js中提供了另一種修改數(shù)據(jù)的方式,要修改一個數(shù)據(jù)之前先制作一份數(shù)據(jù)的拷貝,像這樣

const state = {
  str: '教育',
  obj: {
    y: 1
  },
  arr: [1, 2, 3]
}
const newState = Object.assign({}, state)
?
newState.str = '教育H5學(xué)院'
?
console.log(state.str, newState.str)

我們可以使用很多方式在js中復(fù)制數(shù)據(jù),比如, Object.assign, Object.freeze, slice, concat, map, filter, reduce等方式進行復(fù)制,但這些都是淺拷貝,就是只拷貝第一層數(shù)據(jù),更深層的數(shù)據(jù)還是同一個引用,比如:

const state = {
  str: '教育',
  obj: {
    y: 1
  },
  arr: [1, 2, 3]
}
const newState = Object.assign({}, state)
?
newState.obj.y = 2
newState.arr.push(4)
?
console.log(state, newState)

可以看到,當(dāng)在更改newState更深層次的數(shù)據(jù)的時候,還是會影響到state的值。如果要深層復(fù)制,就得一層一層的做遞歸拷貝,這是一個復(fù)雜的問題。雖然有些第三方的庫已經(jīng)幫我們做好了,比如lodashcloneDeep方法。深拷貝是非常消耗性能的。

import { cloneDeep } from 'lodash'
?
const state = {
  str: '教育',
  obj: {
    y: 1
  },
  arr: [1, 2, 3]
}
const newState = cloneDeep(state)
?
newState.obj.y = 2
newState.arr.push(4)
?
console.log(state, newState)

什么是不可變數(shù)據(jù)

不可變數(shù)據(jù) (Immutable Data )就是一旦創(chuàng)建,就不能再被更改的數(shù)據(jù)。對 Immutable 對象的任何修改或添加刪除操作都會返回一個新的 Immutable 對象。Immutable 實現(xiàn)的原理是持久化數(shù)據(jù)結(jié)構(gòu)( Persistent Data Structure),也就是使用舊數(shù)據(jù)創(chuàng)建新數(shù)據(jù)時,要保證舊數(shù)據(jù)同時可用且不變。同時為了避免 deepCopy 把所有節(jié)點都復(fù)制一遍帶來的s性能損耗,Immutable 使用了 結(jié)構(gòu)共享(Structural Sharing),即如果對象樹中一個節(jié)點發(fā)生變化,只修改這個節(jié)點和受它影響的父節(jié)點,其它節(jié)點則進行共享。

immutable.js的優(yōu)缺點

優(yōu)點:

  • 降低mutable帶來的復(fù)雜度
  • 節(jié)省內(nèi)存
  • 歷史追溯性(時間旅行):時間旅行指的是,每時每刻的值都被保留了,想回退到哪一步只要簡單的將數(shù)據(jù)取出就行,想一下如果現(xiàn)在頁面有個撤銷的操作,撤銷前的數(shù)據(jù)被保留了,只需要取出就行,這個特性在redux或者flux中特別有用
  • 擁抱函數(shù)式編程:immutable本來就是函數(shù)式編程的概念,純函數(shù)式編程的特點就是,只要輸入一致,輸出必然一致,相比于面向?qū)ο螅@樣開發(fā)組件和調(diào)試更方便。推薦一本函數(shù)式編程的在線免費書《JS 函數(shù)式編程指南》, 此書可以推薦給學(xué)生做為課外補充閱讀。

缺點:

  • 需要重新學(xué)習(xí)api
  • 資源包大小增加(源碼5000行左右)
  • 容易與原生對象混淆:由于api與原生不同,混用的話容易出錯。

使用Immutable.js

參考官網(wǎng)重點講解數(shù)據(jù)不可變數(shù)據(jù)的創(chuàng)建、更新及比較方式 。對于就業(yè)班來說,掌握以下知識點即可。

Map

import { Map } from 'immutable'
?
const map = Map({
  a: 1,
  b: 2,
  c: 3
})
?
const newMap = map.set('b', 20) // immutable數(shù)據(jù)每次都是生成新的再重新調(diào)用set進行修改,所以需要 重新賦值給一個新的變量
?
console.log(map, newMap) // immutable.Map不是原生的對象
console.log(map.b, newMap.b) // immutable.Map不是原生的對象, 所以是undefined
console.log(map.get('b'), newMap.get('b')) // 要取值,需要調(diào)用get(key)方法,可以看到,兩個值不一樣
?
const obj = {
  a: 1,
  b: 2,
  c: 3
}
?
console.log(Map.isMap(map), Map.isMap(obj)) // true false, 使用Map.isMap來判斷是否是一個immutable.Map類型

List

import { List } from 'immutable'
?
const list = List([1, 2, 3, 4])
const newList = list.push(5)
console.log(list, newList)
console.log(list[4], newList[4]) // undefined undefined
console.log(list.get(4), newList.get(4)) // undefined 5
console.log(list.size, newList.size) // 4 5
?
const arr = [1, 2, 3, 4]
?
console.log(List.isList(list), List.isList(arr)) // true false

equals & is

import { Map, is } from 'immutable'
?
const map = Map({
  a: 1,
  b: 2,
  c: 3
})
?
const anotherMap = Map({
  a: 1,
  b: 2,
  c: 3
})
?
console.log(map == anotherMap) // false
console.log(map === anotherMap) // false
console.log(map.equals(anotherMap)) // 使用equals進行比較 true
console.log(is(map, anotherMap)) // 使用is進行比較 true

List常用api

import { List } from 'immutable'
?
const list = List([1, 2, 3, 4])
const list1 = list.push(5)
const list2 = list1.unshift(0)
const list3 = list.concat(list1, list2)
const list4 = list.map(v => v * 2)
?
console.log(list.size, list1.size, list2.size, list3.size, list4.toJS()) // 4 5 6 15, [2, 4, 6, 8]

Map常用api

import { Map } from 'immutable'
?
const alpha = Map({
  a: 1,
  b: 2,
  c: 3
})
const objKeys = alpha.map((v, k) => k)
console.log(objKeys.join()) // a, b, c
?
?
const map1 = Map({
  a: 1,
  b: 2
})
const map2 = Map({
  c: 3,
  d: 4
})
const obj = {
  d: 400,
  e: 50
}
?
const mergedMap = map1.merge(map2, obj)
?
console.log(mergedMap.toObject())
console.log(mergedMap.toJS())

嵌套數(shù)據(jù)結(jié)構(gòu)

const { fromJS } = require('immutable');
const nested = fromJS({ a: { b: { c: [ 3, 4, 5 ] } } });
?
const nested2 = nested.mergeDeep({ a: { b: { d: 6 } } });
// Map { a: Map { b: Map { c: List [ 3, 4, 5 ], d: 6 } } }
?
console.log(nested2.getIn([ 'a', 'b', 'd' ])); // 6
?
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 } } }

在redux中使用immutable.js

redux官網(wǎng)推薦使用redux-immutable進行redux和immutable的集成。幾個注意點:

redux中,利用combineReducers來合并多個reduce, redux自帶的combineReducers只支持原生js形式的,所以需要使用redux-immutable提供的combineReducers來代替

// 使用redux-immutable提供的combineReducers方法替換redux里的combineReducers
import {combineReducers} from 'redux-immutable'
import reducerOne from './reducerOne'
import reducerTwo from './reducerTwo'

const rootReducer = combineReducers({
    reducerOne,
    reducerTwo
});

export default rootReducer;

reducer中的initialState也需要初始化成immutable類型, 比如一個counter的reducer

import { Map } from 'immutable'
?
import ActionTypes from '../actions'
?
const initialState = Map({
  count: 0
})
?
export default (state = initialState, action) => {
  switch (action.type) {
    case ActionTypes.INCREAMENT:
      return state.set('count', state.get('count') + 1) // 使用set或setIn來更改值, get或者getIn來取值
    case ActionTypes.DECREAMENT:
      return state.set('count', state.get('count') - 1)
    default:
      return state
  }
}

state成為了immutable類型,connectmapStateToProp也需要相應(yīng)的改變

const mapStateToProps = state => ({
  count: state.getIn(['counter', 'count']) // 永遠(yuǎn)不要在mapStateToProps里使用`toJS`方法,因為它永遠(yuǎn)返回一個新的對象
})

shouldComponentUpdate里就可以使用immutable.is或者instance.equals來進行數(shù)據(jù)的對比了。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容