支持混用
react 17 最主要的改动仍然是要支持渐进式,你可以在一个应用中使用 react17,另外一个使用 react 18。
const rootNode = document.getElementById('root');
ReactDOM.render(<AppReact17 />, rootNode);
const rootNode2 = document.getElementById('root2');
ReactDOM.render(<AppReact18 />, rootNode);
react 官方要支持这种用法,推测的 react18 的改动将会巨大无比,甚至于完全不兼容旧的。
更改事件委托
原来的 react 的事件系统是挂在 document 上的,为了支持混用将会修改到你挂载的 rootNode 中,这样其实会造成一些问题,比如说原来的e.stopPropagation()
能解除原生 document 事件的冒泡,现在只能阻止到 rootNode,但大部分时候是没有影响的。
portals 的事件还会继续监听,当然还有一些小更新
- onScroll事件不再冒泡,以防止出现一些混淆。
- React的onFocus和onBlur事件已在底层切换为原生的focusin和focusout事件。它们更接近React现有行为,有时还会提供额外的信息。
- 捕获事件(例如,onClickCapture)现在使用的是实际浏览器中的捕获监听器
- 去除事件池,在现在浏览器上,这个功能不会提升性能,反而会因为重用事件对象,造成困扰。
- focus 事件底层切换成了 focusin,行为没有变化。
总体而言,这次改动会让 react 的事件树更符合 dom 的规范,虽然会导致很多面试题不能用了。
useEffect 的回调修改为异步调用
useEffect 的副作用清理函数是在 effect 执行之后立马执行的,但是在使用中发现了如果回调中的操作比较耗时,会造成一些性能问题,现在useEffect 的 副作用清理函数会在 render 后执行了。
官方提供了一个 demo 来展示可能会造成的问题,我们一般也不这么用。如果有特殊情况,可以用 useLayoutEffect 来代替。
useEffect(() => {
const instance = someRef.current;
instance.someSetupMethod();
return () => {
instance.someCleanupMethod();
};
});
返回一致的undefined错误
这个改动几乎无感,以前,React只对class和函数组件执行此操作,但并不会检查forwardRef和memo组件的返回值。现在都会检查,不要写奇怪的代码是不会挂的。最好用 return null
来代替。
展望一下 18
自从 2016 年 Seb 提出了 fiber 架构以来,react 已经吊足了人们的胃口 4 年了。react16 重写了 react 的渲染引擎,一直到最近发布的 17,看起来 async component 终于快要发布了。这几年虽然有了 hooks,但是仍然没有 fiber 来的让人激动。
fiber 架构主要还是用来对 cpu 和网络的问题,这两个问题一直也是最影响前端开发体验的地方,一个会造成卡顿,一个会造成白屏。为此 react 为前端引入了两个新概念。
Time Slicing 时间分片
时间分片是利用了 fiber 的可中断,可继续的功能,每个渲染周期内都会留一部分的时间来响应用户的输入,或者其他 IO 的状态修改。
- React 在渲染(render)的时候,不会阻塞现在的线程
- 如果你的设备足够快,你会感觉渲染是同步的
- 如果你设备非常慢,你会感觉还算是灵敏的
- 虽然是异步渲染,但是你将会看到完整的渲染,而不是一个组件一行行的渲染出来
- 同样书写组件的方式
Suspense
// 这不是一个 Promise。这是一个支持 Suspense 的特殊对象。
const resource = fetchProfileData();
function ProfileDetails() {
// 尝试读取用户信息,尽管该数据可能尚未加载
const user = resource.user.read();
return <h1>{user.name}</h1>;
}
function ProfileTimeline() {
// 尝试读取博文数据,尽管数据可能未加载完毕
const posts = resource.posts.read(); return (
<ul>
{posts.map(post => (
<li key={post.id}>{post.text}</li>
))}
</ul>
);
}
function ProfilePage() {
return (
<Suspense fallback={<h1>Loading profile...</h1>}>
<ProfileDetails />
<Suspense fallback={<h1>Loading posts...</h1>}>
<ProfileTimeline />
</Suspense>
</Suspense>
);
}
**
react 的思路已经拓宽了整个前端的边界,18 发布之后虽然会造成很多问题,比如 antd 的动画就一定会挂。但是默认的性能提升可以让很多中后台获得性能加成, 很多项目其实最后也不会有机会进行细致的优化。Suspense 更是会更改我们前端写代码的范式,期待早点发布。
参考文档
https://reactjs.org/blog/2020/08/10/react-v17-rc.html
https://reactjs.org/blog/2018/03/01/sneak-peek-beyond-react-16.html
https://github.com/acdlite/react-fiber-architecture
https://zh-hans.reactjs.org/docs/concurrent-mode-suspense.html#approach-1-fetch-on-render-not-using-suspense