2022-07-08
什么是 Immutable Data
Immutable Data 就是一旦创建,就不能再被更改的数据。对 Immutable 对象的任何修改或添加删除操作都会返回一个新的 Immutable 对象。Immutable 实现的原理是 Persistent Data Structure(持久化数据结构),也就是使用旧数据创建新数据时,要保证旧数据同时可用且不变。同时为了避免 deepCopy 把所有节点都复制一遍带来的性能损耗,Immutable 使用了 Structural Sharing(结构共享),即如果对象树中一个节点发生变化,只修改这个节点和受它影响的父节点,其它节点则进行共享。
好处:提高数据处理的性能
坏处:加大数据的难度,UI库对此对象的支持不友好
目前流行的 Immutable 库有两个:
immutable.js
Facebook 工程师 Lee Byron 花费 3 年时间打造,与 React 同期出现,但没有被默认放到 React 工具集里(React 提供了简化的 Helper)。它内部实现了一套完整的 Persistent Data Structure,还有很多易用的数据类型。像 Collection、List、Map、Set、Record、Seq。有非常全面的map、filter、groupBy、reduce``find函数式操作方法。同时 API 也尽量与 Object 或 Array 类似。
其中有 3 种最重要的数据结构说明一下:(Java 程序员应该最熟悉了)
- Map:键值对集合,对应于 Object,ES6 也有专门的 Map 对象
- List:有序可重复的列表,对应于 Array
- Set:无序且不可重复的列表
seamless-immutable
与 Immutable.js 学院派的风格不同,seamless-immutable 并没有实现完整的 Persistent Data Structure,而是使用 Object.defineProperty(因此只能在 IE9 及以上使用)扩展了 JavaScript 的 Array 和 Object 对象来实现,只支持 Array 和 Object 两种数据类型,API 基于与 Array 和 Object 操持不变。代码库非常小,压缩后下载只有 2K。而 Immutable.js 压缩后下载有 16K。
下面上代码来感受一下两者的不同:
// 原来的写法
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
// 使用 seamless-immutable.js 后
import SImmutable from 'seamless-immutable';
foo = SImmutable({a: {b: 1}})
bar = foo.merge({a: { b: 2}}) // 使用 merge 赋值
console.log(foo.a.b); // 像原生 Object 一样取值,打印 1
console.log(foo === bar); // 打印 false
Immutable不是react自带的,需要安装第三方库
npm i -S immutable
Immutable提供的重要对象
List 类似于原生JS中的数组
Map类似于原生JS中的对象 Map转换只会转换第一层 获取数据的方法get不建议使用,建议使用fromJS
fromJS用于转换把原生JS对象转为对应的immutable对象
let obj={name:'zhangsan',age:20,info:{m:1}}
let state=Map(obj)
console.log(state)
console.log(obj)
获取immutable对象中的数据方法get/getIn
Map
console.log(state.get('name'))
console.log(state.getIn(['name']))
深层调用建议使用 getIn
console.log(state.get('info').get('m'))
console.log(state.getIn(['info','m']))
List
let state = fromJS([1,2,{id:1,name:'lisi'}])
console.log(state.get(1))
console.log(state.getIn([2,'name']))
List中还具有和数组一样的方法,二者之间的区别是List会返回一个新的immutable对象
修改immutable对象中的数据方法 set/setIn/update/updateIn
let newState1=state.set('name','zhangjijie')
修改多层级
let newState2 = state.setIn(['info','m'],10)
在原有数据上进行更改
let newState3 = state.update('age',cb=>cb+10)
let newState=state.updateIn(['info','m'],cb=>cb+10)
toJS是转换为原生JS的方法
console.log(newState.toJS())
删除immutable对象中的数据方法 remove/removeIn/delete
let newState1 = state.remove('age')
let newState = state.removeIn(['info','m'])
immutable优化组件
PureComponent只能优化简单数据类型的重复渲染,对于复杂数据类型的重复渲染优化不了。
过渡动画组件
在项目中可能会有一些动画效果展示或是页面切换效果, react-transition-group是react的第三方模块,这个模块可以实现动画切换效果
安装第三方
npm i react-transition-group
三个动画组件
CSSTransition 动画组件 他只能有一个子元素,之于后代可以有多个
SwitchTranstion 切换过渡
TransitionGroup 列表动画组件,用来包裹 CSSTransition
使用
import React, { Component } from 'react'
import { CSSTransition } from 'react-transition-group'
import './style/animate.css'
export default class App extends Component {
state={
show:true
}
render() {
return (
<div>
<CSSTransition
// 进出场
in={this.state.show}
// 动画时长
timeout={300}
// 给样式名称添加前缀
classNames="fade"
// 出场后删除DOM
unmountOnExit
>
<h3>我是一个内容显示</h3>
</CSSTransition>
<hr />
<button onClick={()=>{
this.setState(state=>({show:!state.show}))
}}>切换显示</button>
</div>
)
}
}
列表过渡组件
import React, { Component } from 'react'
import { CSSTransition ,TransitionGroup} from 'react-transition-group'
import './style/animate.css'
export default class App extends Component {
state={
title:'',
todos:[]
}
onEnter=({key})=>{
if(key==='Enter'){
this.setState(state=>({
todos:[...state.todos,{id:Date.now(),title:state.title}]
}),()=>{
this.setState({title:''})
})
}
}
delItem = id=>{
this.setState(state=>({
todos:state.todos.filter(item=>item.id!==id)
}))
}
render() {
return (
<div>
<div>
<input type="text" value={this.state.title} onChange={e=>this.setState({title:e.target.value.trim()})}
onKeyUp={this.onEnter}
/>
</div>
<TransitionGroup>
{
this.state.todos.map(item =>(
<CSSTransition
// 进出场
in={this.state.show}
// 动画时长
timeout={300}
// 给样式名称添加前缀
classNames="fade"
// 出场后删除DOM
unmountOnExit
>
<div>
<span>{item.title}</span>---
<span onClick={()=>this.delItem(item.id)}>删除</span>
</div>
</CSSTransition>
))
}
</TransitionGroup>
</div>
)
}
}