JSX语法糖本质

理解 JSX 到 JavaScript 的转换过程

问题

JSX 语法糖的本质是什么?它是如何被转换成 JavaScript 代码的?

解答

JSX 是一种 JavaScript 的语法扩展,它会被 Babel 等编译工具转换为普通的 JavaScript 函数调用。

JSX 转换示例

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

转换后的代码(React 17 之前)

// 转换为 React.createElement 调用
const element = React.createElement(
  'div',
  { className: 'container' },
  React.createElement('h1', null, 'Hello'),
  React.createElement('p', null, 'World')
);

转换后的代码(React 17+)

// 使用新的 jsx-runtime,不需要手动引入 React
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 的返回值

// createElement 返回一个普通的 JavaScript 对象(虚拟 DOM)
const element = {
  type: 'div',
  props: {
    className: 'container',
    children: [
      { type: 'h1', props: { children: 'Hello' } },
      { type: 'p', props: { children: 'World' } }
    ]
  }
};

简易版 createElement 实现

function createElement(type, props, ...children) {
  return {
    type,
    props: {
      ...props,
      // 处理子元素,文本节点转为对象
      children: children.map(child =>
        typeof child === 'object' ? child : createTextElement(child)
      )
    }
  };
}

function createTextElement(text) {
  return {
    type: 'TEXT_ELEMENT',
    props: {
      nodeValue: text,
      children: []
    }
  };
}

// 使用
const element = createElement(
  'div',
  { id: 'app' },
  createElement('span', null, 'Hello'),
  'World'
);

console.log(JSON.stringify(element, null, 2));

组件的转换

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

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

// 转换后:type 是函数引用而非字符串
const element = React.createElement(Welcome, { name: 'React' });

关键点

  • JSX 是语法糖,最终被编译为 React.createElement()jsx() 函数调用
  • 转换后返回的是普通 JavaScript 对象,即虚拟 DOM
  • HTML 标签转换后 type 是字符串,组件转换后 type 是函数/类引用
  • React 17+ 使用新的 jsx-runtime,无需手动 import React
  • 这种设计使 React 可以跨平台,虚拟 DOM 可渲染到不同目标(DOM、Native、Canvas)