You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
这样还是存在一个严重的问题:如果 name 改变了,但 obj 没有变,导致子组件不会重新渲染,数据与UI界面不一致。 看来 useRef 只能用于常量
那我们只要保证 name 不变的时候 obj 和上次一样, name 才让子组件更新就可以了。没错,就是 useMemo。
useMemo 的特性就是保证依项不变时,对应的对象也不会变,只有依赖项变化时,对应的对象才会变
constApp=()=>{const[count,setCount]=React.useState(0);const[name,setName]=React.useState('');React.useEffect(()=>{setInterval(()=>{setCount(s=>s+1);},1000);},[]);// 只有当 name 变化时,obj才会变化constobj=React.useMemo(()=>({
name
}),[name]);return(<><h3>count: {count}</h3><inputtype="text"value={name}onChange={e=>{setName(e.target.value);}}/><Childobj={obj}/></>);};
immutable
上面的方式算是一种解决方案,现在我们来看看其他的东西。
我们在 class 组件中更新状态,只需要一个 setState 就行,不管你传入什么 state,组件都会刷新
// class 的 setState 传什么都会更新组件this.setState({name: 'Jack'})
默认渲染行为
React 的渲染主要分两种:首次渲染和重渲染。
首次渲染就是第一次渲染,这是无法避免的就不讨论了,重复渲染是指由于状态改变或 props 改变等原因造成的渲染。
React 默认的渲染特性:当父组件渲染时,会递归渲染下面所有的子组件(让人诟病的特性)
如下:
React 对这种行为可以说是不负责任,不管你三七二十八,只要父组件渲染,所有它下面的子组件都会渲染,这种方式简单而粗暴。
既然如此,那么这块只能我们这些负责任的开发者去优化了。
浅比较优化
React 把优化问题抛给开发者,它给我们提供了三个用于性能优化的 API。
React.PurComponent
用于 class 写法的组件,它会对传入组件的 props 进行浅比较,如果比较的结果相同,则不会去渲染组件
React.memo
与 React.PurComponent 一样,用于函数组件
浅比较
在 js 中,数据主要分两种:
基本类型的数据属于原始值(primitive value),它直接存储在栈内存中,
引用类型的数据存在于堆内存中,通过存在栈内存中的指针来调用它
对于对象,不仅有引用比较,也有深比较和浅比较
如果对象的一级属性中存在引用比较,则不相等。
对象的深比较跳过了引用比较,仅仅是比较相同层级下的属性值。
React.memo 可以让 props 在变化时,该组件才会发生有意义的重新渲染
我们将子组件用 React.memo 包起来,只要 props 不变,在父组件更新时,子组件也不会重渲染
看起来问题解决了,React.memo 将前后的 props 进行浅比较,这基本能解决大多数问题。但如果 props 中含有对象数据,在浅比较时比较的是引用,这种方式就行不通了,好在 React.memo 提供了第二个参数,可以自定义比较前后的 props
浅比较行不通,那么深比较呢
这样的确可以达到效果,但如果是比较复杂的对象,就会存在较大的性能问题,甚至直接挂掉,因此不建议使用深比较去进行性能优化
还有一种方式:如果能保证对象的值相等,再保证对象的引用相等,就可以保证子组件在 props 没变的情况下不会渲染。
React.useRef 的返回值是固定的常量,我们可以使用它来做为对象的引用
这样还是存在一个严重的问题:如果 name 改变了,但 obj 没有变,导致子组件不会重新渲染,数据与UI界面不一致。 看来 useRef 只能用于常量
那我们只要保证 name 不变的时候 obj 和上次一样, name 才让子组件更新就可以了。没错,就是 useMemo。
useMemo 的特性就是保证依项不变时,对应的对象也不会变,只有依赖项变化时,对应的对象才会变
immutable
上面的方式算是一种解决方案,现在我们来看看其他的东西。
我们在 class 组件中更新状态,只需要一个 setState 就行,不管你传入什么 state,组件都会刷新
但在 hooks 里, 如果前后两次的引用相等,就不会更新组件
在 hooks 中,如果你想修改状态对象,必须保证前后修改的对象引用不等。这就要求我们不能直接更新老的 state,而是要保持老的 state 不变,去生成一个新的 state,也就是 immutable 方式。
老的 state 保持不变,也就意味着该 state 应该是 immutable obj
immutable 的写法过于繁琐,这不是我们想要的
其实,综上来说,我们的需求很简单:
第一个冲上脑门的答案就是:先深拷贝,然后做 mutable 修改就可以了
深拷贝有两个缺点:
虽然市面上有些库支持高性能的深拷贝,但会对参考物对等(reference equality )造成了破坏
可以发现,所有对象的结构都被破坏,在 React 中使用这种方式,即使对象属性没有任何变化,也会导致没有意义的重新渲染,仍会导致比较严重的性能问题
深拷贝是非常糟糕的
这么看来,更新状态还要其他的需求。
我们将 oldState 视为一个属性树,改变其中某节点时,能返回一个新的对象
遗憾的是,JavaScript 并没有内置这种对 immutable 数据的支持,更不用说对 immutable 数据更新了,但可以使用一些三方库来解决这个问题,比如:immer 和 immutablejs
Immutable 数据使用结构共享的方式,只更新修改了子节点的引用,不会去修改未更改的子节点引用,达到我们想要的需求
总结
The text was updated successfully, but these errors were encountered: