反应组分与反应元素的区别

时间:2015-06-22 02:32:28

标签: javascript reactjs

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

12 个答案:

答案 0 :(得分:34)

这里涉及三种相关的事情,有自己的名字:

  • 部件
  • 组件实例
  • 元素

这有点令人惊讶,因为如果你习惯了其他UI框架,你可能会认为只有两种东西,大致对应于类(如Widget)和实例(如{{ 1}})。在React中并非如此;组件实例与元素相同,它们之间也没有一对一的关系。为了说明这一点,请考虑以下代码:

new Widget()

在上面的代码中:

  • 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是一个元素。它是 element的实例;相反,它只是要创建的组件实例的描述。它是具有MyComponentkeypropsref属性的对象。此处typekeyrefnull为空对象,propstype
  • MyComponent呈现时,会创建MyComponent实例(并在上面的示例中,从其构造函数中记录自己)。
  • element也是一个元素,并且具有another_elementkeyrefprops属性,就像type一样 - 但是这个时间element的值是字符串type

React团队的博客文章React Components, Elements, and Instances详细探讨了React具有这三个不同概念的设计原因,我推荐阅读。

最后,应该注意的是,虽然官方文档严格使用术语“组件”来引用函数或类,而“组件实例”引用实例,但其他来源不一定遵守此术语;在GitHub上阅读Stack Overflow答案或讨论时,您应该会看到“组件”用于(错误地)表示“组件实例”。

答案 1 :(得分:21)

为了进一步详细说明答案,React Element没有任何方法,也没有原型上​​的任何内容。这也使他们很快。

“ReactElement是DOM元素的轻量级,无状态,不可变的虚拟表示” - https://facebook.github.io/react/docs/glossary.html#react-elements

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

您也可以直接创建元素React.createElement(arg),其中arg可以是html标记名称,也可以是React Component类。

答案 2 :(得分:14)

反应元素

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

  • type,表示HTML标记的String或引用React组件的引用
  • keyString唯一标识React元素
  • ref,访问底层DOM节点或React Component Instance的引用)
  • props(属性Object

React Element不是React Component的实例。它只是一个简化的“描述”,说明如何创建React组件实例(或依赖于type HTML标记)。

描述React组件的React元素不知道它最终呈现到哪个DOM节点 - 这个关联是抽象的,将在渲染时解析。

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

React组件和React组件实例

自定义React组件由React.createClass或扩展React.Component(ES2015)创建。如果实例化React组件,则它需要props Object并返回一个实例,该实例称为React组件实例。

React组件可以包含状态,并且可以访问React Lifecycle方法。它必须至少具有render方法,该方法在调用时返回React元素(-tree)。请注意,您永远不会自己构建React组件实例,但让React为您创建它。

答案 3 :(得分:8)

反应元素与React组件

反应元素

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

_在此进行区分很重要,因为元素不是我们在屏幕上看到的实际物体,而是对象表示形式是呈现的内容。

在以下方面,反应很好:

  • React可以创建和销毁这些元素而没有太多开销。 JS对象重量轻且成本低。
  • React可以将对象与先前的对象表示形式进行区分以查看发生了什么变化。
  • React可以更新实际的DOM,尤其是在检测到更改发生的地方。这有一些性能上的优势。
我们可以使用createElement方法创建DOM节点(也称为React元素)的对象表示。
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!

Huzzah

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

反应成分

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

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

  • 如果react将类或函数视为第一个参数,它将检查以给定相应的props呈现其呈现的元素,并将继续执行此操作,直到不再有createElement调用为止带有类或函数作为第一个参数的对象。

  • 当React看到一个具有函数或类类型的元素时,它会与该组件进行协商,以了解应返回的元素(给定相应的道具)。

  • 在此过程结束时,React将具有DOM树的完整对象表示形式。整个过程在React中称为对帐,每次调用setStateReactDOM.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属性一样。

答案 4 :(得分:4)

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

例如 -

React Element

<button class="blue"></button> - 它是一个接受输入并返回React元素的函数或类。它必须保持对其DOM节点和子组件实例的引用。

React Component

答案 5 :(得分:3)

组件是用于创建元素的工厂。

答案 6 :(得分:1)

你会认为React Element是一个基本的html(dom to be precise)元素。它只是一种在不使用备受争议的jsx格式的情况下创建元素的方法。

您可以将React Component视为对象。它有它的方法,支持React lifecycles并且通常是不可用的(至少还没有找到任何重用,欢迎举例)。它必然需要一个渲染功能。

你称之为React Class。明智的功能React ClassReact Component是相同的。只有语法才是真正的变化,因为React Component基于ES6 syntax。另一个主要变化是除非使用箭头函数,否则不再支持函数的默认绑定。 Mixins之后也不再支持ES6

答案 7 :(得分:0)

元素是一个普通对象,用于描述您要在屏幕上显示的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'
  )
}

答案 8 :(得分:0)

反应组件是可变的,而元素不是可变的

答案 9 :(得分:0)

这是我的看法:

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

如果一切都是functional component,那么就不需要额外的反应Elementfunctional component层次结构可以直接生成VDOM树。

