从泥瓦匠到建筑师:掌握 React 组件化,轻松驾驭复杂前端项目

73 阅读8分钟

在 JSX 里搭积木:初探 React 的组件化哲学

现代前端开发早已告别了“写页面”的时代,转而进入“构建界面系统”的新纪元。React 正是这场范式迁移中的关键推手——它不提供模板语言,也不依赖指令系统,而是将 UI 视为 JavaScript 的自然延伸。通过两个简洁的示例文件,我们可以清晰地看到 React 如何以函数为砖、以状态为胶,搭建起一个声明式、可组合的用户界面世界。


JSX:不是模板,而是 UI 的声明式语法

乍看之下,JSX 极易被误认为是“在 JavaScript 里嵌套 HTML”。但实质上,它是一种用于描述 UI 结构的声明式语法扩展,其本质是 React.createElement() 的语法糖。

const element = <h2>JSX 是 React 中用于描述用户界面的语法扩展</h2>;
const equivalent = createElement('h2', null, 'JSX 是 React 中用于描述用户界面的语法扩展');

二者在运行时完全等价。JSX 的存在,并非为了炫技,而是为了让开发者能以接近 HTML 的直观方式表达组件结构,同时保留在 JavaScript 环境中进行逻辑控制的能力。

image.png


组件即函数:封装与复用的基本单元

React 将组件定义为返回 JSX 的纯函数。这种设计看似简单,却蕴含深意:

function Header() {
  return <header><h1>掘金首页</h1></header>;
}

const Sidebar = () => <aside>侧边栏内容</aside>;

每个组件都是一个独立的功能单元,内聚了结构、样式(通常通过 CSS 模块引入)和交互逻辑。更重要的是,它们可以像乐高积木一样自由组合(简单模拟一下掘金的首页布局):

image.png


//根组件
function JuejinHeader(){
  return(
    
    <div>
    <header>
      <h1>掘金首页</h1>
    </header>
    
    </div>
  )
}
const Ariticles = () => {
  return(
    <div>
      Articles
    </div>
  )
}
const Checkin = () => {
  return(
    <div>
      Checkin
    </div>
  )
}
const TopArticles = () => {
  return(
    <div>
      TopArticles
    </div>
  )
}
function App(){
  
  return(
    <div>
      {/* <h1>hello <b>React!</b></h1> */}
      {/* 头部组件 */}
      <JuejinHeader/>
      <main>
      
      <Ariticles/>
      <aside>
        <Checkin/>
        <TopArticles/>
      </aside>
    </main>
    </div>
  )
}

export default App

此时,整个应用不再是扁平的 DOM 树,而是一棵语义清晰的组件树。开发者不再操作 <div><span>,而是操作 <Header /><Sidebar /> 这样的抽象单元——这正是现代 UI 工程的核心思想。


函数为何成为组件的最佳载体?

将组件实现为函数,绝非偶然。函数天然具备:

  • 输入输出机制(通过 props 接收数据,返回 JSX)
  • 无副作用的纯度(理想情况下,相同输入总产生相同输出)
  • 可组合性与可测试性

配合 Hooks(如 useState),函数组件还能拥有状态和生命周期能力,彻底摆脱了类组件的繁复。例如:

const [name, setName] = useState("vue");
setTimeout(() => setName("react"), 3000);

状态变更后,组件函数自动重新执行,生成新的 UI 快照。这种“状态驱动视图”的模式,让数据流变得可预测、可追踪。


React 的“规矩”:约束带来秩序

React 对组件编写设定了若干硬性规则,初看像是限制,实则是工程化的基石。

单一根节点(Single Root)

每个组件必须返回恰好一个根元素。若需包裹多个兄弟节点,可使用 Fragment(简写为 <>...</>):

return (
  <>
    <h1>标题</h1>
    <p>内容</p>
  </>
);

此举避免了无意义的 DOM 嵌套,同时强制组件保持单一职责。

组件命名首字母大写

React 通过首字母大小写区分原生标签与自定义组件:

  • <div> → 原生 HTML 元素
  • <ArticleList /> → 自定义组件

若误写为 <articleList />,React 会将其视为未知 HTML 标签,导致渲染失败。这一约定虽小,却是组件系统语义清晰的关键保障。


React 的核心语法习惯:用 JavaScript 思考 UI

React 的设计哲学可概括为:不发明新语法,而是最大化利用 JavaScript 本身的能力。以下几点尤为典型:
从一段代码看react的几个基本语法


import {useState,createElement} from 'react'

import './App.css'
function App(){
  
  const [name,setName] = useState("vue")
  const [todos,setTodos] =useState([
    {id:1,title:"学习react",done:false},
    {id:2,title:"学习node",done:true},
    {id:3,title:"学习angular",done:false},
  ])
  const [isLoggedIn,setIsLoggedIn] = useState(false)
  const toggleLogin = () => {
    setIsLoggedIn(!isLoggedIn);
  };
  //语法糖,主要是简化模板开发,提升代码的可读性
  const elemnet =<h2>JSX 是 React 中用于描述用户界面的语法扩展</h2>
  const element2 = createElement('h2',null,'JSX 是 React 中用于描述用户界面的语法扩展')
  setTimeout(()=>{
    setName("react")
  },3000)
 
  return(
    //文档碎片节点
    <>
    <h1>Hello <span className="title">{name}!</span></h1>
    {elemnet}
    {element2}
    {
      todos.length>0 ?(
         <ul>
      {
      //xml in js
        todos.map((todo)=>{
          return(
            <li key={todo.id}>{todo.title}</li>
          )
        })
      }
    </ul>
      ):
      (
        <div>暂无待办事项</div>
      )
    }
   {
    isLoggedIn ? <div>已登录</div> : <div>未登录</div>
   }
   <button onClick={toggleLogin}>
     {isLoggedIn ? "退出登录" : "登录"}
   </button>

    </>
    
  )
}
export default App

值得注意的是,由于 JSX 写在 JavaScript 上下文中,某些关键字需做转义:
classclassName
forhtmlFor
这并非设计缺陷,而是一种有意为之的约束——UI 不再是外部模板,而是程序逻辑的一部分

效果图:

屏幕录制 2025-12-12 222850.gif

1. 条件渲染:用三元运算符或逻辑与

{isLoggedIn ? <Dashboard /> : <LoginPrompt />}
{user && <UserProfile user={user} />}

没有 v-if,没有 *ngIf,只有原生 JavaScript 表达式。逻辑越复杂,越应提前计算变量,保持 JSX 简洁。

2. 列表渲染:拥抱 .map()

{items.map(item => (
  <li key={item.id}>{item.name}</li>
))}

每个列表项必须携带唯一 key,帮助 React 高效比对虚拟 DOM。使用数据 ID 而非数组索引作为 key,是避免渲染错乱的最佳实践。

3. 事件处理:传递函数引用

<button onClick={handleClick}>点击</button>

注意:此处传入的是 handleClick 函数本身,而非调用结果 handleClick()。后者会导致函数在每次渲染时立即执行,引发无限循环。

4. 注释语法特殊

JSX 中的注释需包裹在 {/* ... */} 中:

{/* 这是合法的 JSX 注释 */}
<div>Hello</div>

普通 // 注释在 JSX 块内无效,这是语法解析器的硬性要求。

5. 花括号 {}:JSX 中的“魔法门”

在 JSX 中,花括号 {} 是通往 JavaScript 世界的唯一入口。它允许你在声明式模板中嵌入任意合法的 JavaScript 表达式——变量、函数调用、对象属性、甚至三元运算,皆可通行。

<h1>Hello, {user.name}!</h1>
<p>当前共有 {items.length} 项任务</p>
<div>{formatDate(new Date())}</div>

需要注意的是:

  • 只能放表达式,不能放语句 。例如 ifforvar 等语句不能直接写在 {} 中;
  • 若需复杂逻辑,应提前在组件函数体中计算好,再将结果传入 JSX;
  • 表达式求值结果若为 nullundefined 或 false,React 会自动忽略,不渲染任何内容(这常用于条件显示)。

花括号就像 JSX 的“呼吸孔”——让静态结构得以与动态逻辑自由交换气息,却又不至于破坏其声明式的整洁骨架。


6. 样式引入:从全局污染到模块化隔离

React 并未内置样式方案,而是将选择权交还给开发者。常见的 CSS 引入方式有以下几种:

(1)传统 CSS 文件导入
import './App.css';

这是最直接的方式,适用于快速原型或小型项目。但需注意:CSS 仍是全局作用域,类名冲突风险依然存在。

(2)CSS Modules(推荐用于中大型项目)
import styles from './App.module.css';

// 使用时通过对象访问
<div className={styles.container}>内容</div>

构建工具(如 Webpack)会自动为类名添加哈希后缀(如 container_3xK9s),实现样式局部作用域,彻底避免命名污染。

(3)内联样式(Inline Styles)
const headerStyle = {
  color: 'teal',
  fontSize: '24px',
  margin: '1rem 0'
};

<h1 style={headerStyle}>标题</h1>

内联样式以 JavaScript 对象形式书写,属性名采用驼峰命名(如 fontSize 而非 font-size)。虽然灵活,但难以复用,且不支持伪类、媒体查询等高级特性,通常仅用于动态样式场景。

(4)CSS-in-JS(如 styled-components、Emotion)

这类方案将样式与组件深度绑定,实现真正的“组件即单元”。虽不在基础语法范畴,但体现了 React 社区对“关注点聚合”的极致追求。

无论采用哪种方式,React 的核心理念始终如一:样式是组件的一部分,而非外部附属品。正如结构用 JSX 描述、逻辑用函数封装,样式也应被纳入组件的自治边界之内。


与 Vue 的路径分野:两种响应式,两种世界观

Vue 与 React 都推崇组件化与响应式,但实现路径迥异:

  • Vue 采用“增强 HTML”策略,通过指令(如 v-ifv-for)扩展模板能力,降低学习曲线;
  • React 则坚持“UI as Code”,将一切逻辑交还给 JavaScript,追求更强的表达力与一致性。

前者像一位体贴的向导,后者则更像一位严格的教练——它不替你做决定,但赋予你完整的控制权。


从 DOM 操作到组件组合:开发范式的跃迁

过去,前端开发者如同泥瓦匠,逐块砌起 <div><span>;如今,React 让我们化身建筑设计师,先规划模块(组件),再组装系统(组件树)。审查元素时,看到的不再是杂乱的标签,而是 <App><Header><Navigation>... 这样的语义结构。

这种转变,不仅是工具的升级,更是思维的进化:UI 不再是静态文档,而是一个由状态驱动、由组件构成的动态系统

React 或许门槛略高,但它所倡导的函数式思维、声明式编程与组件化架构,正是构建复杂前端应用的坚实地基。正如一句业内调侃所言:

“在 React 里,你不是在写网页,而是在用 JavaScript 编排一场 UI 的交响乐。”