[译]React 元素 vs React 组件 vs 组件支撑实例

本篇为译文,原文出处:React Elements vs React Components vs Component Backing Instances

React

许多人可能听说过 Facebook 的 React 库,并在自己的工作或项目中使用它。React 是非常流行的,使开发用户界面变得简单且符合声明式编程(译者注:声明式编程可以参考声明式编程和命令式编程的比较)。

正如题目所示,React 有几个概念遍及其文档,这(几个概念)可能会让新React用户困惑。例如,你在 GlossaryTop-Level APIExplanation on Refs 这几章中检索 “React Element”、“React Component” 和 “Component Backing Instance”,你会发现这几个术语遍及各处,已经是 React 的绝对的核心。

这篇文章不是一篇 React 的教程,更多的是分享我最近学习 React 而得到的一些(知识)。因此,这篇文章是针对之前已经涉猎过 React 的人们,我会假设你已经熟悉 React 一些核心概念和语法。

React元素是什么?

React 元素这个概念对于 React 来说很重要,但是我坚信一点,因为使用 React 和 JSX 来构建用户界面过于简单,从而导致可能在最初错过这个概念。如果你之前用过 React + JSX,毫无疑问的说,在你的 JS 文件中书写类 HTML 语法更加符合你的习惯(译者注:Habit is second nature. 习惯成自然)。在这种(书写类HTML语法)假象之下,JSX 语法实际上被编译成特殊的函数调用 — React.createElement()。 猜猜 React.createElement()会产生什么?耶,你猜对了,就是 React 元素。让我们看一个转换过程的例子:

// 使用 JSX 语法
var helloWorld = <div>Hello World!</div>;

// 然后是 JSX 被编译成 JavaScript 后的结果
var helloWorld = React.createElement(
  "div",
  null,
  "Hello World!"
);

// 再是处理成 JavaScript 简单对象后看起来类似这样
var helloWorld = {
    key: null,
    props: {
        children: "Hello World!" // more stuff in here
    },
    ref: null,
    type: "div"
    // more stuff in here
};

React 元素是由一些属性(properties)构成的 JavaScript 简单对象(plain old JavaScript objects,译者注:关于更多解释可以看 Plain Old JavaScriptWhat is a plainObject in JavaScript?):

  • key - 属性 key 是用来在一组相同类型的 React 元素构成的 Array 中标识唯一特定 React 元素。你不必提供一个值给它(译者注:在新版 React 中,如果在循环一组相同类型的 React 元素时不指定 key,会报一个 warning 错误,具体可以参考 React 对循环 warning 提示增加 key 的研究),但是如果你给 key 提供一个值,React 将能够进行优化,使重新渲染过程更高效。
  • props - 属性props 恰恰是你所认为的: 它是向下传递给子组件(child components)的所有 props 和 values 的映射(集合)。
  • ref - 属性ref 用来访问与这个 React 元素相关联的经过(浏览器或渲染引擎)渲染处理后的底层 DOM 元素。
  • type - 属性type 将是两种格式之一,表示任何有效的 HTML 元素的(名称)字符串 或 指向 React 组件类的引用。

此时, 了解每个属性所有细节并不重要。最大的收获在于,React 元素仅仅是 JavaScript 简单对象(plain old JavaScript objects),用来描述组件的 HTML 应是怎样的结构,这个对象上不包含任何方法(Methods),仅仅只有数据。

通过之前 helloWorld React 元素的例子告诉你,helloWorld表示一个子节点为“Hello World!”文本的 div 标签。即使你创建了一个更复杂的 JSX 例子,生成的 React 元素仍将是一个 JavaScript 对象,配合其他嵌套 React 元素描述 HTML 将是怎样的结构。如果这让你感觉熟悉,是因为这正是虚拟 DOM(Virtual DOM)是什么(的解释) - 它是 DOM 在特定时间段应该是怎样的结构的描述,通过 React 元素声明来表示。

React组件是什么?

不同于 React 元素,你可能对 React 组件更熟悉一些。如果你曾写过类似下面例子的代码,那么你之前已经创建过 React 组件类了:

// 这是一个 React 组件类
class CustomForm extends React.Component {
    constructor(props) {
        super(props);

        this.state = {
            inputText: "Willson"
        };

        this.handleInputChange = this.handleInputChange.bind(this);
    }

    handleInputChange(e) {
        const inputText = e.target.value;
        this.setState({ inputText });
    }

    render() {
        return (
            <div className="custom-form">
                <div className="custom-form-header">
                    <p>Hello, {this.state.inputText}</p>
                </div>
                <div className="custom-form-body">
                    <input type="text"
                           value={this.state.inputText}
                           onChange={this.handleInputChange} />
                </div>
            </div>
        );
    }
}

