JSX 本质

JSX 是什么,如何被编译,最终变成什么

问题

JSX 的本质是什么?它是如何工作的?

解答

JSX 是 JavaScript 的语法扩展,它会被 Babel 编译成 React.createElement() 函数调用,最终返回一个描述 UI 的普通 JavaScript 对象(React Element)。

JSX 编译过程

// 你写的 JSX
const element = (
  <div className="container">
    <h1>Hello</h1>
    <p>World</p>
  </div>
);

// Babel 编译后(React 17 之前)
const element = React.createElement(
  'div',
  { className: 'container' },
  React.createElement('h1', null, 'Hello'),
  React.createElement('p', null, 'World')
);

// Babel 编译后(React 17+ 新转换)
import { jsx as _jsx, jsxs as _jsxs } from 'react/jsx-runtime';

const element = _jsxs('div', {
  className: 'container',
  children: [
    _jsx('h1', { children: 'Hello' }),
    _jsx('p', { children: 'World' })
  ]
});

React.createElement 返回值

// React.createElement 返回的对象结构
const element = {
  $$typeof: Symbol(react.element), // 标识这是 React 元素
  type: 'div',                      // 元素类型
  key: null,                        // 列表渲染的 key
  ref: null,                        // ref 引用
  props: {                          // 属性
    className: 'container',
    children: [
      { $$typeof: Symbol(react.element), type: 'h1', props: { children: 'Hello' } },
      { $$typeof: Symbol(react.element), type: 'p', props: { children: 'World' } }
    ]
  }
};

手写简易 createElement

function createElement(type, props, ...children) {
  return {
    $$typeof: Symbol.for('react.element'),
    type,
    key: props?.key || null,
    ref: props?.ref || null,
    props: {
      ...props,
      children: children.length === 1 ? children[0] : children
    }
  };
}

// 使用
const element = createElement(
  'div',
  { className: 'box' },
  createElement('span', null, 'text')
);

console.log(element);
// {
//   $$typeof: Symbol(react.element),
//   type: 'div',
//   key: null,
//   ref: null,
//   props: { className: 'box', children: { type: 'span', ... } }
// }

组件类型的处理

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

// JSX 使用组件
const element = <Welcome name="Sara" />;

// 编译后,type 是函数引用
const element = React.createElement(Welcome, { name: 'Sara' });

// 返回的对象
{
  $$typeof: Symbol(react.element),
  type: Welcome,  // 函数引用,不是字符串
  props: { name: 'Sara' }
}

关键点

  • JSX 是语法糖,不是模板语言,最终编译成函数调用
  • React.createElement(type, props, ...children) 返回普通 JS 对象
  • 原生标签的 type 是字符串(如 'div'),组件的 type 是函数/类引用
  • $$typeof 用于防止 XSS 攻击,JSON 无法表示 Symbol
  • React 17+ 使用新的 JSX 转换,不再需要手动引入 React