react Component Instance层次结构(tree)是一个“工厂”,并且工厂通过馈送到根react Component Instance的props和所有状态“存储在Component Instance树中的任何位置。

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

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

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

react Element是VDOM和Component Instance之间的抽象层,为什么这种抽象对我来说并不完全清楚,最有可能允许优化。例如,如果Element上的某些生命周期方法说不需要渲染该子树,则Component Instance树不会完全渲染。

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

在Haskell中,此概念称为“提升”:https://wiki.haskell.org/Lifting

例如Haskell中的单子就是这种举升的完美例子。

monad是对计算的一种描述,可以将其存储为值,例如42。同样,react Elements是有关如何计算“新” VDOM的描述的要素。如果有人要计算它。

此演讲通过一些简单示例描述了“额外”间接寻址的概念:https://www.youtube.com/watch?v=GqmsQeSzMdw

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

或:https://en.wikipedia.org/wiki/Fundamental_theorem_of_software_engineering

  

软件工程(FTSE)的基本定理是一个术语   由安德鲁·科尼希(Andrew Koenig)提出,用以描述巴特勒·兰普森(Butler Lampson)的一句话[1]   归因于已故的戴维·J·惠勒:[2]

     

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

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

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

引用user6445533答案的重要部分:

  

type,代表HTML标记的字符串或引用   反应组件

这是关键^^^^

元素不是VDOM。

答案 10 :(得分:0)

元素是重击。

React使您可以将UI定义为在应用程序状态上定义的纯函数。它可以通过在每次状态更改期间计算整个UI来实现此目的,但这会很昂贵。元素是计算描述(thunk),如果它们不变,并且您使用的是E/AndroidRuntime: FATAL EXCEPTION: main Process: com.example.myapplication, PID: 5282 java.lang.NullPointerException: Attempt to invoke virtual method 'boolean java.lang.String.equals(java.lang.Object)' on a null object reference at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:715) at android.view.LayoutInflater.rInflate(LayoutInflater.java:806) at android.view.LayoutInflater.inflate(LayoutInflater.java:504) at android.view.LayoutInflater.inflate(LayoutInflater.java:414) at com.example.myapplication.Adapters.CityAdapter.onCreateViewHolder(CityAdapter.java:30) at com.example.myapplication.Adapters.CityAdapter.onCreateViewHolder(CityAdapter.java:17) at androidx.recyclerview.widget.RecyclerView$Adapter.createViewHolder(RecyclerView.java:6794) at androidx.recyclerview.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline(RecyclerView.java:5975) at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:5858) at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:5854) at androidx.recyclerview.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:2230) at androidx.recyclerview.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1557) at androidx.recyclerview.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1517) at androidx.recyclerview.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:612) at androidx.recyclerview.widget.RecyclerView.dispatchLayoutStep2(RecyclerView.java:3924) at androidx.recyclerview.widget.RecyclerView.dispatchLayout(RecyclerView.java:3641) at androidx.recyclerview.widget.RecyclerView.onLayout(RecyclerView.java:4194) at android.view.View.layout(View.java:15596) at android.view.ViewGroup.layout(ViewGroup.java:4966) at android.widget.FrameLayout.layoutChildren(FrameLayout.java:573) at android.widget.FrameLayout.onLayout(FrameLayout.java:508) at android.view.View.layout(View.java:15596) at android.view.ViewGroup.layout(ViewGroup.java:4966) at android.widget.FrameLayout.layoutChildren(FrameLayout.java:573) at android.widget.FrameLayout.onLayout(FrameLayout.java:508) at android.view.View.layout(View.java:15596) at android.view.ViewGroup.layout(ViewGroup.java:4966) at androidx.appcompat.widget.ActionBarOverlayLayout.onLayout(ActionBarOverlayLayout.java:444) at android.view.View.layout(View.java:15596) at android.view.ViewGroup.layout(ViewGroup.java:4966) at android.widget.FrameLayout.layoutChildren(FrameLayout.java:573) at android.widget.FrameLayout.onLayout(FrameLayout.java:508) at android.view.View.layout(View.java:15596) at android.view.ViewGroup.layout(ViewGroup.java:4966) at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1703) at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1557) at android.widget.LinearLayout.onLayout(LinearLayout.java:1466) at android.view.View.layout(View.java:15596) at android.view.ViewGroup.layout(ViewGroup.java:4966) at android.widget.FrameLayout.layoutChildren(FrameLayout.java:573) at android.widget.FrameLayout.onLayout(FrameLayout.java:508) at android.view.View.layout(View.java:15596) at android.view.ViewGroup.layout(ViewGroup.java:4966) at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:2072) at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1829) at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1054) at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:5779) at android.view.Choreographer$CallbackRecord.run(Choreographer.java:767) at android.view.Choreographer.doCallbacks(Choreographer.java:580) at android.view.Choreographer.doFrame(Choreographer.java:550) at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:753) at android.os.Handler.handleCallback(Handler.java:739) at android.os.Handler.dispatchMessage(Handler.java:95) at android.os.Looper.loop(Looper.java:135) at android.app.ActivityThread.main(ActivityThread.java:5221) at java.lang.reflect.Method. ,React将不会费心重新计算该子树。

答案 11 :(得分:-24)

Consider the Component as a Class, and Element as an instance. That's it!