Immutable 在 React 中的应用

理解 Immutable 数据结构及其在 React 项目中的使用方法

问题

什么是 Immutable?如何在 React 项目中应用?

解答

什么是 Immutable

Immutable 指一旦创建就不能被更改的数据。对 Immutable 对象的任何修改、添加或删除操作都会返回一个新的 Immutable 对象。

Immutable 基于 Persistent Data Structure(持久化数据结构)实现。当数据被修改时,会返回一个新对象,但新对象会尽可能利用之前的数据结构,避免内存浪费。

通过 Structural Sharing(结构共享),当对象树中某个节点发生变化时,只修改该节点和受影响的父节点,其他节点保持共享,避免深拷贝带来的性能损耗。

使用 immutable.js

immutable.js 提供了完整的不可变数据结构,主要数据类型包括:

  • List:有序索引集,类似 Array
  • Map:无序索引集,类似 Object
  • Set:没有重复值的集合

常用方法:

fromJS() 和 toJS()

// JS 数据转为 Immutable
const obj = Immutable.fromJS({a: '123', b: '234'})

// Immutable 转为 JS
const jsObj = obj.toJS()

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
is(map1, map2)  // true

get() 和 getIn() 取值

let abs = Immutable.fromJS({a: {b: 2}})
abs.getIn(['a', 'b']) // 2

let arr = Immutable.fromJS([1, 2, 3, {a: 5}])
arr.getIn([3, 'a']) // 5

setIn() 赋值

import Immutable from 'immutable'
const foo = Immutable.fromJS({a: {b: 1}})
const bar = foo.setIn(['a', 'b'], 2)
console.log(foo.getIn(['a', 'b']))  // 1
console.log(foo === bar)  // false

在 React 中应用

优化 shouldComponentUpdate

使用 Immutable 可以简化性能优化,通过 is() 方法快速比较,避免深度比较:

import { is } from 'immutable'

shouldComponentUpdate(nextProps, nextState) {
  return !is(this.props, nextProps) || !is(this.state, nextState)
}

在组件状态中使用

import { Map } from 'immutable'

getInitialState() {
  return {
    data: Map({ times: 0 })
  }
}

handleAdd() {
  this.setState({ 
    data: this.state.data.update('times', v => v + 1) 
  })
}

在 Redux 中使用

import { fromJS } from 'immutable'
import * as constants from './constants'

const defaultState = fromJS({
  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)
    case constants.CHANGE_LIST:
      // 使用 merge 一次修改多个字段,效率更高
      return state.merge({
        list: action.data,
        totalPage: action.totalPage
      })
    default:
      return state
  }
}

关键点

  • Immutable 通过持久化数据结构和结构共享实现高性能的不可变数据
  • 使用 is() 方法可以快速比较两个 Immutable 对象,避免深度比较
  • 在 React 中可以优化 shouldComponentUpdate,减少不必要的渲染
  • 在 Redux 中使用 Immutable 可以简化 reducer 的编写,避免手动深拷贝
  • merge() 方法可以一次性修改多个字段,比多次 set() 效率更高