面试题:React 中 Element 和 Component 有什么区别?

这是一道考察React核心概念的基础题,虽然简单但很容易说错。关键在于理解:Element是描述,Component是函数或类

下面从本质、创建方式、用途等维度来拆解。


一、核心结论

Element是一个普通的JavaScript对象,描述了你想要在屏幕上看到什么;Component是一个函数或类,接收props并返回一个Element树。

一句话总结:

  • Element = 虚拟DOM节点(JS对象)
  • Component = 生成Element的模板(函数/类)

二、详细对比表

维度React ElementReact Component
本质普通JS对象函数或类
创建方式React.createElement() 或 JSX定义函数或继承React.Component
类型对象类型(typeof element === 'object'函数类型或类类型
内容typepropskeyref等属性渲染逻辑(render方法或函数体)
是否可实例化否,它是不可变对象类组件会被实例化,函数组件不会
生命周期类组件有,函数组件用Hooks模拟
用途描述UI结构封装可复用的UI逻辑
命名规范小写(DOM元素)或大写(组件)大写字母开头
内存占用极轻量较重(尤其是类组件实例)

三、代码示例

React Element 示例

// 写法1:JSX(编译后变成 createElement)
const element1 = <div className="container">Hello</div>;

// 写法2:直接调用 createElement
const element2 = React.createElement('div', { className: 'container' }, 'Hello');

// element 的本质是一个普通对象
console.log(element1);
// 输出大致如下:
{
  type: 'div',
  props: {
    className: 'container',
    children: 'Hello'
  },
  key: null,
  ref: null,
  $$typeof: Symbol(react.element)
}

React Component 示例

// 函数组件
function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

// 类组件
class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

// Component 是函数或类
console.log(typeof Welcome); // 'function'(类本质也是函数)

Element 和 Component 的关系

// Component 返回 Element
function MyComponent() {
  return <div>I am Element</div>;  // 返回的是 Element
}

// 使用 Component 生成 Element
const element = <MyComponent />;  // 这是一个 Element,type 是 MyComponent 函数

// React 内部渲染流程:
// <MyComponent /> → React.createElement(MyComponent, null) → Element对象
// → React 根据 element.type 调用 MyComponent() → 得到子 Element
// → 递归直到得到原生 DOM 类型的 Element

四、深入理解

1. Element 的 type 是关键

Element对象的type属性决定了它是什么:

// type 是字符串 → 原生 DOM 元素
{ type: 'div', props: { className: 'box' } }

// type 是函数 → 函数组件
{ type: MyComponent, props: { name: 'React' } }

// type 是类 → 类组件
{ type: class MyComponent extends ... , props: {...} }

2. Component 的两种形式

// 函数组件:纯函数,无实例
function Button({ text }) {
  return <button>{text}</button>;
}

// 类组件:有实例,有生命周期
class Button extends React.Component {
  render() {
    return <button>{this.props.text}</button>;
  }
}

3. 渲染过程对比

原始代码: <App />
    ↓ React.createElement()
Element: { type: App, props: {} }
    ↓ React 识别 type 是组件
调用 App() 或 new App().render()
    ↓ 返回新的 Element
Element: { type: 'div', props: { children: 'Hello' } }
    ↓ React 识别 type 是字符串(DOM)
创建真实 DOM 节点
    ↓
页面更新

五、面试常见追问

追问1:为什么 Component 必须返回 Element?

答: React的渲染引擎期望接收一个Element树来描述UI。Component的本质是一个转换器:输入props,输出Element。这样才能形成递归渲染:Component → Element → 更小的Component → 原生Element → DOM。

追问2:Element 是不可变的吗?

答: 是的。一旦创建,Element的typepropschildren都不能改变。如果UI需要更新,React会创建新的Element树,然后通过Diff算法找出差异并更新真实DOM。这也是虚拟DOM能工作的基础。

追问3:可以在代码中直接使用 Element 对象吗?

答: 可以,但不常见。React内部大量使用Element对象,但开发者通常通过JSX间接创建。理论上你可以手动创建:

const element = {
  type: 'div',
  props: { className: 'box', children: 'Hello' }
};
// 但 React 要求 $$typeof 等内部属性,不建议手动创建

追问4:函数组件返回的 Element 和直接写的 Element 有区别吗?

答: 最终没有区别。无论是直接写的<div />还是组件返回的<div />,最终都是同一种Element对象。区别在于中间过程:组件的Element需要被”展开”才能得到最终的DOM Element树。

追问5:React 17 之后有什么变化?

答: React 17 引入了新的JSX转换,不再需要显式导入React:

// React 17 之前
import React from 'react';
const element = <div />;  // 编译成 React.createElement('div')

// React 17 之后
const element = <div />;  // 编译成 _jsx('div'),不依赖React导入

但Element的本质没有变化,仍然是普通JS对象。


六、类比理解(面试加分点)

可以把 React 比作餐厅:

  • Element = 菜单上的菜品描述(”宫保鸡丁:鸡肉、花生、辣椒”)
    • 只是文字描述,不是真正的菜
    • 不可变,如果你想换菜,需要重新写描述
  • Component = 厨师(函数组件)或菜谱(类组件)
    • 厨师根据订单(props)做菜(返回Element)
    • 菜谱定义了做菜的步骤(render方法)
  • 真实DOM = 端上桌的菜
    • 是真正被用户看到和交互的东西
    • 只能由Element通过渲染得到

七、面试参考答案(精简版)

“React Element 和 Component 的核心区别是:Element是一个描述UI的普通JS对象,包含typepropschildren等属性,类似于虚拟DOM节点;Component是一个函数或类,接收props并返回一个Element树。Element是不可变的,由React.createElement或JSX创建;Component是可复用的UI模板,可以是函数组件或类组件。在渲染过程中,React会递归地将Component展开成最终的Element树,再映射成真实DOM。简单记忆:Component是工厂,Element是产品。”


快速记忆口诀

Element是对象,描述长啥样
Component是函数,返回Element树
组件大写字母开,元素小写DOM来
工厂产品要分清,面试再也不犯晕

THE END
喜欢就支持一下吧
点赞10 分享