原文:https://www.telerik.com/blogs/what-react-17-means-for-developers
我们来看一下最重要的是三个改动——逐步更新(gradual updates)、事件代理的改动,调用堆栈的更新,以及这些改动对于未来的React来说意味着什么。
2020-08-10,React团队发布了React 17版本的发布说明《announced a release candidate of React 17》,并给了一个很有趣的标题——没有新的特性。
尽管标题里说了没有新的特性,但是React 17版本仍然包含了一些所有React开发者需要注意到的改动。
通过这篇文章,我会帮助大家一起跟上React的脚步。
逐步更新(Gradual Updates)
React 17中主要的一个关注点是,让react升级起来更简单。release 博客中提到:
“React 17 支持渐进式的升级。当以将React从15升级到16(或者之后将16升级到17),通常我们都会一次性把整个应用做一次升级。对于大多数应用来说它工作的很好。但如果一些代码已经编写了很多年了,并且不需要求全面持续维护了,要做这类的升级会变成一项巨大的挑战。虽然在React 17之前,在同一个页面运行多个版本的react也是可行的,但是会存在一些事件处理异常的问题。”
在业界,通常开发人员非常愿意尝试框架中新的特性,但却无法随心所欲的进行新特性的尝试,因为通常比较难评估出平滑升级软件且不产生任何新的bug的时间。在React 17的提供了一个有趣的新升级的工作模式,它可以让开发这针对已存在的代码仍然使用旧版本的React的基础上,同时使用新的版本编写新的代码和功能。
在同一个页面是使用不通版本的框架会有一个优先级的问题。比如:Angular团队允许你同时运行Angular1 和Angular 2+的版本。通过那个不存在的搜索引擎,你也可以查询到3800万条关于“同时运行angular1和angular2”的记录。所以,这样的需求是非常常见的。
尽管如此,React团队也明确指出,只有在特殊且必要时才应使用此中多版本公共使用的工作模式。
“对于大多数的Apps来说,统一升级所有依赖的版本依旧是最佳的解决方案。加载两个版本的React——即使其中一个版本是按需加载的,它仍然不是一个理想的方案”
如果你对这个工作模式感兴趣,请查询《sample app the React team shipped with the release》这个demo。它很好把老旧的代码和最新版本的代码结合在一起,目录结构标注了哪些是老旧遗留代码,哪些是新的代码,以及哪些是他们共享的部分。
事件代理机制的改变
第二个React 17重要的改变是内部的事件代理的工作机制。博文中说到:
“在React 17中, React 不再把事件挂载到document上. 更改的是,他会把事件挂载到根节点上你React绑定的跟节点上”
这个改动对于开发者来说没有任何影响,这是React内部的实现改变,并不会影响到任何调用的API。但正因为新版本的React更好的进行了隔离——或者说框架不再依赖于它跟节点之外的DOM元素,它的改变能让React更好的和其他框架一起运行。博文中提到
“这个改动让React更容易和其他框架一起集成起来。比如如果你外部的代码是使用jQuery编写的,而新的代码是使用React编写的,在使用React的代码中使用stopPropagation就可以组织它被外层的jQuery代码捕获到,这和我们想要达到的效果是一样的。”
这对于一些基于DOM的框架,如:jquery来说,把事件都混到document里是很常见的。现在React不会把事件注册到跟节点之外的,这样也让React会更安全的集成到一些老旧的app中去(当你一些老旧的代码使用了一系列过时的js工具库,这也让你更容易移除它们)
更好的调用堆栈跟踪
引起我注意的最后的一个变化是React渲染堆栈跟踪的方式。博文中说:
“在 React 17中, 组件的堆栈使用和原生JavaScript堆栈不同的机制生成. 这让你在生产版本中,可以获得更完整、符号化的组件调用堆栈.”
它们做到这样的方式有点疯狂
“React实现这一点的方式有点不合常规。 目前,浏览器不提供获取函数堆栈帧(源文件和位置)的方法。因此,当React捕获到异常时,它将在可能的情况下,通过从外层每个组件内部抛出(并捕获)一个临时错误来重建组件堆栈。”
但它确实可用,并且它针对production态的调试非常有用。比如,假设你使用如下的代码捕获应用的异常。
import React from 'react';
import { ErrorBoundary } from "react-error-boundary";
function ErrorFallback({ componentStack }) {
console.log(componentStack);
return (
<p style={{ color: "red" }}>Something went wrong!</p>
)
}
function App() {
return (
<ErrorBoundary FallbackComponent={ErrorFallback}>
{ /* Your app */ }
</ErrorBoundary>
);
}
这里的 `ErrorFallback` 使用了 React的 [error boundaries](https://reactjs.org/docs/error-boundaries.html) API, 每当组件发生异常的时候都会输出组件的错误堆栈 `componentStack` . 在React 16中, 上述的输出的错误堆栈在Production版本对于我们定位问题,没有太大的帮助.<br />比如,当代码对null值调用toUpperCase()方法的时候,会输出:
in s
in i
in u
in StrictMode App.js:6:10
在升级到React 17版本之后,调用堆栈会关联到每个组件在源码中的位置:
s@http://localhost:8000/static/js/main.15f3e38c.chunk.js:1:470
i@http://localhost:8000/static/js/2.477a9a31.chunk.js:2:1611
u
它本身并不是特别有用,除非你知道什么是2.477a9a31.chunk.js:2:1611 代表的是什么。但是如果您将这些堆栈跟踪与source maps和以及一些错误符号化的工具(如Sentry)结合起来,您将能够获得生产错误的完整组件堆栈跟踪。
如果有一些生产环境的错误,是很难重现和调试的,那么它绝对是一个让你值得使用的特性。
React的未来
总的来说,React 17的目标是使React更稳定、更易于升级,但这对React的未来意味着什么?博文中提到:
“我们正在积极开发新的React功能,但它们不属于本次发布的一部分。React 17的发布是我们战略的一个关键部分,目的是为了不让任何人被落下。”
想一想React hook。尽管hooks并不是一个完全不兼容的特性改变,但是它导致了相关的在线文档和教程分成两种:一种使用hook,另一种不使用hook。在Progress中,我亲身感受到了这种挣扎,因为我们的一些KendoReact用户更喜欢使用hook查看文档,一些用户更喜欢查看带有类组件的文档,以及有些人希望两者都可用。显然,我们希望让所有用户都满意,但是我们只能支持的React版本和各种api里有限的组合。
考虑到这一点,我确信React团队花了一个版本来关注普通React开发人员的开发体验,并且正在努力改进升级路径。希望这将使未来版本React的功能更易于每个人使用。