浅谈React中的元素、组件、实例和节点


Posted in Javascript onFebruary 27, 2018

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

React 中的元素、组件、实例和节点,是React中关系密切的4个概念,也是很容易让React 初学者迷惑的4个概念。现在,老干部就来详细地介绍这4个概念,以及它们之间的联系和区别,满足喜欢咬文嚼字、刨根问底的同学(老干部就是其中一员)的好奇心。

元素 (Element)

React 元素其实就是一个简单JavaScript对象,一个React 元素和界面上的一部分DOM对应,描述了这部分DOM的结构及渲染效果。一般我们通过JSX语法创建React 元素,例如:

const element = <h1 className='greeting'>Hello, world</h1>;

element是一个React 元素。在编译环节,JSX 语法会被编译成对React.createElement()的调用,从这个函数名上也可以看出,JSX语法返回的是一个React 元素。上面的例子编译后的结果为:

const element = React.createElement(
 'h1',
 {className: 'greeting'},
 'Hello, world!'
);

最终,element的值是类似下面的一个简单JavaScript对象:

const element = {
 type: 'h1',
 props: {
  className: 'greeting',
  children: 'Hello, world'
 }
}

React 元素可以分为两类:DOM类型的元素和组件类型的元素。DOM类型的元素使用像h1、div、p等DOM节点创建React 元素,前面的例子就是一个DOM类型的元素;组件类型的元素使用React 组件创建React 元素,例如:

const buttonElement = <Button color='red'>OK</Button>;

buttonElement就是一个组件类型的元素,它的值是:

const buttonElement = {
 type: 'Button',
 props: {
  color: 'red',
  children: 'OK'
 }
}

对于DOM类型的元素,因为和页面的DOM节点直接对应,所以React知道如何进行渲染。但是对于组件类型的元素,如buttonElement,React是无法直接知道应该把buttonElement渲染成哪种结构的页面DOM,这时就需要组件自身提供React能够识别的DOM节点信息,具体实现方式在介绍组件时会详细介绍。

有了React 元素,我们应该如何使用它呢?其实,绝大多数情况下,我们都不会直接使用React 元素,React 内部会自动根据React 元素,渲染出最终的页面DOM。更确切地说,React元素描述的是React虚拟DOM的结构,React会根据虚拟DOM渲染出页面的真实DOM。

组件 (Component)

React 组件,应该是大家最熟悉的React中的概念。React通过组件的思想,将界面拆分成一个个可以复用的模块,每一个模块就是一个React 组件。一个React 应用由若干组件组合而成,一个复杂组件也可以由若干简单组件组合而成。

React组件和React元素关系密切,React组件最核心的作用是返回React元素。这里你也许会有疑问:React元素不应该是由React.createElement() 返回的吗?但React.createElement()的调用本身也是需要有“人”负责的,React组件正是这个“责任人”。React组件负责调用React.createElement(),返回React元素,供React内部将其渲染成最终的页面DOM。

既然组件的核心作用是返回React元素,那么最简单的组件就是一个返回React元素的函数:

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

Welcome是一个用函数定义的组件。如果使用类(class)定义组件,返回React元素的工作具体就由组件的render方法承担,例如:

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

其实,使用类定义的组件,render方法是唯一必需的方法,其他组件的生命周期方法都只不过是为render服务而已,都不是必需的。

现在来考虑下面这个例子:

class Home extends React.Component {
 render() {
  return (
   <div>
    <Welcome name='老干部' />
    <p>Anything you like</p>
   </div>
  )
 }
}

Home 组件使用了Welcome组件,返回的React元素为:

{
 type: 'div',
 props: {
  children: [
   {
    type: 'Welcome',
    props: {
     name: '老干部'
    }
   },
   {
    type: 'p',
    props: {
     children: 'Anything you like'
    }
   },
  ]
 }
}

对于这个结构,React 知道如何渲染type = 'div' 和 type = 'p' 的节点,但不知道如何渲染type='Welcome'的节点,当React 发现Welcome 是一个React 组件时(判断依据是Welcome首字母为大写),会根据Welcome组件返回的React 元素决定如何渲染Welcome节点。Welcome组件返回的React 元素为:

{
 type: 'h1',
 props: {
  children: 'Hello, 老干部'
 }
}

这个结构中只包含DOM节点,React是知道如何渲染的。如果这个结构中还包含其他组件节点,React 会重复上面的过程,继续解析对应组件返回的React 元素,直到返回的React 元素中只包含DOM节点为止。这样的递归过程,让React 获取到页面的完整DOM结构信息,渲染的工作自然就水到渠成了。

另外,如果仔细思考的话,可以发现,React 组件的复用,本质上是为了复用这个组件返回的React 元素,React 元素是React 应用的最基础组成单位。

实例 (Instance)

这里的实例特指React组件的实例。React 组件是一个函数或类,实际工作时,发挥作用的是React 组件的实例对象。只有组件实例化后,每一个组件实例才有了自己的props和state,才持有对它的DOM节点和子组件实例的引用。在传统的面向对象的开发方式中,实例化的工作是由开发者自己手动完成的,但在React中,组件的实例化工作是由React自动完成的,组件实例也是直接由React管理的。换句话说,开发者完全不必关心组件实例的创建、更新和销毁。

节点 (Node)

在使用PropTypes校验组件属性时,有这样一种类型:

