当前位置: 首页 > news >正文

React 深入系列2:组件分类

为什么80%的码农都做不了架构师?>>>   hot3.png

文:徐超,《React进阶之路》作者

授权发布,转载请注明作者及出处


React 深入系列2:组件分类

React 深入系列,深入讲解了React中的重点概念、特性和模式等,旨在帮助大家加深对React的理解,以及在项目中更加灵活地使用React。

React 组件有很多种分类方式,常见的分类方式有函数组件和类组件,无状态组件和有状态组件,展示型组件和容器型组件。好吧,这又是一篇咬文嚼字的文章。但是,真正把这几组概念咬清楚、嚼明白后,对于页面的组件划分、组件之间的解耦是大有裨益的。

函数组件和类组件

函数组件(Functional Component )和类组件(Class Component),划分依据是根据组件的定义方式。函数组件使用函数定义组件,类组件使用ES6 class定义组件。下面是函数组件和类组件的简单示例:

// 函数组件
function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

// 类组件
class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

上面的两种写法是等价的,但函数组件的写法要比类组件简洁,不过类组件比函数组件功能更加强大。类组件可以维护自身的状态变量,即组件的state,类组件还有不同的生命周期方法,可以让开发者能够在组件的不同阶段(挂载、更新、卸载),对组件做更多的控制。

类组件有这么多优点,是不是我们在开发中应该首选使用类组件呢?其实不然。函数组件更加专注和单一,承担的职责也更加清晰,它只是一个返回React 元素的函数,只关注对应UI的展现。函数组件接收外部传入的props,返回对应UI的DOM描述,仅此而已。当然,如上面例子所示,使用只包含一个render方法的类组件,可以实现和函数组件相同的效果。但函数组件的使用可以从思想上迫使你在设计组件时多做思考,更加关注逻辑和显示的分离,设计出更加合理的页面上组件树的结构。实际操作上,当一个组件不需要管理自身状态时,可以把它设计成函数组件,当你有足够的理由发现它需要“升级”为类组件时,再把它改造为类组件。因为函数组件“升级”为类组件是有一定成本的,这样就会要求你做这个改造前更认真地思考其合理性,而不是仅仅为了一时的方便就使用类组件。

无状态组件和有状态组件

无状态组件(Stateless Component )和有状态组件(Stateful Component),划分依据是根据组件内部是否维护state。无状态组件内部不使用state,只根据外部组件传入的props返回待渲染的React 元素。有状态组件内部使用state,维护自身状态的变化,有状态组件根据外部组件传入的props和自身的state,共同决定最终返回的React 元素。

很容易知道,函数组件一定是无状态组件,类组件则既可以充当无状态组件,也可以充当有状态组件。但如上文所述,当一个组件不需要管理自身状态时,也就是无状态组件,应该优先设计为函数组件。

展示型组件和容器型组件

展示型组件(Presentational Component)和容器型组件(Container Component),划分依据是根据组件的职责。

展示型组件的职责是:组件UI长成什么样。展示型组件不关心组件使用的数据是如何获取的,以及组件数据应该如何修改,它只需要知道有了这些数据后,组件UI是什么样子的即可。外部组件通过props传递给展示型组件所需的数据和修改这些数据的回调函数,展示型组件只是它们的使用者。展示型组件一般是无状态组件,不需要state,因为展示型组件不需要管理数据,但当展示型组件需要管理自身的UI状态时,例如控制组件内部弹框的显示与隐藏,是可以使用state的,这时的state属于UI state。既然大部分情况下展示型组件不需要state,应该优先考虑使用函数组件实现展示型组件。

容器型组件的职责是:组件数据如何工作。容器型组件需要知道如何获取子组件所需数据,以及这些数据的处理逻辑,并把数据和逻辑通过props提供给子组件使用。容器型组件一般是有状态组件,因为它们需要管理页面所需数据。

例如,下面的例子中,UserListContainer是一个容器型组件,它获取用户列表数据,然后把用户列表数据传递给展示型组件UserList,由UserList负责UI的展现。

class UserListContainer extends React.Component{
  constructor(props){
    super(props);
    this.state = {
      users: []
    }
  }
  
  componentDidMount() {
    var that = this;
    fetch('/path/to/user-api').then(function(response) {
      response.json().then(function(data) {
        that.setState({users: data})
      });
    });
  }

  render() {
    return (
      <UserList users={this.state.users} />
    )
  }
}

function UserList(props) {
  return (
    <div>
      <ul className="user-list">
        {props.users.map(function(user) {
          return (
            <li key={user.id}>
              <span>{user.name}</span>
            </li>
          );
        })}
      </ul>
    </div>
  )  
}

