A

对 JSX 的理解

2025-06-03 18:09

官网文档是值得反复研读的

今天,随着我对 JSX 的进一步探索,想写下我对它的一些理解。对我来说,学习并不仅仅是掌握一项技术,更是理解它背后逻辑的过程。就像对待每一门技术一样,理解它的原理、它的存在方式,才是真正将它“内化”的关键。

什么是JSX?

记得刚接触 React 的时候,我以为 JSX 是 React 独有的语法。后来才意识到,实际上 JSX 并不是 React 独有的,它只是 React 官方推荐的语法扩展。JSX 可以看作是 JavaScript 的一个语法糖,它让 JavaScript 代码看起来像 HTML,而官方描述是 JSX and React 是相互独立的 东西。但它们经常一起使用,但你 可以 单独使用它们中的任意一个,JSX 是一种语法扩展,而 React 则是一个 JavaScript 的库。

JSX 本质上是一种 JavaScript 的语法扩展,它看起来像模版语法,但是它具有JavaScript 的所有功能。也就是说它具备JS的所有能力,并且能做到JS无法做到的能力,比如自定义组件。

  • 既然JSX 是JS的语法扩展,浏览器能否识别它?答案是否定的,JSX无法被直接解析。根据官方文档描述:JSX 会被编译为 React.createElement()React.createElement() 将返回一个叫作“React Element”的 JS 对象。

JSX 需要被编译,而编译这个动作就是由 Babel 来完成的。

Babel 是个工具链, 主要用于将 ECMAScript 2015+ 版本的代码转换为向后兼容的 JavaScript 语法,以便能够运行在当前和旧版本的浏览器或其他环境中。

JS
// 编译前 const firstName = 'Dan' const lastName = 'Abramov' conso.log(hello ${firstName} ${lastName} !) // 编译后 var firstName = "Dan"; var lastName = "Abramov"; conso.log("hello ".concat(firstName, " ").concat(lastName, " !"));

更多的内容可以通过访问 Babel官网 进行学习了解。我们只需要知道,Babel 就是一个转译工具,它具备把 JSX 语法转换为 JavaScript 代码的能力。

所以可以理解为写JSX其实就是在写React.createElement函数,JSX 就是 React.createElement 函数调用的语法糖。

JSX 的作用:提高开发效率与可读性

既然 JSX 只是 React.createElement 函数的语法糖,那么为什么不直接使用 React.createElement 来编写代码呢,这样还能省一个 Babel 编译的步骤。

React 框架有一个著名的公式:UI=render(data) ,表达的思想就是UI是由数据决定的,换句话说就是你给我数据(data),我返回你界面(UI),而这个过程由一个纯函数 render() 来完成。比如

JSX
function Welcome({ name }) { return <h1>Hello, {name}</h1>; }

你传不同的 name,就会渲染不同的内容。这种设计是声明式编程(Declarative Programming)的一种体现。

并且这个 render(data) 是纯函数:输入确定 → 输出就确定 也就是说:

  • 相同的 data,无论调用多少次,返回的 UI 是一样的。
  • 没有副作用(比如不去读 DOM、不去改全局变量、不发请求)。
  • 这种纯粹性,带来了高可预测性、好测试、好调试。 React 正是用这种“纯函数式渲染”方式来更新视图的。

所以说你使用纯js来写UI(也就是 React.createElement 的形式),在UI会复杂成:

JSX
return React.createElement('div', null, React.createElement('h1', null, '标题'), React.createElement('p', null, '内容'), React.createElement('ul', null, items.map(item => React.createElement('li', { key: item.id }, item.name) ) ) ); // 这种写法非常不直观、难读,嵌套结构很乱。而使用jsx就是 return ( <div> <h1>标题</h1> <p>内容</p> <ul> {items.map(item => <li key={item.id}>{item.name}</li>)} </ul> </div> ); // 这就清晰多了:结构清楚、逻辑集中、语义明确。

JSX 是 JavaScript 的“语法糖”,本质还是调用 React.createElement(),但它像 HTML 一样直观,能大大提高开发效率和可读性。

JSX 背后的处理:创建虚拟DOM 节点

