banner
SlhwSR

SlhwSR

热爱技术的一名全栈开发者
github
bilibili

redux-immutable優化專案

一、是什麼#

Immutable,不可改變的,在計算機中,即指一旦創建,就不能再被更改的數據

Immutable 對象的任何修改或添加刪除操作都會返回一個新的 Immutable 對象

Immutable 實現的原理是 Persistent Data Structure(持久化數據結構):

  • 用一種數據結構來保存數據
  • 當數據被修改時,會返回一個對象,但是新的對象會盡可能的利用之前的數據結構而不會對內存造成浪費

也就是使用舊數據創建新數據時,要保證舊數據同時可用且不變,同時為了避免 deepCopy 把所有節點都複製一遍帶來的性能損耗,Immutable 使用了 Structural Sharing(結構共享)

如果對象樹中一個節點發生變化,只修改這個節點和受它影響的父節點,其它節點則進行共享

如下圖所示:

image

二、如何使用#

使用Immutable對象最主要的庫是immutable.js

immutable.js 是一個完全獨立的庫,無論基於什麼框架都可以用它

其出現場景在於彌補 Javascript 沒有不可變數據結構的問題,通過 structural sharing 來解決的性能問題

內部提供了一套完整的 Persistent Data Structure,還有很多易用的數據類型,如CollectionListMapSetRecordSeq,其中:

  • List: 有序索引集,類似 JavaScript 中的 Array

  • Map: 無序索引集,類似 JavaScript 中的 Object

  • Set: 沒有重複值的集合

主要的方法如下:

  • fromJS ():將一個 js 數據轉換為 Immutable 類型的數據
const obj = Immutable.fromJS({a:'123',b:'234'})
  • toJS ():將一個 Immutable 數據轉換為 JS 類型的數據
  • is ():對兩個對象進行比較
import { Map, is } from 'immutable'
const map1 = Map({ a: 1, b: 1, c: 1 })
const map2 = Map({ a: 1, b: 1, c: 1 })
map1 === map2   //false
Object.is(map1, map2) // false
is(map1, map2) // true
  • get (key):對數據或對象取值

  • getIn ([]) :對嵌套對象或數組取值,傳參為數組,表示位置

let abs = Immutable.fromJS({a: {b:2}});
abs.getIn(['a', 'b']) // 2
abs.getIn(['a', 'c']) // 子級沒有值

let arr = Immutable.fromJS([1 ,2, 3, {a: 5}]);
arr.getIn([3, 'a']); // 5
arr.getIn([3, 'c']); // 子級沒有值

如下例子:使用方法如下:

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

如果換到原生的js,則對應如下:

let foo = {a: {b: 1}};
let bar = foo;
bar.a.b = 2;
console.log(foo.a.b);  // 打印 2
console.log(foo === bar);  //  打印 true

三、在 React 中應用#

使用 Immutable 可以給 React 應用帶來性能的優化,主要體現在減少渲染的次數

在做react性能優化的時候,為了避免重複渲染,我們會在shouldComponentUpdate()中做對比,當返回true執行render方法

Immutable通過is方法則可以完成對比,而無需像一樣通過深度比較的方式比較

在使用redux過程中也可以結合Immutable,不使用Immutable前修改一個數據需要做一個深拷貝

import '_' from 'lodash';

const Component = React.createClass({
  getInitialState() {
    return {
      data: { times: 0 }
    }
  },
  handleAdd() {
    let data = _.cloneDeep(this.state.data);
    data.times = data.times + 1;
    this.setState({ data: data });
  }
}

使用 Immutable 後:

getInitialState() {
  return {
    data: Map({ times: 0 })
  }
},
  handleAdd() {
    this.setState({ data: this.state.data.update('times', v => v + 1) });
    // 這時的 times 並不會改變
    console.log(this.state.data.get('times'));
  }

同理,在redux中也可以將數據進行fromJS處理

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
    }
}
載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。