Skip to content

JSX、组件与单向数据流

很多人第一次学 React,会把注意力放在“JSX 长得像 HTML”。

但 React 这一层真正要解决的,不是“页面怎么写得像模板”,而是:

  1. UI 怎么拆成可组合的组件
  2. UI 结构怎么用 JavaScript 描述
  3. 数据怎么从父组件流向子组件
  4. 页面逻辑为什么可以和视图结构组织在一起

这篇文章重点讲:

  1. JSX 到底是什么
  2. React Element 和组件是什么关系
  3. 函数组件在现代 React 里的定位
  4. props 和单向数据流为什么重要
  5. 组件组合为什么比继承更常见

1. JSX 到底是什么

JSX 不是浏览器原生语法,也不是模板引擎本身。

JSX 是一种语法糖,用来把“组件树”和“UI 结构”写得更接近最终页面形态。

例如:

jsx
function WelcomeCard({ name }) {
  return <h1>Hello, {name}</h1>;
}

这段代码最终不会直接原样交给浏览器执行,而是会在编译阶段转成类似这样的调用:

js
function WelcomeCard({ name }) {
  return React.createElement("h1", null, "Hello, ", name);
}

🌟 所以 JSX 的核心价值不是“像 HTML”,而是 让 UI 结构、组件组合和数据插值能够自然地写在 JavaScript 里。


2. JSX 和 HTML 有什么本质区别

它们看起来很像,但不是一回事。

对比项JSXHTML
运行位置编译后进入 JavaScript 运行时浏览器原生直接解析
属性写法更接近 JavaScript,例如 classNameonClick更接近文档标记语言
表达式能力可以直接嵌入 JavaScript 表达式不能直接写 JavaScript 表达式
最终目标生成 React Element表达静态文档结构

例如:

jsx
function UserInfo({ user }) {
  const isVip = user.level === "VIP";

  return (
    <section className="user-card">
      <h2>{user.name}</h2>
      {isVip ? <span>VIP</span> : null}
    </section>
  );
}

这段 JSX 最重要的点,是 UI 结构里直接混入了:

  1. 变量读取
  2. 条件判断
  3. 组件组合

也就是说,它不是“HTML 里插一点 JS”,而是“用 JavaScript 描述 UI”。


3. React Element 和组件是什么关系

这两个概念很容易混。

3.1 React Element 是什么

React Element 可以看成 一份轻量的 UI 描述对象。

它通常会记录:

  1. 这是哪种节点
  2. 有哪些 props
  3. 子节点是什么

JSX 写出来的,不是 DOM,也不是组件实例,而更像是“下一步该渲染成什么 UI”的描述。

3.2 组件是什么

组件是生成这份 UI 描述的组织单元。

在现代 React 里,更常见的就是函数组件:

jsx
function ProductCard({ product }) {
  return (
    <article>
      <h3>{product.name}</h3>
      <p>{product.price}</p>
    </article>
  );
}

这个函数组件做的事情,本质上是:

  1. 接收输入 props
  2. 根据输入返回一棵 React Element 树

🌟 把这层关系记住:组件负责组织 UI,React Element 负责描述 UI。


4. 为什么现代 React 以函数组件为主

当前 React 的主线已经明显转向函数组件。

主要原因是:

  1. 更贴近 JavaScript 函数式组织方式
  2. 更容易通过 Hook 复用状态逻辑
  3. 组合方式比类继承更自然
  4. 更适合和现代 React 的渲染模型一起理解

这不等于类组件完全没价值,而是 现代 React 的新能力和主流实践,几乎都围绕函数组件展开。

所以这套笔记也会以函数组件作为主线。


5. props 到底是什么

props 可以看成组件的输入。

它主要解决的是,让组件从外部拿到配置、数据和回调,而不是把一切写死在组件内部。

例如:

jsx
function Button({ type, disabled, children, onClick }) {
  return (
    <button className={`btn btn-${type}`} disabled={disabled} onClick={onClick}>
      {children}
    </button>
  );
}

这里的:

  1. type
  2. disabled
  3. children
  4. onClick

都是外部传进来的输入。

props 最关键的边界是 只读。

也就是说:

  1. 组件可以使用它
  2. 组件不应该直接修改它

如果一个组件会自己改自己的输入,数据来源就会变得混乱。


6. 什么叫单向数据流

React 非常强调单向数据流。

它的核心含义是,数据通常从父组件流向子组件,更新动作通过回调再往上反馈。

例如:

jsx
function CounterPage() {
  const [count, setCount] = useState(0);

  return <Counter value={count} onIncrease={() => setCount(count + 1)} />;
}

function Counter({ value, onIncrease }) {
  return <button onClick={onIncrease}>count: {value}</button>;
}

这段代码里:

  1. count 从父组件传给子组件
  2. 子组件不直接改 count
  3. 子组件通过 onIncrease 通知父组件去更新

这样做的最大价值是:

  1. 状态来源更清楚
  2. 依赖方向更稳定
  3. 调试和排查更容易

7. children 为什么很重要

很多人一开始只把 children 当成“插槽内容”。

它其实是 React 组件组合能力里非常核心的一部分。

例如:

jsx
function Card({ title, children }) {
  return (
    <section className="card">
      <h3>{title}</h3>
      <div className="card-body">{children}</div>
    </section>
  );
}

使用时:

jsx
<Card title="订单信息">
  <OrderSummary />
</Card>

这里 Card 并不关心里面一定是什么组件,它只负责布局和结构容器。

这正是 React 很强调的思想,通过组合组装能力,而不是通过继承叠能力。


8. 为什么 React 更强调组合而不是继承

组件系统真正复杂起来之后,继承往往会带来:

  1. 层次关系越来越深
  2. 行为来源越来越难追踪
  3. 复用方式越来越僵硬

而组合更自然的做法是:

  1. 把布局抽成组件
  2. 把行为抽成 Hook
  3. 把数据通过 props 传入
  4. 把可变区域通过 children 或插槽式 props 交给外部

工程上更常见的复用思路通常是:

  1. 组件组合
  2. 自定义 Hook
  3. 高阶组件或 render props 作为历史或特定场景方案

9. JSX 和组件层最常见的误区

9.1 以为 JSX 就是模板

不够准确。

它更像是 UI 的 JavaScript 表达方式。

9.2 以为组件只是为了拆文件

也不够。

组件真正重要的是:

  1. 抽象 UI 单元
  2. 建立输入输出边界
  3. 复用结构和行为组织方式

9.3 以为单向数据流只是“父传子”

也太浅了。

它真正重要的是,让状态的拥有者和更新入口都更清楚。


10. 一句话总结

React 在 JSX 和组件这一层最核心的设计,是用 JavaScript 描述 UI,用组件组织界面,再通过 props 和单向数据流建立稳定的数据边界。

基于 VitePress 构建的个人技术笔记。