React Component vs React Element

有这样的一个问题:

// 方法定义
function add(x, y) {
    return x + y
}

// 方法调用
add(1, 2)

// 组件定义
class Icon extends Component {}

// 组件调用??????
<Icon />

最后的一句<Icon />用专业的词概括是什么操作,组件调用还是什么?

有答“组件声明”的,有答“组件调用的”,有“组件初始化”的,还有“使用一个组件”的。没有一个统一的称呼。造成这样局面的原因是很多时候我们都没有去详细的了解过JSX和React实际操作之间的抽象层。现在我们就深入研究一下这部分知识。

我们来看看最基础的问题,什么是React?React就是一个用来写界面的库。不管React和React的生态有多复杂,最核心的功能就是用来写界面的。那么我们来看看Element,很简单,但是一个React element描述的就是你想要在界面上看到的。再深入一点,一个React element就是一个代表了DOM节点的对象。注意,一个React element并不是在界面上实际绘制的东西,而是这些内容的代表。由于JavaScript对象是轻量级的,React可以任意的创建和销毁这些element对象,而且不用担心太大的消耗。另外,React可以分析这些对象,把当前的对象和之前的对象对比,找出发生的改变,然后根据实际发生的改变来更新实际的DOM。

为了创建一个DOM节点的代表对象(也就是React element),我们可以使用React的createElement方法。

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

createElement方法传入了三个参数。第一个是标签名称字符串(div、span等),第二个是给element设置的属性,第三个是内容或者是子的React element。本例中的“Login”就是element的内容。上面的createElement方法调用后会返回一个这样的对象:

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

当这个对象绘制为DOM(使用ReactDOM.render方法)的时候,我们就会有一个新的DOM节点:

<div id='login-btn'>Login</div>

有一个很有意思的地方,我们在学React的时候首先注意到的就是component(组件)。“Components(组件)是React的构建块”。注意,我们是以element开始本文的。而且你一旦理解了element,理解component也就是水到渠成的事了。一个component就是一个方法或者一个类,可以接受一定的输入,之后返回一个React element。

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

在上面的定义中,我们有一个Button组件(component)。接收一个onLogin输入并返回一个React element。注意,Button组件接收的onLogin方法是它的prop。然后把这个方法通过createElement方法的第二个参数传入到了实际的DOM里。

更深入一点

目前,我们只接触到了使用HTML元素来创建React element,比如“div”、“span”等。其实,你也可以把其他的React component(组件)作为第一个参数传入createElement方法。

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

然而,不同于一般的HTML标签名称,React如果发现第一个参数是class或者function类型的话,它就会检查传入的参数要绘制的是一个什么element,传入必要的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})
    )
}

上面的例子里有两个component(组件)。一个Button,一个User。User“代表”了一个div,div里面有两个子节点:一个包含用户名的“p”和一个Button组件。现在我们看看上面的例子的具体的调用过程。

function Button({addFriend}) {
    return {
        type: 'button',
        props: {
            onClick: addFriend,
            children: 'Add Friend'
        }
    }
}

function User({name, addFreind}) {
    return {
        type: 'div',
        props: {
            children: [
                {
                    type: 'p',
                    props: {
                        children: name
                    }
                },
                {
                    type: Button,
                    props: {
                        addFriend
                    }
                }
            ]
        }
    }
}

在上面的代码里你会看到四种不同的属性:“button”,“div”,“p”和Button。当React看到一个element是function和类类型的话,它就会检查element会返回什么element,并传入对应的props。在这个过程结束之后,React就拥有了一个代表DOM树的对象的数。上面的例子最后的结构是这样的:

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

上面叙述的整个过程叫做Reconciliation(这个不知道怎么翻译,应该叫和谐?)。在React里,每次调用setState方法或ReactDOM.render方法被调用的时候都会触发这个过程。

那么我们来看看最开始的问题:

// 方法定义
function add(x, y) {
    return x + y
}

// 方法调用
add(1, 2)

// 组件定义
class Icon extends Component {}

// 组件调用??????
<Icon />

现在我们已经有了回答这个问题的全部知识,除了一点点以外。有个地方,你可能觉得奇怪在使用React的时候,从来没有用过createElement方法来创建element。你是用了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 })
  )
}

写成JSX的样子是这样的:


function Button({addFriend}) {
    return (
        <button onClick={addFriend}>Add Friend</button>
    )
}

function User({name, addFriend}) {
    return (
        <div>
            <p>{name}</p>
            <Button addFriend={addFriend} />
        </div>
    )
}

所以,最后我们应该怎么回答前面的问题呢?<Icon />叫做什么?

应该叫做“创建element”,应为JSX最后会转码为createElement方法的调用:

React.createElement(Icon, null)

前面的例子都是“创建一个React element”。

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

<div className='container'>Hello!</div>

<Hello />

原文地址:https://tylermcginnis.com/react-elements-vs-react-components/

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容