immutable.js
- 官方文档
- 每次修改一个
Immutable
对象时都会创建一个新的不可变的对象,在新对象上操作并不会影响到原对象的数据。
immutable
是一种持久化数据结构,immutable
数据就是一旦创建,就不能更改的数据,每当对immutable
对象进行修改的时候,就会返回一个新的immutable
对象,以此来保证数据的不可变。
讲讲深浅拷贝
浅拷贝
- 只拷贝对象的一层数据,再深处层次的引用类型
value
将只会拷贝引用 实现方式:
Object.assign()
和 ES6
的拓展运算符
let a = { age:10, usage:{ sing:'唱歌' } }
let b = Object.assign({}, a) b.age = 11 b.usage.sing='跳舞' console.log(a,b);
|
深拷贝
- 使用
JSON.stringify()
和JSON.parse()
实现深拷贝
let a = { age:10, usage:{ sing:'唱', jump:()=>{console.log('跳');}, ball:undefined }, }
let b = JSON.parse(JSON.stringify(a)) b.usage.sing='说唱!' console.log('a:',a,'b:',b);
|
- 使用递归实现深拷贝
const deepCopy = (obj) => { if(!obj || typeof obj !== 'object') return obj let result = {} if (Object.prototype.toString.call(obj).indexOf('Array') > 0) { result = [] } Object.keys(obj).forEach(key => { result[key] = deepCopy(obj[key]) }); return result }
let aa = { a: undefined, func: function(){console.log(1)}, b:2, c: {x: 'xxx', xx: undefined}, d: null, e: BigInt(100), f: Symbol('s') } let bb = deepCopy(aa) aa.c.x = 123 aa.func = {} console.log("aa", aa) console.log("bb", bb)
|
- 所谓递归就是使用循环一直读取深层数据并复制,这种方法在实际生产中99.99%不会用到,因为这样性能太差了且占用内存,一般使用第三方库来实现深复制例如
immutable.js
等….
为什么在React
需要使用深复制
- 我们学过
Vue
知道,我们根本就不需要知道什么不可变对象
,也不需要考虑数据修改时必须不能影响原状态, 因为Vue
的原理与React
不一样,Vue
要求必须修改原状态,数据才能被监听到从而重新渲染页面,但是在React
中,我们必须保留原状态可用的情况下去操作新状态,由此可见在React
中,一旦你在操作状态时,复制的层级不够, 那么老状态就很可能会被影响,这样来说,整个项目就很可能会出现问题了!
1. Immutable
优化性能的方式
Immutable
实现的原理是 Persistent Data Structure
(持久化数据结构),也就是使用旧数据创建新数据时,要保证旧数据同时可用且不变。同时为了避免 deepCopy
把所有节点都复制一遍带来的性能损耗,Immutable
使用了 Structural Sharing
(结构共享),即如果对象树中一个节点发生变化,只修改这个节点和受它影响的父节点, 其它节点则进行共享。如下图所示
基本使用
- 安装
- 介绍
Map
的基本实用
import {Map} from 'immutable'
var obj = { name:'lam', age:10 } var oldImmuObj = Map(obj)
var newImmuObj = oldImmuObj.set("name","xiaoming") console.log('旧对象:',oldImmuObj,'\n','新对象',newImmuObj)
console.log('读取Map中的数据',oldImmuObj.get("name"),newImmuObj.get("name"));
var obj2 = oldImmuObj.delete('age') console.log('删除对应属性',obj2);
console.log(obj2.toJS());
var obj3 = Map({age:20}) console.log(obj2.merge(obj3));
var obj4 = Map({age:10,usage:()=>{console.log('唱跳rap篮球!');},friends:{num:10}}) console.log(obj2.mergeDeep(obj4));
immutable.fromJS({name:'lam', age:18})
|
- 介绍
List
的基本使用(作用于数组)
import {List} from 'immutable' var arr = List([1,2,3])
var arr2 = arr.push(4) var arr3 = arr2.concat([5,6,7]) console.log(arr.toJS(),arr2.toJS(),arr3.toJS())
arr3.map(item=>{ console.log(item); })
immutable.fromJS([1,2,3,4,5])
|
结果展示:
案例展示:
- 使用
immutable
中的Map
结合组件状态(state
)实现数据的展示,深层数据再次使用Map
包裹,并且将数据传递给子组件,子组件通过校验数据来判断是否重新渲染!
import React, { Component } from 'react' import {Map} from 'immutable'
export default class App extends Component { state = { info:Map({ name:"lam", select:'aa', filter:Map({ text:"", up:true, down:false }) }) } componentDidMount() { console.log(this.state.info.get("filter")) } render() { return ( <div> {/* 设置点击修改状态 */} <button onClick={()=>{ this.setState({ // Map方法支持链式操作修改数据(每一次状态更新数据都是新的) info:this.state.info.set("name","肥林").set("select","dwadwa") }) }}>点击</button> {this.state.info.get("name")}--{this.state.info.get("select")} {/* 组件间的通信 */} <Child filter={this.state.info.get("filter")}/> </div> ) } }
class Child extends Component{ shouldComponentUpdate(nextProps, nextState) { if(this.props.filter === nextProps.filter){ return false }
return true }
render(){ return <div> child </div> }
componentDidUpdate(){ console.log("组件重新渲染了") } }
|
结果展示:
我们可以看到,控制台中并没有输出组件重新渲染
这句或,说明子组件并没有重新渲染!