MyComponent.propTypes = { 
 optionalNode: PropTypes.node,
}

PropTypes.node又是什么类型呢?这表明optionalNode是一个React 节点。React 节点是指可以被React渲染的数据类型,包括数字、字符串、React 元素,或者是一个包含这些类型数据的数组。例如:

// 数字类型的节点
function MyComponent(props) {
 return 1;
}

// 字符串类型的节点
function MyComponent(props) {
 return 'MyComponent';
}

// React元素类型的节点
function MyComponent(props) {
 return <div>React Element</div>;
}

// 数组类型的节点,数组的元素只能是其他合法的React节点
function MyComponent(props) {
 const element = <div>React Element</div>;
 const arr = [1, 'MyComponent', element];
 return arr;
}

// 错误,不是合法的React节点
function MyComponent(props) {
 const obj = { a : 1}
 return obj;
}

最后总结一下,React 元素和组件的概念最重要,也最容易混淆;React 组件实例的概念大家了解即可,几乎使用不到;React 节点有一定使用场景,但看过本文后应该也就不存在理解问题了。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
List Information About the Binary Files Used by an Application
Jun 18 Javascript
JavaScript获取各大浏览器信息图示
Nov 20 Javascript
JS组件Bootstrap导航条使用方法详解
Apr 29 Javascript
总结JavaScript的正则与其他语言的不同之处
Aug 25 Javascript
原生js实现网页顶部自动下拉/收缩广告效果
Jan 20 Javascript
vue2.0父子组件及非父子组件之间的通信方法
Jan 21 Javascript
详解node-ccap模块生成captcha验证码
Jul 01 Javascript
详谈JS中数组的迭代方法和归并方法
Aug 11 Javascript
Vue封装一个简单轻量的上传文件组件的示例
Mar 21 Javascript
小程序实现列表多个批量倒计时
Jan 29 Javascript
小程序组件之自定义顶部导航实例
Jun 12 Javascript
解决layer.confirm选择完之后消息框不消失的问题
Sep 16 Javascript
AngularJS动态添加数据并删除的实例
Feb 27 #Javascript
JS严格模式知识点总结
Feb 27 #Javascript
总结js函数相关知识点
Feb 27 #Javascript
详解jQuery中的isPlainObject()使用方法
Feb 27 #jQuery
详解Vue Elememt-UI构建管理后台
Feb 27 #Javascript
详解react-native WebView 返回处理(非回调方法可解决)
Feb 27 #Javascript
Vue2.5通过json文件读取数据的方法
Feb 27 #Javascript
You might like
一些使用频率比较高的php函数
2008/10/03 PHP
php中判断文件空目录是否有读写权限的函数代码
2012/08/07 PHP
单台服务器的PHP进程之间实现共享内存的方法
2014/06/13 PHP
PHP使用CURL_MULTI实现多线程采集的例子
2014/07/29 PHP
php防止sql注入简单分析
2015/03/18 PHP
gearman管理工具GearmanManager的安装与php使用方法示例
2020/02/27 PHP
javascript管中窥豹 形参与实参浅析
2011/12/17 Javascript
JS连接SQL数据库与ACCESS数据库的方法实例
2013/11/21 Javascript
javascript新建标签,判断键盘输入,以及判断焦点(示例代码)
2013/11/25 Javascript
JavaScript数组的一些奇葩行为
2016/01/25 Javascript
jQuery实现可以控制图片旋转角度效果(附demo源码下载)
2016/01/27 Javascript
JavaScript trim 实现去除字符串首尾指定字符的简单方法
2016/12/27 Javascript
微信小程序开发之麦克风动画 帧动画 放大 淡出
2017/04/18 Javascript
javascript基本常用排序算法解析
2017/09/27 Javascript
iview日期控件,双向绑定日期格式的方法
2018/03/15 Javascript
node实现基于token的身份验证
2018/04/09 Javascript
Vue.directive使用注意(小结)
2018/08/31 Javascript
vue基础之事件v-onclick=&quot;函数&quot;用法示例
2019/03/11 Javascript
Node.js对MongoDB进行增删改查操作的实例代码
2019/04/18 Javascript
js实现星星打分效果
2020/07/05 Javascript
vue 获取到数据但却渲染不到页面上的解决方法
2020/11/19 Vue.js
Python中SOAP项目的介绍及其在web开发中的应用
2015/04/14 Python
Python 模块EasyGui详细介绍
2017/02/19 Python
Python 列表(List) 的三种遍历方法实例 详解
2017/04/15 Python
解决Python3.5+OpenCV3.2读取图像的问题
2018/12/05 Python
pycharm中使用anaconda部署python环境的方法步骤
2018/12/19 Python
Python lambda表达式用法实例分析
2018/12/25 Python
Python爬虫 批量爬取下载抖音视频代码实例
2019/08/16 Python
如何在Anaconda中打开python自带idle
2020/09/21 Python
购买限量版收藏品、珠宝和礼品:Bradford Exchange
2016/09/23 全球购物
Why we need EJB
2016/10/20 面试题
解决python 输出到csv 出现多空行的情况
2021/03/24 Python
一年级数学教学反思
2014/02/01 职场文书
药品营销专业毕业生自荐信
2014/07/02 职场文书
2015新年寄语大全
2014/12/08 职场文书
python 实现德洛内三角剖分的操作
2021/04/22 Python