今天我们解决以下几个问题,什么是immutable data
,mutable data
带来了哪些问题,immutable data优化了哪些性能?
mutable data 数据的可变性
数据的可变性用一段代码就可以描述清楚
const a = [{ todo: 'Learn js'}, {todo: 'Learn react'}];
const b = [...a];
b[1].todo = 'Learn vue';
console.log(a[1].todo); //Learn vue
其实可以一眼看出这是浅copy
导致的问题。内层的对象指向堆内存
地址相同,所以修改b数组中的对象,a数组也会发生变化。平时大伙在项目中操作比较复杂的数据结构时,都习惯性deepCopy
,否则就会出现一些不易察觉的bug。
immutable数据优化了哪些性能
首先我们要看一下React 是如何渲染的。
React 渲染机制解析
graph LR
setState或者props改变 --> shouldComponentUpdate --true --> 递归render
递归render --> componentDidUpdate
在React中,render函数返回虚拟dom树,并经过Diff算法计算出与上次虚拟dom的区别,针对差异的部分做更新,渲染出真实dom。
递归render的过程是性能消耗的大头,如果shouldComponentUpdate返回false,更新的过程就会被打住,所以我们要好好的利用这个shouldComponentUpdate。
shouldComponentUpdate
这是一个组件的子树。每个节点中,SCU 代表 shouldComponentUpdate 返回的值,而 vDOMEq 代表返回的 React 元素是否相同。最后,圆圈的颜色代表了该组件是否需要被调停,红色代表shouldComponentUpdate返回true,进行render,绿色代表返回false,不进行render。
c1是红色节点,shouldComponentUpdate
返回 true
,进入diff算法比对新旧VDom树,如果新旧VDom树中节点类型不同
,则全部替换
,包括下面子组件,图中展示的是节点类型相同
的情况,则递归子组件
。
//什么是节点类型不同
<A>
<C/>
</A>
// A与B是不同节点类型
<B>
<C/>
</B>
React会直接删掉A节点(包括它所有的子节点),然后新建一个B节点插入。
节点 C2 的 shouldComponentUpdate
返回了 false
,React 因而不会调用 C2 的 render
,也因此 C4 和 C5 的 shouldComponentUpdate
不会被调用到。
C3,shouldComponentUpdate
返回了 true
,所以 React 需要继续向下查询子节点。这里 C6 的 shouldComponentUpdate
返回了 true
,同时由于渲染的元素与之前的不同
使得 React 更新了该 DOM。
最后一个有趣的例子是 C8。React 需要渲染这个组件,但是由于其返回的 React 元素和之前渲染的相同,所以不需要更新 DOM。
显而易见,你看到 React 只改变了 C6 的 DOM。对于 C8,通过对比了渲染的 React 元素跳过了渲染。而对于 C2 的子节点和 C7,由于 shouldComponentUpdate 使得 render 并没有被调用。因此它们也不需要对比元素了。
类组件React.PureComponent与函数组件memo
通过shouldComponentUpdate可以避免不必要的渲染过程,从而达到性能上的优化。但是如果需要我们挨个对比props和state中的每个属性的话就太麻烦了,React提供了两种方式自动帮我们完成shouldComponentUpdate中的工作,类组件只要继承React.PureComponent
就可以,函数组件提供了memo
方法。
//三种方式
class CounterButton extends React.Component {
constructor(props) {
super(props);
this.state = {count: 1};
}
shouldComponentUpdate(nextProps, nextState) {
if (this.props.color !== nextProps.color) {
return true;
}
if (this.state.count !== nextState.count) {
return true;
}
return false;
}
render() {
return (
<button
color={this.props.color}
onClick={() => this.setState(state => ({count: state.count + 1}))}>
Count: {this.state.count}
</button>
);
}
}
class CounterButton extends React.PureComponent {
constructor(props) {
super(props);
this.state = {count: 1};
}
render() {
return (
<button
color={this.props.color}
onClick={() => this.setState(state => ({count: state.count + 1}))}>
Count: {this.state.count}
</button>
);
}
}
const CounterButton = props => {
const [count, setCount] = useState(1);
return (
<button color={props.color} onClick={() => setCount(count => count + 1)}>
Count: {count}
</button>
);
};
export default React.memo(CounterButton);
那有没有一种方式,使用浅比较就可以得出哪部分是改变的数据节点呢?
immutable 数据结构
我们还是通过一小段代码来认识什么是immutable数据,这里使用的是Immer这个库。
import produce from 'immer';
const a = [{ todo: 'Learn js' }, { todo: 'Learn react' }];
const b = produce(a, draftState => {
draftState[1].todo = 'Learn vue';
});
console.log(a === b); //false
console.log(a[0] === b[0]); //true
console.log(a[1] === b[1]); //false
console.log(a[1].todo === b[1].todo); //false
这里可以看到未改变的引用类型内存地址未发生改变,保证了旧节点的可用且不变,而改变了的节点,它和与它相关的所有上级节点都更新。如图所示:
这样就避免了深拷贝带来的极大的性能开销问题,并且更新后返回了一个全新的引用,即使是浅比对也能感知到哪一部分数据需要更新。
immer应用示例
const [state, setState] = useState({
id: 14,
email: "stewie@familyguy.com",
profile: {
name: "Stewie Griffin",
bio: "You know, the... the novel you've been working on",
age:1
}
});
function changeBio(newBio) {
setState(current => ({
...current,
profile: {
...current.profile,
bio: newBio
}
}));
}
//使用 immer
import { useImmer } from 'use-immer';
const [state, setState] = useImmer({
id: 14,
email: "stewie@familyguy.com",
profile: {
name: "Stewie Griffin",
bio: "You know, the... the novel you've been working on",
age:1
}
});
function changeBio(newBio) {
setState(draft => {
draft.profile.bio = newBio;
});
}
减少了解构语法是不是清爽了很多,当然随着数据结构进一步复杂,immer优势也会进一步体现。
感谢大家的阅读。
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!