React Elements VS React Components

翻译文章
原文:https://tylermcginnis.com/react-elements-vs-react-components/
原文作者:Tyler

如翻译有问题,麻烦请指出,(菜鸟级博文翻译员不胜感激!)。


几个月之前,我在Twitter提出了一个问题

I'm big on precise language when teaching but I haven't nailed down a great verbiage for using a component. Thoughts?
我在教学时非常重视精确的语言,但是我还没有找到一个很好的语言来使用组件。有想法吗?

// 函数定义
function add(x, y) {
 return x + y;
}
// 函数调用
add(1, 2);
// 组件定义
class Icon extends Component {}
// 组件调用???

让我感到惊讶的不是围绕着这个问题的一些困惑,而是我收到了大量不准确的回答。

造成我困惑的主要原因是JSX和在React中实际发生了什么之间,通常没有讨论抽象层。为了回答这个问题,我们需要深入挖掘这种抽象。

让我们从React的基础知识开始吧。什么是React?他是构建用户界面的库。无论React或者React生态系统看起来有多复杂,React的核心是——构建UIs。有了这个思路,我们先看我们的第一个定义,元素(Element)。简言之,React元素描述了展现在屏幕中你想要看到的内容。复杂点说,React元素是DOM节点的一个对象表示(object representation)。注意,我使用了词语是"描述"。重要的是要注意,Reast元素不是你在屏幕上看到的东西,而仅仅是一个对象表示。这里有几个原因。首先是JavaScript对象是轻量级的——React可以在没有太大的开销的情况下创建和销毁这些元素。第二,React能够分析这个对象,通过和前一个对象表示进行比较来区别其中的变化,进而更新那些仅仅发生改变的实际DOM(actual DOM)。这有一些性能方面的好处。

为了创建DOM节点的对象表示(即React元素),我们能够使用React的createElement方法。

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

createElement方法接收三个元素,第一个参数是标签名字符串(div,span等),第二个参数是你想要添加的任何属性,第三个参数是文本内容或者是这个元素的子元素,在上面的示例中是文本"Login"。调用上面的createElement方法将会返回下面这个形状的对象。

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

当使用ReactDOM.render将它渲染成DOM,我们将会得到一个新的DOM节点,像这样

Login

到目前为止,都很好。有趣的是学习React,通常第一件事情教你的是组件。"组件(Components)是构建React的块"。注意,然而,这篇文章我们是以元素开始介绍的。是因为,一旦你了解了元素,那么理解组件会是一个平滑的过度。组件是一个函数或者一个类,它可 选的接收输入并返回一个React元素。

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

通过上面的函数定义, 我们有一个Button组件,它接收onLogin的输入并且返回一个React元素。需要注意的一件事是,Button组件接收一个onLogin方法作为它的prop。为了将将其传递给DOM对象表示,我们将它作为第二个参数传递给createElement,就像id·属性一样。

到目前为止,我们只介绍了使用本地HTML元素(span, div等)的"type"属性创建React元素,但是也可以将其它React组件传递给createElement的第一个参数。

const element = React.createElement(
 User,
 {name: 'TylerMcGinnis'},
 null
)

然而,与HTML标签名不同,如果React看到一个类或者一个函数作为第一个参数,它会检查它渲染的是什么元素,然后给予相对应的props。React将会继续这样做,直到没有调用的具有一个类或有一个函数作为第一个参数的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})
 )
}

如上所示,我们有两个组件。一个Button组件和一个User组件。User组件的DOM对象表示,是一个有两个子元素div元素,一个p元素包裹着一个用户名和一个Button组件。现在让我们将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, div, p和Button组件。当React看到一个元素有函数或者类类型(就像type: Button),它将会通过解析那个组件来了解它返回哪个元素,给予相应的props。有这些思路,最后一个过程,React有一整个对象表示的DOM树。例子如下

{
 type: 'div',
 props: {
 children: [
 {
 type: 'p',
 props: {
 children: 'Tyler McGinnis'
 }
 },
 {
 type: 'button',
 props: {
 onClick: addFriend,
 children: 'Add Friend'
 }
 }
 ]
 }
}

这整个过程在React中被称为reconciliation,并且每次调用setState或者ReactDOM.render时触发。

现在再来看看我们最初提出的问题

// 函数定义
function add(x, y) {
 return x + y;
}
// 函数调用
add(1, 2);
// 组件定义
class Icon extends Component {}
// 组件调用???

现在我们用用解答这个问题的所有知识,除了一个重要的部分。奇怪的是,如果你已经在任何时间使用React,你没有使用React.createElement来创建你的DOM对象表示。相反的,你可能使用JSX。前面我写过"造成我困惑的主要原因是JSX和在React中实际发生了什么之间,通常没有讨论抽象层。"。这个抽象层是JSX总是通过Babel传递给React.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 })
 )
}

转换之后的结果:

function Button ({ addFriend }) {
 return (
 Add Friend
 )
}
function User ({ name, addFriend }) {
 return (
 {name}
 )
}

所以最后,当我们将我们的组件写成<Icon />,我们应该将它称为什么?

因为JSX被传递之后,我们可以称它为"创建一个元素(creating an element)",这正是发生了什么。

React.createElement(Icon, null)

所有这些示例,都是"创建一个React元素"

React.createElement(
 'div',
 { className: 'container' },
 'Hello!'
)
Hello!

更多请参考:"React Components, Instances, and Elements"

w3ctech微信

扫码关注w3ctech微信公众号

共收到0条回复

AltStyle によって変換されたページ (->オリジナル) /