🧩 React 组件化进阶:像乐高大师一样搭建你的应用世界!

🧩 React 组件化进阶:像乐高大师一样搭建你的应用世界!

"当你在拼接 DOM 碎片时,我在组装预制的功能模块------这就是组件化思维的分水岭!" 🚀


🔍 重新认识 Vite:你的前端工程"智能工厂"

(图示:现代化工厂流水线示意图)

传统开发 vs Vite 工程化:

  • 手工小作坊:直接写 HTML/CSS/JS → 适用于微型页面
  • Vite 智能工厂
    • 🏗️ 自动构建流水线(代码编译/打包)
    • 🔥 实时热更新引擎(修改即生效)
    • 📦 模块化装载系统(依赖管理)
    • 闪电开发服务器(端口 5173 的奥秘)

为何需要特定端口?

就像工厂需要专用输电网(端口)供电,Vite 服务器实时处理:

  • 模块热替换(HMR)
  • CSS 预处理器编译
  • 文件路径重定向
  • API 请求代理
bash 复制代码
# 启动你的工厂流水线
npm run dev

# 输出:
  VITE v5.2.0  ready in 220 ms
  ➜  Local:   http://localhost:5173/
  ➜  Network: use --host to expose

🧱 组件:前端世界的乐高积木

何为组件: 在 React 里,组件是构建用户界面的基础单位。它把 UI 划分成多个独立、可复用的模块,每个模块都能独立维护。组件的核心优势是复用性、可维护性以及可测试性。

组件的表现显示是什么:

  • 类组件(Class Components)

类组件属于较早的组件定义方式,它借助 ES6 的 class 来继承React.Component,并且要定义render()方法。下面是类组件的示例:

javascript 复制代码
import React, { Component } from 'react';

class Welcome extends Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}
  • 函数组件(Function Components)

函数组件是更为简洁的组件定义方式,它其实就是 JavaScript 函数。函数组件接收 props 作为参数,然后返回 JSX 元素。下面是函数组件的示例:

javascript 复制代码
import React from 'react';

const Welcome = (props) => {
  return <h1>Hello, {props.name}</h1>;
};
  • 无状态组件(Stateless Components)

无状态组件是只负责渲染 UI,不管理自身状态的组件。类组件和函数组件都可以成为无状态组件。下面是无状态函数组件的示例:

javascript 复制代码
const Button = ({ label, onClick }) => (
  <button onClick={onClick}>{label}</button>
);
  • 有状态组件(Stateful Components)

有状态组件能够管理和更新自身状态。在过去,只有类组件可以是有状态组件,不过自从 React 16.8 引入 Hooks 之后,函数组件也能使用useState Hook 来管理状态了。下面是使用 Hooks 的有状态函数组件示例:

javascript 复制代码
import React, { useState } from 'react';

const Counter = () => {
  const [count, setCount] = useState(0);
  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>Click me</button>
    </div>
  );
};
  • 高阶组件(Higher-Order Components, HOC)

高阶组件是一种特殊的组件,它接收一个组件作为参数,然后返回一个新的组件。高阶组件主要用于代码复用和状态逻辑共享。下面是高阶组件的示例:

javascript 复制代码
const withLogging = (WrappedComponent) => {
  return class extends React.Component {
    componentDidMount() {
      console.log(`Component ${WrappedComponent.name} mounted`);
    }
    render() {
      return <WrappedComponent {...this.props} />;
    }
  };
};
  • 函数式组件与 Hooks

React 16.8 引入的 Hooks 让函数组件可以使用状态和生命周期方法等类组件的特性。常见的 Hooks 有useStateuseEffectuseContext等。下面是使用多个 Hooks 的函数组件示例:

javascript 复制代码
import React, { useState, useEffect } from 'react';

const UserProfile = ({ userId }) => {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    setLoading(true);
    fetch(`/api/users/${userId}`)
      .then((res) => res.json())
      .then((data) => {
        setUser(data);
        setLoading(false);
      });
  }, [userId]);

  return loading ? (
    <p>Loading...</p>
  ) : (
    <div>
      <h1>{user.name}</h1>
      <p>{user.email}</p>
    </div>
  );
};
  • 纯组件(Pure Components)

纯组件会自动浅比较 props 和 state,只有当它们发生变化时才会重新渲染。类组件可以通过继承React.PureComponent来成为纯组件。函数组件则可以使用React.memo来实现相同的功能。下面是纯函数组件的示例:

