1. Time Slicing
时间分片
官方说明是可以进行异步的渲染,不会阻塞当前的线程,当cpu足够用的话,看起来是同步渲染的,当cpu很吃紧的时候,会看到有点卡顿,但不至于卡死,对于开发者透明,不影响开发过程。
原理暂时没搞明白,渲染的方式肯定还是同步的渲染下来,至少父子组件的渲染是同步,猜测是把渲染任务推到任务队列里,然后一个个去执行,如果主线程很忙就会执行频率降低,如果主线程不忙就执行频率和同步没区别。
2. Suspense , 与 lazy
const OtherComponent = React.lazy(() => import('./OtherComponent'));
function MyComponent() {
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
<OtherComponent />
</Suspense>
</div>
);
}
使用React.lazy方法包裹传入组件的函数,
加载的过程中显示fallback传入的组件,加载完成后显示真正的组件
注意,Suspense可以包含多个懒加载的组件
这可以解决组件异步加载的问题,如果OthoerComponent组件很庞大,庞大到需要1秒时间来加载,那么如果不用lazy的话,MyComponent的执行必须等OthoerComponent加载完才能执行,但是现在我们可以先渲染MyComponent再去加载OtherComponent,加载完后把loading组件替换成OtherComponnet
3. React.memo
纯函数组件没有shouldComponentUpdate()这样的钩子,每当父组件render,纯函数组件就会重新render一次,即使纯函数组件的参数没有变化。为了给这个纯函数组件检查参数是否变化了,就必须写成一个Class,在她shouldComponentUpdate()里检查Props,
React.memo就是为了帮我们方便得处理这样的问题而不用去写一个class,
import React from "react";
function Child({seconds}){
console.log('I am rendering');
return (
<div>I am update every {seconds} seconds</div>
)
};
function areEqual(prevProps, nextProps) {
if(prevProps.seconds===nextProps.seconds){
return true
}else {
return false
}
}
export default React.memo(Child,areEqual)
React.memo()可接受2个参数,第一个参数为纯函数的组件,第二个参数用于对比props控制是否刷新,与shouldComponentUpdate()
功能类似
又可以偷懒了,其实这样的函数自己实现也容易,相当于把第一参数作为render,第二个参数作为shouldComponentUpdate去创建一个组件,具体react的实现应该会比我们自己实现的性能更好
4. React.Profiler 性能分析
React 16.5 添加了对新的 profiler DevTools 插件的支持。这个插件使用 React 的 Profiler 实验性 API 去收集所有 component 的渲染时间,目的是为了找出你的 React App 的性能瓶颈。它将会和我们即将发布的 时间片 特性完全兼容。
这个工具以后有机会再看,大概是个能够检查渲染性能工具
5.
Hooks
解决 class 中生命周期函数经常包含不相关的逻辑,但又把相关逻辑分离到了几个不同方法中的问题
如果一个组件需要监听窗口的变换来调整自己,那么他就需要在mounted时增加监听,在willUnmount时移除监听,这样的代码是没问题,在在逻辑上并不合理,如果能把增加监听和移除监听写到一块就比较清晰了。
import React, { useState } from 'react';
function Example() {
const [count, setCount] = useState(0);
// 声明多个 state 变量
const [age, setAge] = useState(42);
const [fruit, setFruit] = useState('banana');
const [todos, setTodos] = useState([{ text: '学习 Hook' }]);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
我理解hook实际上是这么工作,render时使用useState从一个状态池中取出一个状态,并且提供一个函数可以修改这个状态,这个状态池和这个render函数绑定,也就是说每个状态池都属于这个render函数,状态池实际上是个空数组,并且有个游标也和这个函数绑定,每当函数开始执行,游标置为0,当调用一次useState后,游标加一,useState会去这个状态池的游标对应的位置去获取元素,如果这个函数是第一次执行则会将useState的参数先存到油标对应的状态池里。按这个思路,我打算有时间自己实现一个hook来玩玩。
另一个点,useEffect
function FriendStatus(props) {
const [isOnline, setIsOnline] = useState(null);
useEffect(() => {
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
// Specify how to clean up after this effect:
return function cleanup() {
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
};
});
if (isOnline === null) {
return 'Loading...';
}
return isOnline ? 'Online' : 'Offline';
}
componentDidMount,componentUpdate时执行 useEffect里的函数
useEffect传入一个函数,在首次执行render函数时会将userEffect传入的函数保存起来,存在render函数下,在组件挂载,更新时执行传入的函数,在组件卸载时调用返回的函数(cleanup),如果组件频繁更新则会导致频繁地执行副作用。并且这将非render的任务写到了render里,个人觉得虽然代码骚起来了,但代码也更乱了,更难维护了。