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

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

许多人可能听说过 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()

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

[参考资料]

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


react 中的高阶组件主要是对于 hooks 之前的类组件来说的,如果组件之中有复用的代码,需要重新创建一个父类,父类中存储公共代码,返回子类,同时把公用属性...
我们上一节了解了组件的更新机制,但是只是停留在表层上,例如我们的 setState 函数式同步执行的,我们的事件处理直接绑定在了 dom 元素上,这些都跟 re...
我们上一节了解了 react 的虚拟 dom 的格式,如何把虚拟 dom 转为真实 dom 进行挂载。其实函数是组件和类组件也是在这个基础上包裹了一层,一个是调...
react 本身提供了克隆组件的方法,但是平时开发中可能很少使用,可能是不了解。我公司的项目就没有使用,但是在很多三方库中都有使用。本小节我们来学习下如果使用该...
mobx 是一个简单可扩展的状态管理库,中文官网链接。小编在接触 react 就一直使用 mobx 库,上手简单不复杂。
我们在平常的开发中不可避免的会有很多列表渲染逻辑,在 pc 端可以使用分页进行渲染数限制,在移动端可以使用下拉加载更多。但是对于大量的列表渲染,特别像有实时数据...
本小节开始前,我们先答复下一个同学的问题。上一小节发布后,有小伙伴后台来信问到:‘小编你只讲了类组件中怎么使用 ref,那在函数式组件中怎么使用呢?’。确实我们...
上一小节我们了解了固定高度的滚动列表实现,因为是固定高度所以容器总高度和每个元素的 size、offset 很容易得到,这种场景也适合我们常见的大部分场景,例如...
上一小节我们处理了 setState 的批量更新机制,但是我们有两个遗漏点,一个是源码中的 setState 可以传入函数,同时 setState 可以传入第二...
我们知道 react 进行页面渲染或者刷新的时候,会从根节点到子节点全部执行一遍,即使子组件中没有状态的改变,也会执行。这就造成了性能不必要的浪费。之前我们了解...
在平时工作中的某些场景下,你可能想在整个组件树中传递数据,但却不想手动地通过 props 属性在每一层传递属性,contextAPI 应用而生。
楼主最近入职新单位了,恰好新单位使用的技术栈是 react,因为之前一直进行的是 vue2/vue3 和小程序开发,对于这些技术栈实现机制也有一些了解,最少面试...
我们上一节了了解了函数式组件和类组件的处理方式,本质就是处理基于 babel 处理后的 type 类型,最后还是要处理虚拟 dom。本小节我们学习下组件的更新机...
前面几节我们学习了解了 react 的渲染机制和生命周期,本节我们正式进入基本面试必考的核心地带 -- diff 算法,了解如何优化和复用 dom 操作的,还有...
我们在之前已经学习过 react 生命周期,但是在 16 版本中 will 类的生命周期进行了废除,虽然依然可以用,但是需要加上 UNSAFE 开头,表示是不安...
上一小节我们学习了 react 中类组件的优化方式,对于 hooks 为主流的函数式编程,react 也提供了优化方式 memo 方法,本小节我们来了解下它的用...
开源不易,感谢你的支持,❤ star me if you like concent ^_^
hel-micro,模块联邦sdk化,免构建、热更新、工具链无关的微模块方案 ,欢迎关注与了解
本文主题围绕concent的setup和react的五把钩子来展开,既然提到了setup就离不开composition api这个关键词,准确的说setup是由...
ReactsetState的执行是异步还是同步官方文档是这么说的setState()doesnotalwaysimmediatelyupdatethecomponent.Itmaybatchordefertheupdateuntillater.Thismakesreadingthis.staterightaftercallingsetState()apotentialpitfall.Instead,usecom