immutable.js 是 Facebook 開源的一個項目,用於實現 javascript 的數據不可變,解決引用帶來的反作用。javascript
不變的數據(Immutable Data )一旦建立就沒法更改,從而能夠簡化應用程序開發,進行防護性複製,並可使用簡單的邏輯實現高級的備忘和更改檢測技術。持久數據提供了一個可變API,該API不會就地更新數據,而是始終產生新的更新數據,也就是說,對 Immutable 對象的任何修改或添加刪除操做都會返回一個新的 Immutable 對象。java
// 原來的寫法
let foo = {a: {b: 1}};
let bar = foo;
bar.a.b = 2;
console.log(foo.a.b); // 打印 2
console.log(foo === bar); // 打印 true
// 使用 immutable.js 後
import Immutable from 'immutable';
foo = Immutable.fromJS({a: {b: 1}});
bar = foo.setIn(['a', 'b'], 2); // 使用 setIn 賦值
console.log(foo.getIn(['a', 'b'])); // 使用 getIn 取值,打印 1
console.log(foo === bar); // 打印 false
複製代碼
immutable.js提供了一些永久不可變數據結構,包括:react
做用:將一個js數據轉換爲Immutable類型的數據
用法:fromJS(value, reviver)
簡介:value是要轉變的數據,reviver是要作的操做。第二個參數可不填,默認狀況會將數組準換爲List類型,將對象轉換爲Map類型,其他不作操做。git
let bar = { a: 10, b: [10, 20, 30],c: 40,d: 10, e: [10, 20, 30],f: {g:10,h:{i:20}}}
let demo = Immutable.fromJS(bar,
function (key, value, path) {
console.log(Immutable.isKeyed(value),'key='+key,'value='+value,'path=',path)
})
//打印結果
//false "key=b" "value=Seq [ 10, 20, 30 ]" "path=" ["b"]
//false "key=e" "value=Seq [ 10, 20, 30 ]" "path=" ["e"]
//true "key=h" "value=Seq { "i": 20 }" "path=" (2) ["f", "h"]
//true "key=f" "value=Seq { "g": 10, "h": undefined }" "path=" ["f"]
//true "key=" "value=Seq { "a": 10, "b": undefined, "c": 40, "d": 10, "e": undefined, "f": undefined }" "path=" []
複製代碼
做用:將一個Immutable List類型數據轉換爲JS類型的數據
用法:value.toJS()es6
做用:對兩個對象進行比較 用法:is(first,second) 簡介:能夠比較原始類型(如字符串和數字),Immutable.js集合(如Map和List),以及任何經過提供和方法實現的自定義對象,比較0和-0值是相同的。和js中對象的比較不一樣,在js中比較兩個對象比較的是地址,可是在Immutable中比較的是這個對象hashCode和valueOf,只要兩個對象的hashCode相等,值就是相同的,避免了深度遍歷,提升了性能。github
做用:數據的合併,新數據與舊數據對比ajax
此示例爲Map結構,List與Map原理相同:npm
const Map1 = Immutable.fromJS({a:111,b:222,c:{d:333,e:444}});
const Map2 = Immutable.fromJS({a:111,b:222,c:{e:444,f:555}});
const Map3 = Map1.merge(Map2); //舊數據中不存在的屬性直接添加,就數據中已存在的屬性用新數據中的覆蓋
//Map {a:111,b:222,c:{e:444,f:555}}
const Map4 = Map1.mergeDeep(Map2); //深合併,新舊數據中同時存在的的屬性爲新舊數據合併以後的數據
//Map {a:111,b:222,c:{d:333,e:444,f:555}}
const Map5 = Map1.mergeWith((oldData,newData,key)=>{ //自定義淺合併,可自行設置某些屬性的值
if(key === 'a'){
return 666;
}else{
return newData
}
},Map2);
//Map {a:666,b:222,c:{e:444,f:555}}
複製代碼
做用:獲取數據結構中的數據redux
做用:判斷是否存在某一個key數組
做用:判斷是否存在某一個value
Immutable.fromJS
而不是 Immutable.Map
或 Immutable.List
來建立對象,這樣能夠避免 Immutable 和原生對象間的混用,例如,Map & Immutable.Map。map.get('key')
而不是 map.key
,array.get(0)
而不是 array[0]
。另外 Immutable 每次修改都會返回新對象,也很容易忘記賦值。安裝redux-immutable的命令 npm install redux-immutable。
安裝immutable的命令 npm install immutable。 redux-immutable提供一個combineReducers()函數,將stroe中最外層的reducer中的state轉化爲immutable對象(這裏涉及到reducer的拆分,拆分用到了與redux中同名的combineReducers()方法)
好比如今我建立了一個Header的組件(代碼有刪減)
import React,{Component} from 'react';
import {connect} from 'react-redux';
import {CSSTransition} from 'react-transition-group';
class Header extends Component{
render() {
const {home,focused,list,login,handleInputFocus,handleInputBlur,handleHomeActive,logout} = this.props
return (
<HeaderWrapper>
<SearchWrapper>
<CSSTransition
in={focused}
timeout={200}
classNames="slide"
>
<NavSearch
className={focused?'focused':''}
onFocus={()=>handleInputFocus(list)}
/>
</CSSTransition>
{this.getListArea()}
</SearchWrapper>
</HeaderWrapper>
)
}
getListArea() {
const {focused,list,page,totalPage} = this.props;
const newList = list.toJS() //immutable對象裝換成js對象
const pageList = []
//ajax請求發送獲取數據以後再顯示,保證key值得有效
if(newList.length){
for(let i=(page-1)*10;i<page*10;i++) {
pageList.push(
<SearchInfoItem key={newList[i]}>{newList[i]}</SearchInfoItem>
)
}
}
if(focused) { //鼠標聚焦輸入框時展現熱門搜索的列表
return (
<SearchInfo>
<SearchInfoTitle>
熱門搜索
</SearchInfoTitle>
<SearchInfoList>
{pageList}
</SearchInfoList>
</SearchInfo>
)
}else{
return null
}
}
}
const mapState = (state)=>{
return {
home:state.getIn(['header','home']), //獲取state中保存的數據
focused:state.getIn(['header','focused']),
list:state.getIn(['header','list']),
}
}
const mapDispatch = (dispatch)=>{
return {
handleHomeActive(value){
dispatch(changeHomeActive(value))
},
handleInputFocus(list) {
(list.size===0) && dispatch(actionCreators.getList())
dispatch(actionCreators.searchFocus())
},
}
}
export default connect(mapState,mapDispatch)(Header)
}
複製代碼
Header組件目錄下的reducer.js的內容
import * as constants from './constants'
import {fromJS} from 'immutable'
const defaultState = fromJS({ //將數據轉化成immutable數據
home:true,
focused:false,
mouseIn:false,
list:[],
page:1,
totalPage:1
})
export default(state=defaultState,action)=>{
switch(action.type){
case constants.SEARCH_FOCUS:
return state.set('focused',true) //更改immutable數據
case constants.CHANGE_HOME_ACTIVE:
return state.set('home',action.value)
case constants.SEARCH_BLUR:
return state.set('focused',false)
case constants.CHANGE_LIST:
// return state.set('list',action.data).set('totalPage',action.totalPage)
//merge效率更高,執行一次改變多個數據
return state.merge({
list:action.data,
totalPage:action.totalPage
})
case constants.MOUSE_ENTER:
return state.set('mouseIn',true)
case constants.MOUSE_LEAVE:
return state.set('mouseIn',false)
case constants.CHANGE_PAGE:
return state.set('page',action.page)
default:
return state
}
}
複製代碼
根目錄下的reducer.js文件
import {combineReducers} from 'redux-immutable'
import {reducer as headerReducer} from '../common/Header/store'
//當項目中有多個組件時,reducer能夠拆分爲多個reducer,而後在最外層使用combineReducers將各個reducer組合在一塊兒。這樣代碼邏輯比較清晰
const reducer = combineReducers({
header:headerReducer,
})
export default reducer
複製代碼
引用的文章: