React Component和React Element之间的区别


75

React Component和React Element有什么区别?该文档提到了两者,但没有详细介绍,某些方法需要组件,其他元素...

Answers:


70

这里涉及三种相关的事物,它们都有自己的名称:

  • 组件
  • 组件实例
  • 元素

这有点令人惊讶,因为如果您习惯于其他UI框架,那么您可能会期望只有两种,大致对应于类(如Widget)和实例(如new Widget())。在React中不是这样。组件实例与元素一样,它们之间也不存在一对一的关系。为了说明这一点,请考虑以下代码:

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';

class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    console.log('This is a component instance:', this);
  }

  render() {
    const another_element = <div>Hello, World!</div>;
    console.log('This is also an element:', another_element);
    return another_element;
  }
}

console.log('This is a component:', MyComponent)

const element = <MyComponent/>;

console.log('This is an element:', element);

ReactDOM.render(
  element,
  document.getElementById('root')
);

在上面的代码中:

  • MyComponent类本身)是一个Component
  • element是一个元素。这不是的实例MyComponent;相反,它只是要创建的组件实例的描述。这是一个对象keypropsreftype属性。在这里,key并且refnullprops是一个空的对象,typeMyComponent
  • 呈现时创建一个get实例MyComponent(并在上面的示例中,从其构造函数记录其自身)element
  • another_element也是一个元素,并有keyrefpropstype刚才一样的属性element那样-但这次的值type是字符串"div"

我建议阅读React团队的博客文章React Components,Elements和Instances中详细探讨React具有这三个截然不同的概念的设计原因。

最后,应该注意的是,尽管官方文档严格使用术语“组件”来指代函数或类,而“组件实例”来指代实例,但其他来源并不一定要遵循该术语。阅读GitHub上的Stack Overflow答案或讨论时,您应该期望看到“组件”(错误地)表示“组件实例”。


总而言之,可以使用Component创建一个实例,并在渲染该实例时创建一个Element。
Revanth Kumar '18

1
关于React Node有什么想法吗?
Revanth Kumar,

1
组件实例和元素之间不是一对一的关系吗?您是否建议一个元素可以描述多个组件实例?还是多个元素可以描述同一个组件实例?否则,根据定义它们是一对一相关的。
安迪

2
@Andy多个元素可以描述相同的组件实例。(不是同时发生的,而是在该实例的整个生命周期中。如果您有一个始终<Child/>从其render()方法返回的父组件,则每次render()调用都会返回一个新元素,但是Child可以重复使用一个已经存在的组件以实现元素。)
Mark Amery

@Andy ...而且,虽然可能不那么常见,但多个组件也可以由同一元素描述-甚至同时描述。您可以将一个元素存储在变量中-例如,el = <SomeComponent>然后将一个元素多次嵌入到render()方法返回的某个大型JSX表达式中(即do <div>{el}{el}{el}{el}</div>)。
Mark Amery

26

为了进一步阐述答案,React Element没有任何方法,并且原型上也没有。这也使它们快速。

“ ReactElement是DOM元素的轻量,无状态,不可变的虚拟表示形式”-React术语表

react组件render()函数返回幕后的react元素DOM树(这是虚拟DOM btw)。这里涉及一些复杂的映射和差异逻辑,但是基本上这些React元素映射到DOM元素。

您还可以直接创建一个Element,React.createElement(arg)其中arg可以是html标签名称,也可以是React Component类。


我不确定ReactElementVDOM本身就是...但是我不是这个主题的专家,这只是预感。以我的理解,Element仍然需要对VDOM呈现一个反应,然后将其与“先前的” VDOM进行比较。我认为user6445533答案也是如此。对 ?我不是专家,但经过一些思考后,常识告诉我这是唯一有意义的解释……但是我的直觉可能是错误的。
jhegedus

这个答案来自很久以前(2015年),VDOM结构及其访问方式已经发生了变化。我认为我必须对其进行更新,但是从那时起我就再也没有使用过React,因此我必须先进行研究。如果有人想对此进行评论或发表评论,我将非常高兴!:)
kunl

22

反应元素

React Element只是一个普通的旧JavaScript,Object没有自己的方法。它本质上具有四个属性:

  • typeString表示HTML标签或引用React组件的引用
  • keyString用于唯一标识React元素
  • ref,用于访问基础DOM节点或React Component Instance的引用)
  • props(属性Object

React元素不是React组件的实例。这只是type要创建的React Component实例(或取决于HTML标签)的外观的简化“描述” 。

描述React组件的React元素不知道最终将其渲染到哪个DOM节点-该关联被抽象化并将在渲染时解析。

React Elements可能包含子元素,因此能够形成代表虚拟DOM树的元素树。

React组件和React组件实例

自定义React组件是React.createClass通过扩展或通过扩展创建的React.Component(ES2015)。如果实例化一个React Component,则它期望aprops Object并返回一个实例,该实例称为React Component Instance。

React组件可以包含状态,并可以访问React Lifecycle方法。它必须至少有一个render方法,该方法在调用时返回一个React Element(-tree)。请注意,您永远不会自己构造React Component Instances,而是让React为您创建它。


这和Mark的答案是“好”的...他们澄清了React Element不是VDOM的观点!!!:) :) ...这是一种“提升”-有点像编程语言的抽象语法树。还是单子。简而言之。
jhegedus

8

React Elements与React Components

反应元素

  • React Element是从组件返回的内容。这个对象实际上描述了组件代表的DOM节点。
  • 对于函数组件,此元素是函数返回的对象。
  • 对于类组件,元素是组件的渲染函数返回的对象。[R
  • React元素不是我们在浏览器中看到的。它们只是内存中的对象,我们无法对其进行任何更改。
  • type除本地HTML元素外,React元素可以具有其他属性。
我们知道以下几点:
  • react元素描述了我们想要在屏幕上看到的内容。
一种更复杂的说法是:
  • React元素是DOM节点的对象表示。

_在这里进行区分很重要,因为元素不是我们在屏幕上看到的实际东西,而是对象表示就是渲染的东西。

在以下方面,React对此很有用:

  • React可以创建和销毁这些元素,而无需太多开销。JS对象是轻量级且低成本的。
  • React可以将对象与先前的对象表示形式进行比较,以查看发生了什么变化。
  • React可以更新实际的DOM,特别是它检测到的更改发生的地方。这具有一些性能优势。
我们可以使用方法创建DOM节点(也称为React元素)的对象表示createElement
const element = React.createElement(
  'div',
  {id: 'login-btn'},
  'Login'
  )
这里createElement接受三个论点
  1. 标签名称(例如div,span等)
  2. 我们希望元素具有的任何属性
  3. 元素子元素的内容(例如,显示为Login
createElement调用返回一个对象
{
  type: 'div',
  props: {
    children: 'Login',
    id: 'login-btn'
  }
}
当使用渲染到DOM时ReactDOM.render,我们将有一个新的DOM节点,如下所示:
<div id='login-btn'>Login</div>

晕!

Huzzah

通常,React是从“组件优先”的方法讲授的,但是首先要了解“元素”才能平稳过渡到组件。

反应组件

组件是可以选择接受输入并返回React元素的函数或类。

  • React Component是一个模板。蓝图。全局定义。这可以是一个函数或一个类(带有渲染函数)。

  • 如果react将一个类或函数作为第一个参数,它将检查是否呈现了该元素,并给出相应的支持,并将继续执行此操作,直到不再createElement有将类或函数作为第一个参数的调用为止。

  • 当React看到一个具有函数或类类型的元素时,它会与该组件进行协商,以在给定相应的道具的情况下知道它应该返回哪个元素。

  • 在此过程结束时,React将具有DOM树的完整对象表示。这整个过程在React中称为对帐,每次都会触发setState或被ReactDOM.render调用。

基于类的组件

类语法是定义React组件的最常用方法之一。尽管比功能语法更冗长,但它以生命周期挂钩的形式提供了更多控制。

  • 我们可以渲染同一组件的许多实例。
  • 实例是在基于类的组件内部使用的“ this”关键字。
  • 不是手动创建的,而是位于React内存中的某个位置。

创建一个类组件

// MyComponent.js
import React, { Component } from 'react';

class MyComponent extends Component {
  render() {
    return (
      <div>This is my component.</div>
    );
  }
}

export default MyComponent;

在其他任何组件中使用

// MyOtherComponent.js
import React, { Component } from 'react';
import MyComponent from './MyComponent';

class MyOtherComponent extends Component {
  render() {
    return (
      <div>
        <div>This is my other component.</div>
        <MyComponent />
      </div>
    );
  }
}

export default MyOtherComponent;

使用道具

<MyComponent myProp="This is passed as a prop." />

道具可以通过 this.props

class MyComponent extends Component {
  render() {
    const {myProp} = this.props;
    return (
      <div>{myProp}</div>
    );
  }
}

使用状态

class MyComponent extends Component {
  render() {
    const {myState} = this.state || {};
    const message = `The current state is ${myState}.`;
    return (
      <div>{message}</div>
    );
  }
}

使用生命周期挂钩

class MyComponent extends Component {
  // Executes after the component is rendered for the first time
  componentDidMount() {
    this.setState({myState: 'Florida'});
  }

  render() {
    const {myState} = this.state || {};
    const message = `The current state is ${myState}.`;
    return (
      <div>{message}</div>
    );
  }
}
基于功能的组件
  • 没有实例。
  • 可以多次渲染,但是React不会将本地实例与每个渲染关联。
  • React使用功能的调用来确定要为功能呈现的DOM元素。

createElement

function Button ({ addFriend }) {
  return React.createElement(
    "button",
    { onClick: addFriend },
    "Add Friend"
  )
}

function User({ name, addFriend }) {
  return React.createElement(
    "div",
    null,
    React.createElement(
      "p",
      null,
      name
    ),
    React.createElement(Button, { addFriend })
  )
}

有什么createElement回报

function Button ({ addFriend }) {
  return {
    type: 'button',
    props: {
      onClick: addFriend,
      children: 'Add Friend'
    }
  }
}

function User ({ name, addFriend }) {
  return {
    type: 'div',
    props: {
      children: [
        {
          type: 'p',
          props: {
            children: name
          }
        },
        {
          type: Button,
          props: {
            addFriend
          }
        }
      ]
    }
  }
}

在这里,我们有一个Button接受onLogin输入并返回React元素的组件。

  • Button组件接收一个onLogin方法作为其属性。
  • 要将其传递给DOM的对象表示形式,我们将其作为createElement的第二个参数传递,就像对id属性所做的那样。

7

React Element-这是一个简单的对象,描述了DOM节点及其可以说的属性。它是一个不变的描述对象,您不能在其上应用任何方法。

例如-

<button class="blue"></button>

React Component-它是接受输入并返回React元素的函数或类。它必须保留对其DOM节点和子组件实例的引用。

const SignIn = () => (
  <div>
   <p>Sign In</p>
   <button>Continue</button>
   <button color='blue'>Cancel</button>
  </div>
);


3

一个元素是描述你要在DOM节点或其他部件方面出现在屏幕上什么平原对象。元素可以在其道具中包含其他元素。创建一个React元素很便宜。一旦创建了元素,就永远不会对其进行突变。React元素的对象表示如下:

const element = React.createElement(
  'div',
  {id: 'login-btn'},
  'Login'
)

上面的createElement返回如下对象:

{
  type: 'div',
  props: {
    children: 'Login',
    id: 'login-btn'
  }
}

最后,它使用ReactDOM.render如下所示呈现给DOM,

<div id='login-btn'>Login</div>

而一个组件可以以多种不同的方式来声明。它可以是带有render()方法的类。或者,在简单情况下,可以将其定义为函数。无论哪种情况,它都将props作为输入,并返回一个元素树作为输出。最后将JSX转换为createElement。

function Button ({ onLogin }) {
  return React.createElement(
    'div',
    {id: 'login-btn', onClick: onLogin},
    'Login'
  )
}

0

这是我的看法:

Element是描述如何构造VDOM的东西。它基本上是相应的“冻结”版本Component Instance

如果一切都顺利的functional component话,那就不需要额外的反应了Element。该functional component层次结构可以直接产生的虚拟域树。

reactComponent Instance层级(tree)是一个“工厂”,工厂通过馈入根端的propsComponent Instance和所有“存储”在Component Instance树中任何位置的状态来参数化。

因此,反应Element基本上是一个“抽象语法树”,该树被编译为实际的VDOM。

那么,为什么不通过使用react直接生成VDOMComponent Instances呢?这是真正的问题。

乍一看,我不明白为什么不可能这样做。因此,最有可能的答案是性能问题。

反应Element是VDOM和VDOM之间的一层抽象Component Instance,为什么对我来说尚不清楚,为什么需要这种抽象,所以很可能允许进行优化。例如,如果Element树上的某些生命周期方法无法完全渲染树,Component Instance说不需要渲染该子树,则无法完全渲染该树。

现在,在这种情况下,处理此优化的逻辑“需要”这个额外的间接层-因为信息需要存储在某个地方,不应将VDOM的某些部分与新的VDOM进行比较,甚至更多。根本不应该计算VDOM。因此,使用额外的间接层可使渲染逻辑更简单,并导致更清洁,更可维护的实现-我想。

在Haskell中,这个概念称为“解除”:

例如,Haskell中的单子就是此类举升的完美示例。

monad是对计算的一种描述,可以将其存储为值42。同样,reactElements是有关如何计算“新” VDOM的描述的基本内容。如果有人要计算它。

本演讲通过一些简单的示例来描述“额外”间接寻址的概念:

换句话说:过早的优化是万恶之源。

或:软件工程的基本定理

软件工程(FTSE)的基本定理是由安德鲁·科尼希(Andrew Koenig)提出的,用于描述Butler Lampson 1的评论,该评论 归功于已故的David J. Wheeler:2

我们可以通过引入额外的间接级别来解决任何问题。

因此,据我了解,ReactElements是一个实现细节,可以优雅地处理复杂性并允许一些优化(性能)。我不明白为什么不能摆脱这种额外的间接作用-原则上-react仍然可以“正常工作”-但是它可能会非常慢,实现和维护react“ engine”本身可能是一场噩梦。

如果我在这里缺少一些东西,请纠正我。

引用user6445533答案的重要部分:

type,代表HTML标签的字符串或引用React组件的引用

这是关键^^^^

元素不是VDOM。


-1

AReact Element是您认为是基本的html(准确地说是dom)元素。这只是不使用引起争议的jsx格式创建元素的一种方法。

React Component您可以将A视为对象。它具有其方法,支持React lifecycles并且通常不可重用(至少尚未找到任何重用,欢迎使用示例)。它必然需要具有渲染功能。

AReact Class是您所谓的课程。功能明智的React ClassReact Component是一样的。仅基于语法是真正的改变,这React Component是基于的ES6 syntax。另一个主要更改是,除非使用箭头功能,否则不再支持默认的功能绑定。Mixins从也不再受支持ES6


-1

元素是重击。

React使您可以将UI定义为在应用程序状态上定义的纯函数。它可以通过在每次状态更改期间计算整个UI来实现此目的,但这会很昂贵。元素是计算描述(thunk),如果它们不变,并且您使用PureComponent的是s,React将不会费心重新计算该子树。


这不能回答问题。
Janez Kuhar

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.