前文提到 JSX 是 React.createElement 函数的语法糖,那么 React.createElement 函数又是做什么的呢?让我们从 React 源码 中一探究竟。 注意:本文摘取的 React 源码都来自于 19.0.0 版本。

小技巧:在 github repo 页面,通过单击键盘上的 . 键,可以启动在线 IDE 模式,实现更好的代码阅读体验。

JS
// 源码入口 packages/react/src/jsx/ReactJSXElement.js // createElement 有 3 个入参,这 3 个入参包含了 React 创建一个元素所需要知道的全部信息 // type:用于标识节点的类型。它可以是类似 “h1” “div” 这样的标准 HTML 标签字符串,也可以是函数组件、类组件、Fragment 类型等 // config:一个对象,包含了传给组件的 props,如 { className: 'btn', id: 'submit' },也可能包含特殊的 key 或 ref // children:代表组件的子节点,比如文本、React 元素、数组、null 等 export function createElement(type, config, children) { const props = {}; // props 将最终传递给 ReactElement let key = null; // key 是用于列表 diff 的唯一标识 // 从 config 中提取 key,并将其从 config 中移除(避免出现在 props 中) if (config != null) { // 如果 config.key 存在且合法,则转换为字符串形式存入 key if (hasValidKey(config)) { key = '' + config.key; } // 遍历 config,将除 key、__self、__source 之外的属性全部合并进 props // __self 和 __source 是用于调试的信息,不会作为实际 props 使用 for (let propName in config) { if (propName !== 'key' && propName !== '__self' && propName !== '__source') { props[propName] = config[propName]; } } } // 处理 children(从第三个参数开始都视为 children) const childrenLength = arguments.length - 2; // 如果只有一个 children,直接赋值(避免不必要的数组包裹) if (childrenLength === 1) { props.children = children; } // 如果有多个 children,将它们组成数组传入(也就是 props.children 是一个数组) else if (childrenLength > 1) { props.children = Array.prototype.slice.call(arguments, 2); } // 处理 defaultProps,如果组件设置了默认属性,则进行填充 // 注意:只有当 props 中没有该属性时才会使用默认值 if (type && type.defaultProps) { const defaultProps = type.defaultProps; for (let propName in defaultProps) { if (props[propName] === undefined) { props[propName] = defaultProps[propName]; } } } // 最后调用 ReactElement 创建 React 元素对象(虚拟 DOM 节点) // 这里省略了 owner、ref、source 等参数的处理(为了简化) return ReactElement(type, key, null, null, null, props); }
图片加载中...
图表示意图
createElement 函数做的事情其实不复杂,简单来说就是格式化数据,给 ReactElement 准备参数,算是一个数据处理层,真正“干活”的还是 ReactElement。

ReactElement就是 createElement返回的对象, 也就是常说的虚拟DOM 节点,它的结构大概是:

JS
{ $$typeof: Symbol(react.element), // 表明这是一个 React 元素 type, // 类型(字符串如 'div',或组件函数) key, // 用于列表的唯一标识 ref: null, // 可能会被赋值为 useRef/useImperativeHandle props, // 所有传入的 props,包括 children _owner: null, // 当前组件的 Fiber 所有者(调试用途) }

后面 虚拟DOM就会通过ReactDOM.render()渲染成真实的DOM。

总结

JSX 是 React 中一个非常重要的概念,它让我们能够以类似 HTML 的方式来编写组件,同时保持 JavaScript 的强大能力。虽然它本质上是 JavaScript 的语法糖,但它能够大大提高代码的可读性和开发效率。通过 JSX 和 React 的虚拟 DOM,开发者可以更加专注于 UI 的描述,而不必过多关注 DOM 操作的细节。这种声明式编程的方式,使得 React 成为了一个强大且易于使用的前端库。


每一次学习,都是一次思想的碰撞与升华。通过对 JSX 的理解,我们不仅仅是在掌握一种技术,更是在理解构建界面背后的思想和设计哲学。希望这篇文章能够为你打开一扇新的窗,带你走进更高效、更有深度的开发世界。

_愿我们在这条不断探索的技术道路上,彼此分享、彼此成长,未来能有更多的交流和共鸣。

参考资料