/*
 * 我假设你有可用的 React 和 ReactDom,以及  
 * 一个 id 为 "root" 的 HTML 元素
 * ReactDOM.render() 的返回值是组件实例
 */
var componentInstance = ReactDOM.render(<CustomForm />, document.getElementById("root"));

上面的例子是用 ES2015 新的 Class 语法写的,但它几乎相当于使用 React.createClass()。你也许熟悉编写这些组件类,但重要的是(React文档中所述的)React 组件指代的是一个 React 组件类的实例。

假如你熟悉 JavaScript 的话,当你听到到“实例化一个类”且智商在线时,你可能会考虑使用new操作符。然而,(在 React 中)从不需要你用 new 操作符创建 React 组件。相反的是,你将使用 ReactDOM.render() 把一个 React 元素渲染成一个特定的 DOM 元素,与此同时,ReactDOM.render() 将返回一个 React 组件实例。

ReactDOM.render() 返回的组件实例可以调用在 React 组件类中定义的方法。反过来想一下会让你明白,ReactDOM.render() 是 React 为你实例化一个组件的机制。通常, 你不需要访问组件实例本身, 但我发现在测试 React 组件时, 将组件实例的引用保存下来(对测试)很有帮助。

ReactDOM.render() 有关的最后一个有趣的点是,React 在 ReactDOM.render() 时对虚拟 DOM 执行高效的 diff 算法(译者注: diff 算法延伸阅读)。如果你记得(上面说的),React 元素表示虚拟 DOM(元素),这意味着 ReactDOM.render() 接收一个虚拟 DOM(元素),将其渲染成一个真实 DOM 元素,返回(React 元素 type 指定的)组件实例。在底层,如果你将相同的React元素类型(带有可能不同的 props 值)通过 ReactDOM.render() 渲染成相同的 DOM 元素,React 将执行 diff 算法,对 DOM 元素进行仅必须的最小更改。也许令人惊讶的是,它每次返回相同的组件实例 - 除了更新的 prop 和 state 值

组件支撑实例是什么?

前两节探讨了 React 元素和 React 组件实例,这两个概念都是 DOM 之上的抽象层。 现在我们将讨论术语“组件支撑实例”,以及它们自身如何与真实 DOM 元素相关联。

// 组件类
class CustomForm extends React.Component {
    constructor(props) {
        super(props);

        this.state = {
            inputText: "Willson"
        };

        this.handleInputChange = this.handleInputChange.bind(this);
    }

    handleInputChange(e) {
        const inputText = e.target.value;
        this.setState({ inputText });
    }

    render() {
        return (
            <div className="custom-form">
                <div className="custom-form-header">
                    <p>Hello, {this.state.inputText}</p>
                </div>
                <div className="custom-form-body">
                    <input type="text"
                           value={this.state.inputText}
                           onChange={this.handleInputChange} />
                </div>
            </div>
        );
    }
}

// 组件实例
var componentInstance = ReactDOM.render(<CustomForm />, document.getElementById("root"));

// DOM 实例
var domInstance = ReactDOM.findDOMNode(componentInstance);
// 原文作者这里出了个 Bug,多了一个 D 

在前面的实例中,ReactDOM.render() 通过将 React 元素渲染到现有的 DOM 元素中来生成一个组件实例。正如我们已经知道的,组件实例不是真实 DOM 节点。然而,访问与组件实例关联的底层 DOM 节点实际上很简单 - 我们使用 ReactDOM.findDOMNode(),并将组件实例作为其参数传递。那么这与“组件支撑实例”术语有什么关系呢?让我们一脸懵逼的是,React 在其文档的各处将引用的真实 DOM 节点称之为组件支撑实例

总结

这3个术语“React 元素”,“React 组件”和“组件支撑实例”是密切相关的。 由于 JSX 语法被转译为 React.createElement()调用(译者注:理解转译这个概念可以参考 Compiling Vs Transpiling),最终返回我们称之为“React 元素”的 JavaScript 简单对象(plain old JavaScript objects),你可以将 React元素视为基础构建单元。React 组件实例表示下一个抽象层 - ReactDOM.render() 接收一个 React 元素、引用一个真实 DOM 节点、返回一个 React 组件实例。该实例可以访问组件类中定义的方法,但是在单元测试之外很少用到这一点。React 元素和 React 组件实例都不表示真实 DOM 元素。渲染组件实例产生的 DOM 元素被称为组件支撑实例,访问它的主要方式是使用ReactDOM.findDOMNode()

我希望这篇文章能够帮助你理清这些术语!

[参考资料]

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

推荐阅读更多精彩内容