React Component和React Element有什么区别?文档提到了两者,但没有详细说明,一些方法需要组件,其他元素......
答案 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
的实例;相反,它只是要创建的组件实例的描述。它是具有MyComponent
,key
,props
和ref
属性的对象。此处type
和key
为ref
,null
为空对象,props
为type
。MyComponent
呈现时,会创建MyComponent
的实例(并在上面的示例中,从其构造函数中记录自己)。element
也是一个元素,并且具有another_element
,key
,ref
和props
属性,就像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组件的引用key
,String
唯一标识React元素ref
,访问底层DOM节点或React Component Instance的引用)props
(属性Object
) React Element不是React Component的实例。它只是一个简化的“描述”,说明如何创建React组件实例(或依赖于type
HTML标记)。
描述React组件的React元素不知道它最终呈现到哪个DOM节点 - 这个关联是抽象的,将在渲染时解析。
React Elements可能包含子元素,因此能够形成表示虚拟DOM树的元素树。
自定义React组件由React.createClass
或扩展React.Component
(ES2015)创建。如果实例化React组件,则它需要props
Object
并返回一个实例,该实例称为React组件实例。
React组件可以包含状态,并且可以访问React Lifecycle方法。它必须至少具有render
方法,该方法在调用时返回React元素(-tree)。请注意,您永远不会自己构建React组件实例,但让React为您创建它。
答案 3 :(得分:8)
type
属性。_在此进行区分很重要,因为元素不是我们在屏幕上看到的实际物体,而是对象表示形式是呈现的内容。
createElement
方法创建DOM节点(也称为React元素)的对象表示。
const element = React.createElement(
'div',
{id: 'login-btn'},
'Login'
)
createElement
在这里接受三个参数
Login
的文本)createElement
调用返回一个对象
{
type: 'div',
props: {
children: 'Login',
id: 'login-btn'
}
}
将其呈现到DOM(使用ReactDOM.render
)后,我们将拥有一个新的DOM节点,如下所示:
<div id='login-btn'>Login</div>
通常,React是从“组件优先”的方法讲授的,但是首先了解“元素”才能顺利过渡到组件。
组件是可以选择接受输入并返回React元素的函数或类。
React Component是一个模板。蓝图。全局定义。这可以是一个函数或一个类(带有渲染函数)。
如果react将类或函数视为第一个参数,它将检查以给定相应的props呈现其呈现的元素,并将继续执行此操作,直到不再有createElement
调用为止带有类或函数作为第一个参数的对象。
当React看到一个具有函数或类类型的元素时,它会与该组件进行协商,以了解应返回的元素(给定相应的道具)。
在此过程结束时,React将具有DOM树的完整对象表示形式。整个过程在React中称为对帐,每次调用setState
或ReactDOM.render
时都会触发。
类语法是定义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>
);
}
}
基于功能的组件
使用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
方法作为其属性。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 Class
和React 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
,那么就不需要额外的反应Element
。 functional 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!