React Component和React Element有什么区别?该文档提到了两者,但没有详细介绍,某些方法需要组件,其他元素...
Answers:
这里涉及三种相关的事物,它们都有自己的名称:
这有点令人惊讶,因为如果您习惯于其他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
(类本身)是一个Componentelement
是一个元素。这不是的实例MyComponent
;相反,它只是要创建的组件实例的描述。这是一个对象key
,props
,ref
和type
属性。在这里,key
并且ref
有null
,props
是一个空的对象,type
是MyComponent
。MyComponent
(并在上面的示例中,从其构造函数记录其自身)element
。another_element
也是一个元素,并有key
,ref
,props
和type
刚才一样的属性element
那样-但这次的值type
是字符串"div"
。我建议阅读React团队的博客文章React Components,Elements和Instances中详细探讨React具有这三个截然不同的概念的设计原因。
最后,应该注意的是,尽管官方文档严格使用术语“组件”来指代函数或类,而“组件实例”来指代实例,但其他来源并不一定要遵循该术语。阅读GitHub上的Stack Overflow答案或讨论时,您应该期望看到“组件”(错误地)表示“组件实例”。
<Child/>
从其render()
方法返回的父组件,则每次render()
调用都会返回一个新元素,但是Child
可以重复使用一个已经存在的组件以实现元素。)
el = <SomeComponent>
然后将一个元素多次嵌入到render()
方法返回的某个大型JSX表达式中(即do <div>{el}{el}{el}{el}</div>
)。
为了进一步阐述答案,React Element没有任何方法,并且原型上也没有。这也使它们快速。
“ ReactElement是DOM元素的轻量,无状态,不可变的虚拟表示形式”-React术语表
react组件render()
函数返回幕后的react元素DOM树(这是虚拟DOM btw)。这里涉及一些复杂的映射和差异逻辑,但是基本上这些React元素映射到DOM元素。
您还可以直接创建一个Element,React.createElement(arg)
其中arg可以是html标签名称,也可以是React Component类。
ReactElement
VDOM本身就是...但是我不是这个主题的专家,这只是预感。以我的理解,Element
仍然需要对VDOM呈现一个反应,然后将其与“先前的” VDOM进行比较。我认为user6445533
答案也是如此。对 ?我不是专家,但经过一些思考后,常识告诉我这是唯一有意义的解释……但是我的直觉可能是错误的。
React Element只是一个普通的旧JavaScript,Object
没有自己的方法。它本质上具有四个属性:
type
,String
表示HTML标签或引用React组件的引用key
,String
用于唯一标识React元素ref
,用于访问基础DOM节点或React Component Instance的引用)props
(属性Object
)React元素不是React组件的实例。这只是type
要创建的React Component实例(或取决于HTML标签)的外观的简化“描述” 。
描述React组件的React元素不知道最终将其渲染到哪个DOM节点-该关联被抽象化并将在渲染时解析。
React Elements可能包含子元素,因此能够形成代表虚拟DOM树的元素树。
自定义React组件是React.createClass
通过扩展或通过扩展创建的React.Component
(ES2015)。如果实例化一个React Component,则它期望aprops
Object
并返回一个实例,该实例称为React Component Instance。
React组件可以包含状态,并可以访问React Lifecycle方法。它必须至少有一个render
方法,该方法在调用时返回一个React Element(-tree)。请注意,您永远不会自己构造React Component Instances,而是让React为您创建它。
type
除本地HTML元素外,React元素可以具有其他属性。_在这里进行区分很重要,因为元素不是我们在屏幕上看到的实际东西,而是对象表示就是渲染的东西。
createElement
。
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将一个类或函数作为第一个参数,它将检查是否呈现了该元素,并给出相应的支持,并将继续执行此操作,直到不再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
属性所做的那样。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>
);
一个元素是描述你要在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'
)
}
这是我的看法:
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。
AReact Element
是您认为是基本的html(准确地说是dom)元素。这只是不使用引起争议的jsx格式创建元素的一种方法。
React Component
您可以将A视为对象。它具有其方法,支持React lifecycles
并且通常不可重用(至少尚未找到任何重用,欢迎使用示例)。它必然需要具有渲染功能。
AReact Class
是您所谓的课程。功能明智的React Class
和React Component
是一样的。仅基于语法是真正的改变,这React Component
是基于的ES6 syntax
。另一个主要更改是,除非使用箭头功能,否则不再支持默认的功能绑定。Mixins
从也不再受支持ES6
。
元素是重击。
React使您可以将UI定义为在应用程序状态上定义的纯函数。它可以通过在每次状态更改期间计算整个UI来实现此目的,但这会很昂贵。元素是计算描述(thunk),如果它们不变,并且您使用PureComponent
的是s,React将不会费心重新计算该子树。