javascript 复制代码
const PureButton = React.memo(({ label, onClick }) => (
  <button onClick={onClick}>{label}</button>
));
组件类型 定义方式 状态管理 生命周期支持 性能优化 适用场景 代码复杂度 示例语法
类组件 继承 React.Component 有(this.state 完整 手动优化或PureComponent 复杂状态/生命周期依赖 class Counter extends Component { state={count:0}; render(){...} }
函数组件(无Hooks) 普通JS函数 纯展示组件 const Button = (props) => <button>{props.label}</button>
函数组件(有Hooks) 函数 + Hooks 有(useState useEffect模拟 React.memo+useCallback 大多数场景 const Counter=()=>{const [c,setC]=useState(0);return...}
纯组件 继承 React.PureComponent 完整 自动浅比较 性能敏感组件 class PureCounter extends PureComponent {...}
高阶组件(HOC) 函数接收组件返回新组件 取决于实现 取决于实现 需手动处理 代码复用/渲染劫持 const withAuth = (Comp) => (props) => {...}
Hooks组件 函数 + 组合Hooks 灵活 灵活 React.memo+自定义Hooks 复杂逻辑复用 中高 const Profile=()=>{const {user}=useAuth();...}

组件的选择建议

  • 优先选用函数组件和 Hooks,因为它们的代码更简洁,并且能更好地复用状态逻辑。
  • 只有在确实需要使用复杂的生命周期方法时,才考虑使用类组件。
  • 运用高阶组件和React.memo来优化性能和复用逻辑。

(图示:乐高积木拼装成复杂结构)

组件的本质 = HTML + CSS + JS 的功能单元:如果把组件比作积木,那传统的标签就如同沙子一般

jsx 复制代码
// 传统标签:原子级操作
<button>Click</button>
<input type="text" />

// React 组件:功能级封装
<PaymentForm />
<UserProfileCard />
<DataVisualizationChart />

TodoList 组件化拆解示例:

graph TD A[App] --> B[Header] A --> C[TodoList] A --> D[Footer] C --> E[TodoItem] C --> F[AddTodoForm]

组件开发黄金法则:

  1. 单一职责原则 :每个组件只解决一个问题(如 TodoItem 只负责单条待办)
  2. 高内聚低耦合:组件内部紧密协作,对外接口简单明确
  3. 可复用优先:像乐高积木一样设计可重复使用的组件

📂 项目目录:组件化工程的车间布局

markdown 复制代码
my-app/
├── src/
│   ├── components/    # 组件仓库
│   │   ├── Header/
│   │   │   ├── Header.jsx
│   │   │   └── Header.css
│   │   ├── TodoList/
│   │   │   ├── TodoList.jsx
│   │   │   ├── TodoItem.jsx
│   │   │   └── AddForm.jsx
│   ├── App.jsx        # 总装流水线
│   └── index.css      # 全局样式库

src文件内的代码,将会是我们未来主要使用修改的 模块化导入示例:

jsx 复制代码
// App.jsx
import Header from './components/Header/Header';
import TodoList from './components/TodoList/TodoList';

function App() {
  return (
    <div className="app">
      <Header />
      <TodoList />
    </div>
  );
}

⚙️ 组件化开发实战:TodoList 深度解构

这段代码实现了一个基础的待办事项列表应用,由三个核心组件构成:AppTodoListTodoFormTodos,我们来看看它们是如何联系起来的:

组件关系概览

  1. App 组件(根组件)

    • 作为整个应用的入口,渲染 TodoList 组件。
    • 无状态,仅负责组件组合。
  2. TodoList 组件(状态管理核心)

    • 管理待办事项的状态(todos)和标题(title)。
    • 提供添加待办的方法(handleAdd)。
    • 渲染 TodoForm(表单输入)和 Todos(列表展示)。
  3. TodoForm 组件(表单输入)

    • 负责处理用户输入和表单提交。
    • 通过回调函数(onAdd)将输入数据传递给父组件(TodoList)。
  4. Todos 组件(列表展示)

    • 接收父组件传递的 todos 数据并渲染列表。

完整代码注解

jsx 复制代码
//App.jsx
import { useState } from 'react'

import './App.css'
import TodoList from './components/TodoList'
function App() {
 

  return (
    <>
      <TodoList/>
      {/* 自定义的组件插入点 */}
    </>
  )
}

export default App
jsx 复制代码
//TodoList.jsx
//内置的hook 函数
import { useState } from 'react'
import '../Todo.css'
import TodoForm from './TodoForm'
import Todos from './Todos'
//为什么函数是一个组件?
function TodoList(){
    //数据驱动的界面
    //静态页面?
    //DOM编程  数组->map->join('')->innerHTML底层API 编程
    //缺点:低效、面向API
    //面向业务 懂业务
    //数据 -> 变化 -> 数据状态 -> 自动刷新页面 -> **数据**驱动页面 
    //返回数组,第一项是数据变量,第二项是一个函数 执行,并传入新的todos的值
    // 页面会自动刷新
    //挂载点 tbody
    //{todos.map}
    //setTodos DOM及动态更新
    //响应式界面开发
    //hi 数据状态 sethi 修改数据状态的方法
    //es6 解构
    const[hi,setHi]=useState('haha')
    // const hi=useState('haha')[0];
    // const setHi=useState('haha')[1];
    const [title,setTitle]=useState('Todo list')
    const [todos,setTodos]=useState([
        {
            id:1,
            text:'吃饭',
            completed:false//是否完成s
        }
    ])
    const handleAdd =(text)=>{
        setTodos([
            ...todos,
            // 把原来的保留
            {
                id:todos.length+1,
                text,
                completed:false
            }
        ])
    }
    // setTimeout(()=>{
    //     setTodos([
    //         ...todos,
    //         {
    //             id:2,
    //             text:'睡觉',
    //             completed:false//是否完成
    //         }
    //     ])
    //    setTitle('字节')
    //    setHi('加油')
    // },3000)
 
    return(
        <div className="container">
            <h1 className="title">{title}{hi}</h1>
            {/* 表单 */}
            <TodoForm onAdd={handleAdd}/>
            {/* 列表 */}
            <Todos todos={todos}/>
            {/* {
                //当下这个位置
                //数据为王,界面是被驱动的
                //数据驱动
                //数据绑定 data binding
                //发生改变 自动改变
                todos.map(todo=>(
                    <li>{todo.text}</li>
                ))
            } */}
        </div>
    )
}

export default TodoList;//将组件导出,向外输出
jsx 复制代码
//TodoForm.jsx
import { useState } from "react";
function TodoForm(props) {
    const onAdd=props.onAdd
    const[text,setText]=useState('打豆豆')
    const handleSubmit=(e)=>{
        //阻止默认的行为
        //由js控制
        e.preventDefault();
        //console.log(text);
        onAdd(text)
        //如何修改todos? 打报告
    }
    const handleChange=(e)=>{
        setText(e.target.value)
    }
    return (
        <form action="http://www.baidu.com" onSubmit={handleSubmit}>
            <input 
            type="text" 
            placeholder="请输入代办事项"
            // 占位置,一个输入框一定要写placeholder 增强语义化
            value={text}
            onChange={handleChange}
            />
            <button type='submit'>添加</button>
        </form>
    )

}
export default TodoForm;
jsx 复制代码
//Todos.jsx
//列表的渲染
function Todos(props){
    console.log(props,'/////')
    //父组件传过来的数据呢?传参
    const todos=props.todos
    return(
        <ul>
            {
                todos.map(todo=>(
                    <li key={todo.id}> {todo.text} </li>
                ))
            }

        </ul>
    )
}
export default Todos;

1. <TodoForm onAdd={handleAdd} /> 的作用

这行代码是在TodoList组件中渲染TodoForm组件,并通过onAdd属性传递了一个回调函数handleAdd。这个模式称为反向数据流(Child-to-Parent Communication):

  • 父组件(TodoList) 定义了状态todos和修改状态的函数handleAdd
  • 子组件(TodoForm) 通过props.onAdd接收这个函数,并在用户提交表单时调用它
  • 数据流向是单向的:从父组件到子组件,再通过回调函数返回

2. TodoForm 组件的实现

这个组件负责渲染一个输入表单,并将用户输入传递给父组件:

javascript 复制代码
function TodoForm(props) {
  const onAdd = props.onAdd; // 从父组件接收回调函数
  const [text, setText] = useState('打豆豆'); // 本地状态:输入框内容

  const handleSubmit = (e) => {
    e.preventDefault(); // 阻止表单默认提交行为
    onAdd(text); // 调用父组件的回调函数,传递输入内容
  };

  const handleChange = (e) => {
    setText(e.target.value); // 更新本地状态
  };

  return (
    <form action="http://www.baidu.com" onSubmit={handleSubmit}>
      <input 
        type="text" 
        placeholder="请输入代办事项"
        value={text} 
        onChange={handleChange} 
      />
      <button type='submit'>添加</button>
    </form>
  );
}

关键细节:

  • 受控组件 :输入框的值由 React 状态text控制,实现双向数据绑定

  • 事件处理

    • handleChange:实时更新输入框内容到本地状态
    • handleSubmit:阻止表单默认提交,调用父组件的onAdd函数
  • 数据流

    1. 用户输入内容 -> handleChange更新text状态
    2. 用户点击提交 -> handleSubmit触发onAdd(text)
    3. 父组件的handleAdd函数被调用,更新todos列表

3. Todos 组件的实现

这个组件负责渲染待办事项列表,接收父组件传递的todos数组:

javascript 复制代码
function Todos(props) {
  const todos = props.todos; // 从父组件接收todos数组
  
  return (
    <ul>
      {todos.map(todo => (
        <li key={todo.id}> {todo.text} </li>
      ))}
    </ul>
  );
}

关键细节:

  • props 接收数据 :通过props.todos获取父组件传递的待办事项列表
  • 列表渲染 :使用.map()方法遍历数组,生成<li>元素
  • key 属性 :每个列表项必须有唯一的key,这里使用todo.id确保 React 能正确识别每个元素

数据流总结

这个待办事项应用的数据流遵循单向数据流原则:

  1. TodoList 组件 持有状态todos和修改状态的函数handleAdd

  2. TodoListhandleAdd传递给TodoForm 作为onAdd属性

  3. TodoListtodos数组传递给Todos 组件作为todos属性

  4. TodoForm 在用户提交时调用onAdd(text),触发父组件的状态更新

  5. Todos 组件接收到更新后的todos数组,自动重新渲染列表

这种模式使得数据流向清晰可控,是 React 应用的核心设计模式。


🧠 组件化思维:从沙粒到摩天大楼的进化

(图示:从散沙到结构化建筑的演变过程)

前端开发的认知升级:

原始时代:HTML标签(5分,沙粒)、CSS样式(3分,颜料)

农耕时代:DOM操作(2分,手工烧砖)、jQuery(4分,简单工具)

工业时代:组件化(8分,预制构件)、工程化(7分,流水线生产)

组件化核心优势:

  • 高效复用 :一次开发,随处调用(如 <Button> 组件)
  • 团队协作:多人并行开发不同组件
  • 维护简易:故障组件可单独替换
  • 业务聚焦:开发者专注功能逻辑而非 DOM 操作

组件调试大法:专业前端开发人员必备

在chrome中下载插件chromewebstore.google.com/detail/reac...

然后通过终端给你的端口,打开你创建的react项目,右键,检查

在这里就可以观察该项目所有的组件,便于维护


💡 React 开发心法:数据驱动视图的艺术

传统 DOM 操作 vs React 数据驱动:

js 复制代码
// 传统方式:手动操作DOM(易出错)
document.getElementById('list').innerHTML += `
  <li>${newTodoText}</li>
`;

// React方式:更新数据,自动渲染(优雅!)
setTodos(prev => [...prev, newTodo]);

数据状态管理三原则:

  1. 单一数据源:状态提升到最近公共父组件
  2. 不可变数据 :永远通过 setState 更新状态
  3. 向下流动:数据通过 props 单向传递
jsx 复制代码
// 状态提升示例
function Parent() {
  const [data, setData] = useState([]); // 状态在此管理
  
  return (
    <ChildA data={data} />
    <ChildB onDataUpdate={setData} />
  );
}

🚀 成为组件化大师的终极秘诀

  1. 拆分思维训练

    • 拿到设计稿先画组件树
    • 按功能边界切割组件
    • 组件文件保持300行内
  2. 组件设计模式掌握

    markdown 复制代码
    - 容器组件 vs 展示组件
    - 高阶组件(HOC)
    - Render Props
    - 复合组件(Compound Components)
  3. 工具链精通

    bash 复制代码
    # 组件文档工具
    npm install -D storybook
    
    # 组件测试工具
    npm install -D @testing-library/react
  4. 性能优化意识

    • React.memo 缓存组件
    • 避免 props 不必要变更
    • 惰性加载组件(lazy + Suspense

"不会拆分组件的React开发者,就像用挖掘机搭积木------力量有余,精度不足!" 🏗️💥

相关推荐
满分观察网友z几秒前
vue的<router-link>的to里面的query和params的区别
前端
小约翰仓鼠1 分钟前
vue3表格使用Switch 开关
前端·javascript·vue.js
JiangJiang3 分钟前
🔥 面试官:Webpack 为什么能热更新?你真讲得清吗?
前端·面试·webpack
程序员小刘4 分钟前
如何优化React Native应用以适配HarmonyOS5?
javascript·react native·react.js·华为·harmonyos
anyup22 分钟前
快崩溃了!华为应用商店已经 4 次驳回我的应用上线
前端·华为·uni-app
Qian Xiaoo36 分钟前
前后端分离开发 和 前端工程化
前端
要加油哦~1 小时前
vue · 插槽 | $slots:访问所有命名插槽内容 | 插槽的使用:子组件和父组件如何书写?
java·前端·javascript
先做个垃圾出来………1 小时前
split方法
前端
前端Hardy2 小时前
HTML&CSS:3D图片切换效果
前端·javascript
豆包AI编程2 小时前
👾 豆包脑洞研究所| 创意应用有奖征集活动说明
ai编程