一道 React 面试题:在浏览器、组件和元素中都渲染了些什么?

作者:Samer Buna

翻译:疯狂的技术宅

原文:https://medium.com/edge-coder...

未经允许严禁转载

这道题的答案有点复杂。

首先要搞清楚 elementcomponent 是不是一回事?

Image for post

从技术上来说,ReactDOM 不会在 DOM 中渲染 React 组件或 React 元素。它渲染由其组件实例支持的 DOM 元素。对于类组件来说这是正确的。但是对于函数组件,ReactDOM 仅渲染 DOM 元素。函数组件没有实例(可以通过 this 访问),因此在使用函数组件时,ReactDOM 会渲染由函数返回的元素所生成的 DOM 元素。

你需要在这里理解的是,React 元素不同于 DOM 元素。 React 元素只是 HTML 元素、React 组件或它们的混合的“描述”。

好吧,一个更好的面试题可能应该这样问:当你在JSX中使用 <MyComponent/> 之类的东西时,它是组件、元素还是实例?

这是一个元素,但不是 DOM 元素,而是一个 React元 素。因为任何 JSX 标签都会被转换为 React.createElement 再去调用。

但是要想让 React 继续使用这个 React 元素的话,必须调用一个函数或从一个类中创建实例。

你可能会在一些 React 教程中看到 组件(component)、元素(element)实例(instance) 这些词。我在这里混用这些词是不对的,但是我认为 React 的初学者需要了解它们的区别。 React 官方博客上有一篇文章专门说明这些概念,但我认为这些内容对于初学者来说还远远不够。

以下是这些术语的简单定义:

  • React Component 是模板,蓝图,全局定义的。可以是函数(带有渲染功能)。
  • React Element 是从组件中返回的东西。这个对象实际上描述了组件所代表的 DOM 节点。对于函数组件来说,此元素是函数返回的对象。对于类组件,元素是组件的渲染函数返回的对象。 React 元素不是我们在浏览器中所看到的。它们只是内存中的对象,我们无法对其进行任何更改。
  • React 在其内部通过创建、更新和销毁 instance 来找出需要渲染给浏览器的 DOM 元素树。使用类组件时,通常将其浏览器渲染的 DOM 元素称为组件实例。你可以渲染同一组件的多个实例。实例是你在基于类的组件内部使用的 this 关键字。你不需要手动从类创建实例,只需要记住它就在 React 的内存中即可。
  • 基于函数的 React 元素没有实例。一个函数组件仍然可以被多次渲染,但是 React 不会将本地实例与每个渲染相关联。它只是用函数的调用来确定要为该函数渲染的 DOM 元素。

最重要的是,ReactDOM 不会在浏览器中渲染组件,也不会渲染元素(这里的术语元素代表 React.createElement 的返回值)。它也不渲染实例。 它只渲染 DOM 元素。

不幸的是,使用术语组件既指模板又指通过模板使用的任何一种实例或者调用,这似乎是很普遍的。人们对此感到困惑很正常,这挺痛苦的。

What’s the story here?

这是什么故事?

每个 React 应用都从一个使用 React elementrender 调用开始。下面以 reactjs.org 官网提供的 HelloMessage 案例作为例子,我对这个例子稍微做了一些修改,使其具有了函数组件:

const Today = () => (
  <div>Today is {new Date().toDateString()}</div>
);class HelloMessage extends React.Component {
  render() {
    return (
      <React.Fragment>
        <div>Hello {this.props.name}</div>
        <Today />
      </React.Fragment>
    );
  }
}ReactDOM.render(
  <HelloMessage name="Taylor" />,
  mountNode
);

第一个 React 元素是我们在 ReactDOM.render 调用中开始的元素:

<HelloMessage name="Taylor" /> // 这是 React element

这个 React 元素描述了要渲染的 DOM 树应该以 HelloMessage 组件和值等于 Taylor 的 prop name 开始。

现在回答问题:HelloMessage 是什么?

每当 React 元素描述一个 React 组件时(就像上面的 React 元素一样),React 使用该组件将描述替换为组件返回的内容。这时它将会为基于类的组件创建一个实例,并将该实例的引用保留在内存中。它不会为基于函数的组件创建任何内容。它只是调用它们。

HelloMessage 组件返回的是一个描述 React.Fragment 组件的 React 元素。

回答问题: React.Fragment 是什么?

React 会持续不断的减少这些组件的未知描述,直到只存在有效的 DOM 节点。 React.Fragment 的描述被翻译成 2 个React 元素,一个描述 div ,另一个描述 Today 组件。

回答问题:代码中的 Today 什么是?

它调用 Today 函数来找出最后一个问题。 Today 函数返回描述一个 div 的 React 元素。

至此,virtual 树中包含了所有描述 DOM 节点的 React 元素。 React 通过一致性比较算法找出要在浏览器中更新的内容。用组件实例所转换的树节点保留修改该实例的能力。

173382ede7319973.gif


本文首发微信公众号:前端先锋

欢迎扫描二维码关注公众号,每天都给你推送新鲜的前端技术文章

欢迎扫描二维码关注公众号,每天都给你推送新鲜的前端技术文章

欢迎继续阅读本专栏其它高赞文章:


阅读 1.1k

推荐阅读
疯狂的技术宅
用户专栏

本专栏文章首发于公众号:前端先锋 。

24297 人关注
416 篇文章
专栏主页