函数组件性能比类组件好,但是在现代浏览器中,闭包和类的原始性能只有在极端场景下才会有明显的差别。
性能主要取决于代码的作用,而不是选择函数式还是类组件。尽管优化策略有差别,但性能差异可以忽略不计。
参考官网:()
参考作者github:(https://github.com/ryardley/hooks-perf-issues/pull/2)
而下面会重点讲述:React的函数式组件和类组件之间根本的区别: 在心智模型上。
简单的案例
函数式组件以来,它一直存在,但是经常被忽略:函数式组件捕获了渲染所用的值。(Function components capture the rendered values.)
思考这个组件:
function ProfilePage(props) { const showMessage = () => alert('你好 ' + props.user); const handleClick = () => setTimeout(showMessage, 3000); return <button onClick={handleClick}>Follow</button> }
上述组件:如果 props.user是 Dan,它会在三秒后显示 你好 Dan。
如果是类组件我们怎么写?一个简单的重构可能就象这样:
class ProfilePage extends React.Component { showMessage = () => alert('Followed ' + this.props.user); handleClick = () => setTimeout(this.showMessage, 3000); render() { return <button onClick={this.handleClick}>Follow</button>; } }
通常我们认为,这两个代码片段是等效的。人们经常在这两种模式中自由的重构代码,但是很少注意到它们的含义:
我们通过 React 应用程序中的一个常见错误来说明其中的不同。
我们添加一个父组件,用一个下拉框来更改传递给子组件(ProfilePage),的 props.user,实例地址:(https://codesandbox.io/s/pjqnl16lm7)
按步骤完成以下操作:
点击 其中某一个 Follow 按钮。
在3秒内 切换 选中的账号。
查看 弹出的文本。
这时会得到一个奇怪的结果:
当使用 函数式组件 实现的 ProfilePage, 当前账号是 Dan 时点击 Follow 按钮,然后立马切换当前账号到 Sophie,弹出的文本将依旧是 'Followed Dan'。
当使用 类组件 实现的 ProfilePage, 弹出的文本将是 'Followed Sophie':
在这个例子中,函数组件是正确的。 如果我关注一个人,然后导航到另一个人的账号,我的组件不应该混淆我关注了谁。 ,而类组件的实现很明显是错误的。
案例解析
所以为什么我们的例子中类组件会有这样的表现? 让我们仔细看看类组件中的 showMessage 方法:
showMessage = () => { alert('Followed ' + this.props.user); };
这个类方法从 this.props.user 中读取数据。
在 React 中 Props 是 不可变(immutable)的,所以他们永远不会改变。
而 this 是而且永远是 可变(mutable)的。**
这也是类组件 this 存在的意义:能在渲染方法以及生命周期方法中得到最新的实例。
所以如果在请求已经发出的情况下我们的组件进行了重新渲染, this.props将会改变。 showMessage方法从一个"过于新"的 props中得到了 user。
从 this 中读取数据的这种行为,调用一个回调函数读取 this.props 的 timeout 会让 showMessage 回调并没有与任何一个特定的渲染"绑定"在一起,所以它"失去"了正确的 props。。
如何用类组件解决上述BUG?(假设函数式组件不存在)
我们想要以某种方式"修复"拥有正确 props 的渲染与读取这些 props 的 showMessage回调之间的联系。在某个地方 props被弄丢了。
方法一:在调用事件之前读取 this.props,然后显式地传递到timeout回调函数中:
class ProfilePage extends React.Component { showMessage = (user) => alert('Followed ' + user); handleClick = () => { const {user} = this.props; setTimeout(() => this.showMessage(user), 3000); }; render() { return <button onClick={this.handleClick}>Followbutton>; } }
然而,这种方法使得代码明显变得更加冗长。如果我们需要的不止是一个props 该怎么办? 如果我们还需要访问state 又该怎么办? 如果 showMessage 调用了另一个方法,然后那个方法中读取了 this.props.something 或者 this.state.something ,我们又将遇到同样的问题。然后我们不得不将 this.props和 this.state以函数参数的形式在被 showMessage调用的每个方法中一路传递下去。
这样的做法破坏了类提供的工程学。同时这也很难让人去记住传递的变量或者强制执行,这也是为什么人们总是在解决bugs。
这个问题可以在任何一个将数据放入类似 this 这样的可变对象中的UI库中重现它(不仅只存在 React 中)
方法二:如果我们能利用JavaScript闭包的话问题将迎刃而解。*
如果你在一次特定的渲染中捕获那一次渲染所用的props或者state,你会发现他们总是会保持一致,就如同你的预期那样: