pin_drop当前位置:知识文库 ❯ 图文

框架集成:04 React组件与HTML - 详细教程与实战指南

一、教程简介

组件是React的基石,每个React应用都是由组件构成的组件树。组件本质上是一个返回UI描述的函数(或类),它接收props作为输入,返回描述HTML结构的React元素。本教程将深入讲解函数组件与类组件、Props传递、Children组合模式、组件通信、高阶组件、Render Props以及常见的组件设计模式,帮助你掌握React组件化开发的精髓。


二、函数组件

函数组件是React推荐的组件编写方式,它是一个纯函数,接收props返回React元素:

代码示例

function UserCard({ name, email, role = 'user', avatar }) {
  const roleBadge = {
    admin: 'badge-admin',
    user: 'badge-user',
    guest: 'badge-guest'
  };
  const roleLabel = {
    admin: '管理员',
    user: '普通用户',
    guest: '访客'
  };

  return (
    <div className="user-card">
      <img className="user-avatar" src={avatar} alt={name} />
      <div className="user-info">
        <h3>
          {name}
          <span className={`badge ${roleBadge[role]}`}>{roleLabel[role]}</span>
        </h3>
        <p>{email}</p>
      </div>
    </div>
  );
}

// 带状态的函数组件(使用Hooks)
function StatsPanel() {
  const [stats, setStats] = React.useState({
    users: 1250,
    orders: 386,
    revenue: 52800
  });

  function refreshStats() {
    setStats({
      users: stats.users + Math.floor(Math.random() * 10),
      orders: stats.orders + Math.floor(Math.random() * 5),
      revenue: stats.revenue + Math.floor(Math.random() * 1000)
    });
  }

  return (
    <div>
      <div className="stats-grid">
        <div className="stat-card" style={{ background: 'linear-gradient(135deg, #667eea, #764ba2)' }}>
          <div className="stat-value">{stats.users.toLocaleString()}</div>
          <div className="stat-label">用户数</div>
        </div>
        <div className="stat-card" style={{ background: 'linear-gradient(135deg, #f093fb, #f5576c)' }}>
          <div className="stat-value">{stats.orders.toLocaleString()}</div>
          <div className="stat-label">订单数</div>
        </div>
        <div className="stat-card" style={{ background: 'linear-gradient(135deg, #4facfe, #00f2fe)' }}>
          <div className="stat-value">¥{stats.revenue.toLocaleString()}</div>
          <div className="stat-label">收入</div>
        </div>
      </div>
      <button onClick={refreshStats}>刷新数据</button>
    </div>
  );
}

三、类组件

类组件是React早期的组件编写方式,使用ES6 class语法:

代码示例

// 类组件:计时器
class Timer extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      seconds: 0,
      isRunning: false
    };
    this.intervalId = null;
  }

  start = () => {
    if (this.state.isRunning) return;
    this.setState({ isRunning: true });
    this.intervalId = setInterval(() => {
      this.setState(prev => ({ seconds: prev.seconds + 1 }));
    }, 1000);
  };

  pause = () => {
    this.setState({ isRunning: false });
    clearInterval(this.intervalId);
  };

  reset = () => {
    this.setState({ seconds: 0, isRunning: false });
    clearInterval(this.intervalId);
  };

  componentWillUnmount() {
    clearInterval(this.intervalId);
  }

  formatTime(totalSeconds) {
    const hours = Math.floor(totalSeconds / 3600);
    const minutes = Math.floor((totalSeconds % 3600) / 60);
    const seconds = totalSeconds % 60;
    return `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}`;
  }

  render() {
    return (
      <div className="timer">
        <div className="timer-value">{this.formatTime(this.state.seconds)}</div>
        <div className="timer-controls">
          {!this.state.isRunning ? (
            <button className="btn-start" onClick={this.start}>开始</button>
          ) : (
            <button className="btn-pause" onClick={this.pause}>暂停</button>
          )}
          <button className="btn-reset" onClick={this.reset}>重置</button>
        </div>
      </div>
    );
  }
}
特性 函数组件 类组件
定义方式 function / 箭头函数 class extends React.Component
状态管理 useState Hook this.state / this.setState
生命周期 useEffect Hook componentDidMount等
this指向 无this问题 需要绑定this
性能 更轻量 稍重(创建实例)
推荐度 推荐 不推荐新代码使用

四、Props传递与Children组合

代码示例

// Card组件 - Children组合模式
function Card({ title, children, footer }) {
  return (
    <div className="card">
      {title && <div className="card-header">{title}</div>}
      <div className="card-body">{children}</div>
      {footer && <div className="card-footer">{footer}</div>}
    </div>
  );
}

// Alert组件 - 不同类型的提示
function Alert({ type = 'info', icon, children }) {
  const icons = { success: '✓', warning: '⚠', error: '✕', info: 'ℹ' };
  return (
    <div className={`alert alert-${type}`}>
      <span className="alert-icon">{icon || icons[type]}</span>
      <span>{children}</span>
    </div>
  );
}

// Layout组件 - 多插槽组合
function Layout({ sidebar, children, aside }) {
  return (
    <div className="layout">
      <div className="layout-sidebar">{sidebar}</div>
      <div className="layout-main">{children}</div>
      <div className="layout-aside">{aside}</div>
    </div>
  );
}

五、组件通信

代码示例

// 父子通信:通过props和回调函数
function ShoppingCart({ cart, onRemove, total }) {
  return (
    <div>
      <h4>购物车</h4>
      {cart.length === 0 ? (
        <p style={{ color: '#999', textAlign: 'center', padding: '20px' }}>购物车为空</p>
      ) : (
        <div>
          {cart.map(item => (
            <div key={item.id} className="cart-item">
              <span>{item.name} x {item.count}</span>
              <span>¥{(item.price * item.count).toFixed(2)}
                <button onClick={() => onRemove(item.id)}>移除</button>
              </span>
            </div>
          ))}
          <div className="total">合计:¥{total.toFixed(2)}</div>
        </div>
      )}
    </div>
  );
}

function ProductList({ products, onAdd }) {
  return (
    <div>
      <h4>商品列表</h4>
      <div className="product-list">
        {products.map(product => (
          <div key={product.id} className="product-item">
            <div style={{ fontWeight: 'bold' }}>{product.name}</div>
            <div style={{ color: '#e74c3c' }}>¥{product.price}</div>
            <button onClick={() => onAdd(product)}>加入购物车</button>
          </div>
        ))}
      </div>
    </div>
  );
}

// 父组件管理状态,子组件通过回调通信
function ShopApp() {
  const products = [
    { id: 1, name: 'React书籍', price: 79.9 },
    { id: 2, name: 'Vue书籍', price: 59.9 },
    { id: 3, name: 'Angular书籍', price: 69.9 },
    { id: 4, name: 'Svelte书籍', price: 49.9 }
  ];

  const [cart, setCart] = React.useState([]);

  function addToCart(product) {
    setCart(prev => {
      const existing = prev.find(item => item.id === product.id);
      if (existing) {
        return prev.map(item =>
          item.id === product.id
            ? { ...item, count: item.count + 1 }
            : item
        );
      }
      return [...prev, { ...product, count: 1 }];
    });
  }

  function removeFromCart(id) {
    setCart(prev => prev.filter(item => item.id !== id));
  }

  const total = cart.reduce((sum, item) => sum + item.price * item.count, 0);

  return (
    <div>
      <ProductList products={products} onAdd={addToCart} />
      <ShoppingCart cart={cart} onRemove={removeFromCart} total={total} />
    </div>
  );
}

六、高阶组件与Render Props

代码示例

// Render Props模式:Mouse追踪器
function Mouse({ render }) {
  const [position, setPosition] = React.useState({ x: 0, y: 0 });

  function handleMouseMove(e) {
    const rect = e.currentTarget.getBoundingClientRect();
    setPosition({
      x: e.clientX - rect.left,
      y: e.clientY - rect.top
    });
  }

  return (
    <div className="mouse-tracker" onMouseMove={handleMouseMove}>
      {render(position)}
    </div>
  );
}

// 高阶组件模式:withToggle
function withToggle(WrappedComponent) {
  return function ToggleHOC(props) {
    const [isOn, setIsOn] = React.useState(false);
    return (
      <WrappedComponent
        {...props}
        isOn={isOn}
        toggle={() => setIsOn(!isOn)}
      />
    );
  };
}

// 使用withToggle的组件
function SettingsPanel({ isOn, toggle, title }) {
  return (
    <div className="toggle-content">
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
        <h4 style={{ margin: 0 }}>{title}</h4>
        <button className={isOn ? 'btn-success' : 'btn-primary'} onClick={toggle}>
          {isOn ? '已开启' : '已关闭'}
        </button>
      </div>
      {isOn && <p style={{ marginTop: '10px', color: '#666' }}>功能已启用,可以进行相关操作。</p>}
    </div>
  );
}

const EnhancedSettings = withToggle(SettingsPanel);
特性 高阶组件 Render Props 自定义Hook
复用逻辑 包装组件 函数props Hook函数
嵌套问题 可能很深 中等 无嵌套
调试难度 较高 中等 较低
推荐度 旧模式 旧模式 推荐

七、组件设计模式

代码示例

// 模式1:Tabs组件(受控模式)
function Tabs({ activeTab, onTabChange, children }) {
  return (
    <div>
      <div className="tabs">
        {React.Children.map(children, child => (
          <button
            className={`tab ${activeTab === child.props.label ? 'active' : ''}`}
            onClick={() => onTabChange(child.props.label)}
          >
            {child.props.label}
          </button>
        ))}
      </div>
      <div className="tab-content">
        {React.Children.map(children, child =>
          child.props.label === activeTab ? child : null
        )}
      </div>
    </div>
  );
}

// 模式2:Accordion组件(非受控模式)
function Accordion({ items }) {
  const [openIndex, setOpenIndex] = React.useState(-1);

  return (
    <div>
      {items.map((item, index) => (
        <div key={index} className="accordion-item">
          <div className="accordion-header" onClick={() => setOpenIndex(openIndex === index ? -1 : index)}>
            <span>{item.title}</span>
            <span>{openIndex === index ? '−' : '+'}</span>
          </div>
          <div className={`accordion-body ${openIndex === index ? 'open' : ''}`}>
            {item.content}
          </div>
        </div>
      ))}
    </div>
  );
}

// 模式3:Modal组件(条件渲染模式)
function Modal({ isOpen, onClose, title, children }) {
  if (!isOpen) return null;
  return (
    <div className="modal-overlay" onClick={onClose}>
      <div className="modal-content" onClick={e => e.stopPropagation()}>
        <h3 className="modal-title">{title}</h3>
        {children}
        <div className="modal-actions">
          <button className="btn-secondary" onClick={onClose}>取消</button>
          <button className="btn-primary" onClick={onClose}>确认</button>
        </div>
      </div>
    </div>
  );
}

八、浏览器兼容性

特性 Chrome Firefox Safari Edge 说明
函数组件 全部 全部 全部 全部 React基础API
类组件 全部 全部 全部 全部 React基础API
Hooks (useState等) 60+ 60+ 12+ 79+ React 16.8+
React.Fragment 全部 全部 全部 全部 React 16.2+
React.Children 全部 全部 全部 全部 React基础API
defaultProps 全部 全部 全部 全部 React基础API

九、注意事项与最佳实践

  • 优先使用函数组件:React官方推荐使用函数组件+Hooks,类组件仅用于维护旧代码

  • Props是只读的:组件绝不能修改自己的props,这是React单向数据流的基础

  • 合理使用默认值:使用默认参数或defaultProps为props提供默认值

  • 避免prop drilling:当props需要传递多层时,考虑使用Context或状态管理库

  • 组件职责单一:每个组件只做一件事,复杂组件应拆分为更小的子组件

  • 提取可复用逻辑:使用自定义Hook替代高阶组件和Render Props


十、代码规范示例

代码示例

// 不推荐:庞大的单体组件
function Dashboard() {
  const [users, setUsers] = useState([]);
  const [orders, setOrders] = useState([]);
  const [stats, setStats] = useState({});
  // 100行渲染逻辑...
}

// 推荐:拆分为小组件
function Dashboard() {
  return (
    <div>
      <UserSummary />
      <OrderList />
      <StatsPanel />
    </div>
  );
}

// 不推荐:直接修改props
function BadComponent({ items }) {
  items.push('new item'); // 不要修改props!
}

// 推荐:使用状态管理数据
function GoodComponent({ items }) {
  const [localItems, setLocalItems] = useState(items);
  function addItem() {
    setLocalItems([...localItems, 'new item']);
  }
}

// 不推荐:深层prop drilling
<GrandParent>
  <Parent theme={theme}>
    <Child theme={theme}>
      <GrandChild theme={theme} /> {/* theme传了3层 */}
    </Child>
  </Parent>
</GrandParent>

// 推荐:使用Context
const ThemeContext = createContext();
<ThemeContext.Provider value={theme}>
  <GrandParent>
    <Parent>
      <Child>
        <GrandChild /> {/* 通过useContext(ThemeContext)获取 */}
      </Child>
    </Parent>
  </GrandParent>
</ThemeContext.Provider>

十一、常见问题与解决方案

常见问题

函数组件和类组件哪个性能更好?

函数组件略优,因为它不需要创建类实例,内存占用更少。但实际差异很小,不应作为选择的主要依据。选择函数组件的主要原因是Hooks更简洁、更易组合。

什么时候用受控组件,什么时候用非受控组件?

如果父组件需要知道和控制子组件的状态(如表单验证、联动),用受控组件。如果组件状态是内部实现细节(如手风琴展开/折叠),用非受控组件。

高阶组件和自定义Hook怎么选?

优先使用自定义Hook。Hook更简洁、没有嵌套地狱问题、更容易类型推导。高阶组件主要用于需要操作包裹组件props或渲染结构的场景。

children prop有什么限制?

children可以是任何类型:字符串、元素、数组、函数(Render Props)甚至undefined。但注意React.Children.map会过滤掉null和undefined。

如何调试组件的props和state?

使用React DevTools浏览器扩展,可以查看组件树、props、state和hooks。也可以在代码中使用console.log,但注意不要在render中产生副作用。


十二、总结

本教程深入讲解了React组件与HTML模板的关系。函数组件是React推荐的组件编写方式,通过Hooks管理状态和副作用。Props是组件间通信的基础,遵循单向数据流原则。Children组合模式提供了灵活的组件组合能力,让组件更具复用性。高阶组件和Render Props是旧的模式,现代React推荐使用自定义Hook来复用逻辑。常见的组件设计模式包括受控/非受控组件、Tabs、Accordion、Modal等,理解这些模式有助于构建可维护的组件库。下一教程将介绍React事件处理与HTML的关系。

标签: React组件 函数组件 类组件 Props传递 组件通信 高阶组件 设计模式

本文涉及AI创作

内容由AI创作,请仔细甄别

list快速访问

上一篇: 框架集成:03 React JSX与HT - 详细教程与实战指南 下一篇: 框架集成:05 React事件处理与HT - 详细教程与实战指南

poll相关推荐