React中函数组件与类组件

与React类组件相比,React函数式组件究竟有何不同呢?之前听到最典型的回答就是:类组件提供了更多特性(如:State)。当然随着 hooks 出现,事实早非如此

举个例子

相信很多人都看过一个非常经典的例子:

这是 react 函数组件的实现

function ProfilePage(props) {
  const showMessage = () => {
    alert('您已成功关注' + props.user);
  };
// 用setTimeout 模拟网络请求
  const handleClick = () => {
    setTimeout(showMessage, 3000);
  };
  return (
    <button onClick={handleClick}>Follow</button>
  );
}

这是 React 类组件的实现

class ProfilePage extends React.Component {
  showMessage = () => {
    alert('您已成功关注' + this.props.user);
  };
  handleClick = () => {
    setTimeout(this.showMessage, 3000);
  };
  render() {
    return <button onClick={this.handleClick}>Follow</button>;
  }
}

不知道您是否能看出两个实现之间的差异。你可能就认为这是一个很简单的代码重构,但你是否发现其中的问题所在。

UI界面大致如下
在这里插入图片描述
当我选择了小红进入小红的主页后,关注了她。由于网络延迟,3s内我又选择了小明的主页。而代码上都触发了 showMessage ()。但是这时候你会发现,两个组件呈现的效果是不一样的。
如果你有兴趣可以在 SandBox 中尝试。

结果:

  • 当使用 函数式组件 实现的 ProfilePage, 当前账号是 小明 时点击 关注ta 按钮,然后立马切换当前账号到 小红,弹出的文本将依旧是 小明
  • 当使用 类组件 实现的 ProfilePage, 弹出的文本将是 小红

原因分析

在这里例子中,我们认为函数式组件实现的是期待的效果,因为关注的事件行为不应该因为切换主页的行为而发生改变。

为什么例子中类组件会出现这样的现象呢?

我们看看类组件中的 showMessage 方法中,这个类方法从 this.props.user中读取数据。在 ReactProps 是不可变(immutable)的,所以他们永远不会改变。然而,this是,而且永远是,可变(mutable)的。事实上,这就是类组件 this 存在的意义。React本身会随着时间的推移而改变,以便你可以在渲染方法以及生命周期方法中得到最新的实例。

所以如果在请求已经发出的情况下我们的组件进行了重新渲染,this.props 将会改变。showMessage方法从一个“过于新”的 props 中得到了user
这暴露了一个关于用户界面性质的一个有趣观察。如果我们说UI在概念上是当前应用状态的一个函数,那么事件处理程序则是渲染结果的一部分 —— 就像视觉输出一样。我们的事件处理程序“属于”一个拥有特定 propsstate 的特定渲染。然而,调用一个回调函数读取 this.propstimeout 会打断这种关联。我们的 showMessage 回调并没有与任何一个特定的渲染“绑定”在一起,所以它“失去”了正确的 props。从 this 中读取数据的这种行为,切断了这种联系。

假设函数式组件不存在。我们将如何解决这个问题?

我们想要以某种方式“修复”拥有正确 props 的渲染与读取这些 propsshowMessage 回调之间的联系。在某个地方 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}>Follow</button>;
  }
}

这种方法 会起作用。然而,这种方法使得代码明显变得更加冗长,并且随着时间推移容易出错。如果我们需要的不止是一个props怎么办?如果我们还需要访问state怎么办?如果 showMessage 调用了另一个方法,然后那个方法中读取了 this.props.xxx 或者 this.state.xxx,我们又将遇到同样的问题。然后我们不得不将 this.propsthis.state 以函数参数的形式在被 showMessage 调用的每个方法中一路传递下去。这个问题并不是React所独有的 —— 你可以在任何一个将数据放入类似 this 这样的可变对象中的UI库中重现它。

总结

从组件的角度上来看,类组件和函数组件在使用方式上和呈现上是没有任何不同的,而在现代浏览器中他们的性能差异也是可以忽略不计的。
但在开发模式和思维上,两者存是存在差异的。
类组组件要是面向对象编程,主打的是继承,生命周期等核心概念等特点
函数组件主要是函数式编程,主打的是immutable,无副作用,引用透明等特点
因为两者主打的特点不一样,所以在使用场景上自然就存在一些差异了:
如果组件本身依赖生命周期,设计上也有继承的需要,那自然选择类组件会更合适一点
由于Ract hooks的推出,淡化生命周期的概念,函数组件可以完全取代函数组件,而且官方也推崇“组合优于继承”的设计概念,所以类组件的优势也在慢慢的淡出。

性能优化上,类组件主要依靠shouldComponentUpdate生命周期来阻断渲染,从而提升性能,而函数组件是依靠React.memo缓存渲染结果来提升性能。

从上手程度而言,类组件更容易上手,而从未来趋势上看,由于React Hooks 的推出,函数组件成了社区未来主推的方案。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值