展示型组件和容器型组件是可以互相嵌套的,展示型组件的子组件既可以包含展示型组件,也可以包含容器型组件,容器型组件也是如此。例如,当一个容器型组件承担的数据管理工作过于复杂时,可以在它的子组件中定义新的容器型组件,由新组件分担数据的管理。展示型组件和容器型组件的划分完全取决于组件所做的事情。

总结

通过上面的介绍,可以发现这三组概念有很多重叠部分。这三组概念都体现了关注点分离的思想:UI展现和数据逻辑的分离。函数组件、无状态组件和展示型组件主要关注UI展现,类组件、有状态组件和容器型组件主要关注数据逻辑。但由于它们的划分依据不同,它们并非完全等价的概念。它们之间的关联关系可以归纳为:函数组件一定是无状态组件,展示型组件一般是无状态组件;类组件既可以是有状态组件,又可以是无状态组件,容器型组件一般是有状态组件。

下篇预告:

React 深入系列3:State 和 Props


新书推荐《React进阶之路》

作者:徐超

毕业于浙江大学,硕士,资深前端工程师,长期就职于能源物联网公司远景智能。8年软件开发经验,熟悉大前端技术,拥有丰富的Web前端和移动端开发经验,尤其对React技术栈和移动Hybrid开发技术有深入的理解和实践经验。



美团点评广告平台大前端团队招收2019\2020年前端实习生

有意者邮件:yao.zhou@meituan.com

转载于:https://my.oschina.net/ikcamp/blog/1791077

相关文章:

  • 深信服防火墙设备故障机的更换方法
  • [日常] Go语言圣经--作用域,基础数据类型,整型
  • Elasticsearch 父子关系维护和检索案例分享
  • Wamp集成环境 添加PHP的新版本
  • 建立私有CA及Nginx绑定SSL加密
  • Vue:替换/合并现有的特性
  • 杨老师课堂之JavaScript案例之自动切换轮播图片
  • 关于Vue.js面试题汇总
  • Android两次后退键退出
  • 敏捷开发与瀑布式开发的区别
  • [转] Webpack的devtool和source maps
  • 数据中心资源向AWS迁移的四大挑战
  • 区块链概况:什么是区块链
  • 二进制数字系统
  • js正则,这点儿就够用了
  • 【跃迁之路】【669天】程序员高效学习方法论探索系列(实验阶段426-2018.12.13)...
  • CSS3 变换
  • JavaScript中的对象个人分享
  • Java超时控制的实现
  • JAVA之继承和多态
  • js对象的深浅拷贝
  • k个最大的数及变种小结
  • Python_网络编程
  • Quartz初级教程
  • vagrant 添加本地 box 安装 laravel homestead
  • Vue实战(四)登录/注册页的实现
  • 得到一个数组中任意X个元素的所有组合 即C(n,m)
  • 机器人定位导航技术 激光SLAM与视觉SLAM谁更胜一筹?
  • ------- 计算机网络基础
  • 区块链技术特点之去中心化特性
  • 我的面试准备过程--容器(更新中)
  • 主流的CSS水平和垂直居中技术大全
  • 阿里云重庆大学大数据训练营落地分享
  • 昨天1024程序员节,我故意写了个死循环~
  • # Python csv、xlsx、json、二进制(MP3) 文件读写基本使用
  • (4)事件处理——(6)给.ready()回调函数传递一个参数(Passing an argument to the .ready() callback)...
  • (力扣记录)1448. 统计二叉树中好节点的数目
  • (十五)devops持续集成开发——jenkins流水线构建策略配置及触发器的使用
  • (转)h264中avc和flv数据的解析
  • .bat批处理(一):@echo off
  • .helper勒索病毒的最新威胁:如何恢复您的数据?
  • .net core 客户端缓存、服务器端响应缓存、服务器内存缓存
  • .NET开源项目介绍及资源推荐:数据持久层 (微软MVP写作)
  • .net企业级架构实战之7——Spring.net整合Asp.net mvc
  • @Mapper作用
  • @vue/cli脚手架
  • [1525]字符统计2 (哈希)SDUT
  • [8-27]正则表达式、扩展表达式以及相关实战
  • [Android实例] 保持屏幕长亮的两种方法 [转]
  • [AX]AX2012开发新特性-禁止表或者表字段
  • [c++] C++多态(虚函数和虚继承)
  • [C++]类和对象(中)
  • [C++数据结构](22)哈希表与unordered_set,unordered_map实现
  • [cocos2d-x]关于CC_CALLBACK
  • [elastic 8.x]java客户端连接elasticsearch与操作索引与文档