翻译文章
原文: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微信公众号
共收到0条回复