React 性能优化与高级模式深度实战

本文深入探讨 React 高级特性、性能优化策略和架构模式,涵盖 Hooks 深度使用、渲染优化、状态管理、并发特性等核心领域,通过完整的实战案例展示如何构建高性能、可维护的大型 React 应用。

图片[1]-React 性能优化与高级模式深度实战指南

一、React Hooks 深度解析与高级模式

1.1 自定义 Hooks 架构设计

import React, { useCallback, useMemo, useRef, useEffect, useReducer } from 'react';
import { useRouter } from 'next/router';

/**
 * 高级数据获取 Hook - 支持缓存、重试、竞态处理
 */
export function useAdvancedFetch(url, options = {}) {
  const {
    enabled = true,
    cacheTime = 5 * 60 * 1000, // 5分钟缓存
    retryCount = 3,
    retryDelay = 1000,
    onSuccess,
    onError
  } = options;

  const [state, dispatch] = useReducer(fetchReducer, {
    data: null,
    error: null,
    status: 'idle',
    retryCount: 0
  });

  const cache = useRef(new Map());
  const abortControllerRef = useRef(null);
  const timeoutRef = useRef(null);

  const fetchReducer = (state, action) => {
    switch (action.type) {
      case 'FETCH_START':
        return { ...state, status: 'loading', error: null };
      case 'FETCH_SUCCESS':
        return { 
          ...state, 
          status: 'success', 
          data: action.payload,
          error: null,
          retryCount: 0
        };
      case 'FETCH_ERROR':
        return { 
          ...state, 
          status: 'error', 
          error: action.payload,
          retryCount: state.retryCount + 1
        };
      case 'FETCH_IDLE':
        return { ...state, status: 'idle', error: null };
      default:
        return state;
    }
  };

  const execute = useCallback(async (overrideUrl = url) => {
    if (!enabled || !overrideUrl) return;

    // 检查缓存
    const cacheKey = `${overrideUrl}-${JSON.stringify(options)}`;
    const cached = cache.current.get(cacheKey);
    
    if (cached && Date.now() - cached.timestamp < cacheTime) {
      dispatch({ type: 'FETCH_SUCCESS', payload: cached.data });
      onSuccess?.(cached.data);
      return;
    }

    // 取消之前的请求
    if (abortControllerRef.current) {
      abortControllerRef.current.abort();
    }

    abortControllerRef.current = new AbortController();
    dispatch({ type: 'FETCH_START' });

    try {
      const response = await fetch(overrideUrl, {
        ...options,
        signal: abortControllerRef.current.signal
      });

      if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status}`);
      }

      const data = await response.json();
      
      // 更新缓存
      cache.current.set(cacheKey, {
        data,
        timestamp: Date.now()
      });

      dispatch({ type: 'FETCH_SUCCESS', payload: data });
      onSuccess?.(data);
    } catch (error) {
      if (error.name === 'AbortError') return;

      // 重试逻辑
      if (state.retryCount < retryCount) {
        timeoutRef.current = setTimeout(() => {
          execute(overrideUrl);
        }, retryDelay * Math.pow(2, state.retryCount));
        return;
      }

      dispatch({ type: 'FETCH_ERROR', payload: error });
      onError?.(error);
    }
  }, [url, enabled, cacheTime, retryCount, retryDelay, onSuccess, onError]);

  const refetch = useCallback(() => {
    // 清除缓存重新获取
    const cacheKey = `${url}-${JSON.stringify(options)}`;
    cache.current.delete(cacheKey);
    execute();
  }, [execute]);

  const cancel = useCallback(() => {
    if (abortControllerRef.current) {
      abortControllerRef.current.abort();
    }
    if (timeoutRef.current) {
      clearTimeout(timeoutRef.current);
    }
    dispatch({ type: 'FETCH_IDLE' });
  }, []);

  // 自动执行
  useEffect(() => {
    if (enabled) {
      execute();
    }
    return cancel;
  }, [execute, enabled, cancel]);

  return {
    data: state.data,
    error: state.error,
    isLoading: state.status === 'loading',
    isError: state.status === 'error',
    isSuccess: state.status === 'success',
    refetch,
    cancel
  };
}

/**
 * 虚拟列表 Hook - 优化大型列表渲染
 */
export function useVirtualList(items, options = {}) {
  const {
    itemHeight = 50,
    overscan = 5,
    containerRef
  } = options;

  const [scrollTop, setScrollTop] = React.useState(0);
  const [containerHeight, setContainerHeight] = React.useState(0);

  // 容器尺寸监听
  useEffect(() => {
    if (!containerRef.current) return;

    const resizeObserver = new ResizeObserver((entries) => {
      const entry = entries[0];
      setContainerHeight(entry.contentRect.height);
    });

    resizeObserver.observe(containerRef.current);
    return () => resizeObserver.disconnect();
  }, [containerRef]);

  // 滚动处理
  useEffect(() => {
    const container = containerRef.current;
    if (!container) return;

    const handleScroll = () => {
      setScrollTop(container.scrollTop);
    };

    container.addEventListener('scroll', handleScroll, { passive: true });
    return () => container.removeEventListener('scroll', handleScroll);
  }, [containerRef]);

  // 计算可见项
  const visibleItems = useMemo(() => {
    if (!containerHeight) return [];

    const totalHeight = items.length * itemHeight;
    const startIndex = Math.max(0, Math.floor(scrollTop / itemHeight) - overscan);
    const endIndex = Math.min(
      items.length - 1,
      Math.floor((scrollTop + containerHeight) / itemHeight) + overscan
    );

    const visible = [];
    for (let i = startIndex; i <= endIndex; i++) {
      visible.push({
        index: i,
        data: items[i],
        style: {
          position: 'absolute',
          top: i * itemHeight,
          height: itemHeight,
          width: '100%'
        }
      });
    }

    return visible;
  }, [items, scrollTop, containerHeight, itemHeight, overscan]);

  const totalHeight = items.length * itemHeight;

  return {
    visibleItems,
    totalHeight,
    scrollTop
  };
}

/**
 * 防抖 Hook - 优化频繁更新
 */
export function useDebounce(value, delay) {
  const [debouncedValue, setDebouncedValue] = React.useState(value);

  useEffect(() => {
    const handler = setTimeout(() => {
      setDebouncedValue(value);
    }, delay);

    return () => {
      clearTimeout(handler);
    };
  }, [value, delay]);

  return debouncedValue;
}

/**
 * 路由状态管理 Hook
 */
export function useRouteState(initialState) {
  const router = useRouter();
  const [state, setState] = React.useState(() => ({
    ...initialState,
    ...router.query
  }));

  // 同步路由参数到状态
  useEffect(() => {
    if (router.isReady) {
      setState(prev => ({ ...prev, ...router.query }));
    }
  }, [router.isReady, router.query]);

  // 状态变化时更新路由
  const updateState = useCallback((updates) => {
    setState(prev => {
      const newState = { ...prev, ...updates };
      
      // 更新路由,但不滚动到顶部
      router.replace(
        {
          pathname: router.pathname,
          query: newState
        },
        undefined,
        { shallow: true, scroll: false }
      );

      return newState;
    });
  }, [router]);

  return [state, updateState];
}

二、性能优化策略与模式

2.1 渲染优化组件

import React, { memo, useMemo, useCallback, useRef, useEffect } from 'react';

/**
 * 高性能虚拟列表组件
 */
export const VirtualList = memo(({
  items,
  itemHeight = 50,
  overscan = 5,
  renderItem,
  className = '',
  ...props
}) => {
  const containerRef = useRef(null);
  const {
    visibleItems,
    totalHeight
  } = useVirtualList(items, {
    itemHeight,
    overscan,
    containerRef
  });

  const memoizedRenderItem = useCallback(({ index, data, style }) => (
    <div key={index} style={style}>
      {renderItem({ item: data, index })}
    </div>
  ), [renderItem]);

  return (
    <div
      ref={containerRef}
      className={`virtual-list ${className}`}
      style={{ 
        height: '100%', 
        overflow: 'auto',
        position: 'relative'
      }}
      {...props}
    >
      <div style={{ height: totalHeight, position: 'relative' }}>
        {visibleItems.map(memoizedRenderItem)}
      </div>
    </div>
  );
});

VirtualList.displayName = 'VirtualList';

/**
 * 惰性加载图片组件
 */
export const LazyImage = memo(({
  src,
  alt,
  placeholder = '/placeholder.jpg',
  threshold = 0.1,
  rootMargin = '50px',
  className = '',
  ...props
}) => {
  const [isLoaded, setIsLoaded] = React.useState(false);
  const [isInView, setIsInView] = React.useState(false);
  const imgRef = useRef(null);
  const observerRef = useRef(null);

  useEffect(() => {
    const img = imgRef.current;
    if (!img) return;

    observerRef.current = new IntersectionObserver(
      ([entry]) => {
        if (entry.isIntersecting) {
          setIsInView(true);
          observerRef.current?.unobserve(img);
        }
      },
      { threshold, rootMargin }
    );

    observerRef.current.observe(img);

    return () => {
      observerRef.current?.unobserve(img);
    };
  }, [threshold, rootMargin]);

  const handleLoad = useCallback(() => {
    setIsLoaded(true);
  }, []);

  return (
    <img
      ref={imgRef}
      src={isInView ? src : placeholder}
      alt={alt}
      className={`lazy-image ${isLoaded ? 'loaded' : 'loading'} ${className}`}
      onLoad={isInView ? handleLoad : undefined}
      loading="lazy"
      {...props}
    />
  );
});

LazyImage.displayName = 'LazyImage';

/**
 * 优化表单组件 - 防抖输入
 */
export const OptimizedInput = memo(({
  value,
  onChange,
  debounceMs = 300,
  placeholder,
  type = 'text',
  className = '',
  ...props
}) => {
  const [internalValue, setInternalValue] = React.useState(value);
  const debouncedValue = useDebounce(internalValue, debounceMs);

  // 同步外部值
  useEffect(() => {
    setInternalValue(value);
  }, [value]);

  // 防抖后触发 onChange
  useEffect(() => {
    if (debouncedValue !== value) {
      onChange(debouncedValue);
    }
  }, [debouncedValue, onChange, value]);

  const handleChange = useCallback((e) => {
    setInternalValue(e.target.value);
  }, []);

  return (
    <input
      type={type}
      value={internalValue}
      onChange={handleChange}
      placeholder={placeholder}
      className={`optimized-input ${className}`}
      {...props}
    />
  );
});

OptimizedInput.displayName = 'OptimizedInput';

/**
 * 上下文选择器 - 避免不必要渲染
 */
export function createContextSelector() {
  const context = React.createContext();
  
  const Provider = ({ value, children }) => (
    <context.Provider value={value}>
      {children}
    </context.Provider>
  );

  const useSelector = (selector) => {
    const state = React.useContext(context);
    
    const selectedState = React.useMemo(() => {
      return selector ? selector(state) : state;
    }, [state, selector]);

    const ref = React.useRef();

    React.useEffect(() => {
      ref.current = selectedState;
    });

    return React.useMemo(() => {
      if (ref.current === selectedState) {
        return ref.current;
      }
      return selectedState;
    }, [selectedState]);
  };

  return [Provider, useSelector];
}

// 使用示例:用户上下文
const [UserProvider, useUserSelector] = createContextSelector();

export { UserProvider, useUserSelector };

2.2 高级模式组件

import React, { createContext, useContext, useReducer, useCallback } from 'react';

/**
 * 复合组件模式 - 可复用的表格组件
 */
const TableContext = createContext();

export const Table = ({ children, data, onSort, className = '' }) => {
  const [sortConfig, setSortConfig] = React.useState({ key: null, direction: 'asc' });

  const handleSort = useCallback((key) => {
    const direction = sortConfig.key === key && sortConfig.direction === 'asc' ? 'desc' : 'asc';
    setSortConfig({ key, direction });
    onSort?.(key, direction);
  }, [sortConfig, onSort]);

  const sortedData = React.useMemo(() => {
    if (!sortConfig.key) return data;

    return [...data].sort((a, b) => {
      const aVal = a[sortConfig.key];
      const bVal = b[sortConfig.key];

      if (aVal < bVal) return sortConfig.direction === 'asc' ? -1 : 1;
      if (aVal > bVal) return sortConfig.direction === 'asc' ? 1 : -1;
      return 0;
    });
  }, [data, sortConfig]);

  const value = React.useMemo(() => ({
    data: sortedData,
    sortConfig,
    onSort: handleSort
  }), [sortedData, sortConfig, handleSort]);

  return (
    <TableContext.Provider value={value}>
      <table className={`advanced-table ${className}`}>
        {children}
      </table>
    </TableContext.Provider>
  );
};

export const TableHeader = ({ columnKey, children, sortable = false, className = '' }) => {
  const { sortConfig, onSort } = useContext(TableContext);

  const handleClick = useCallback(() => {
    if (sortable) {
      onSort(columnKey);
    }
  }, [sortable, columnKey, onSort]);

  const sortIndicator = sortable && sortConfig.key === columnKey ? (
    <span className="sort-indicator">
      {sortConfig.direction === 'asc' ? '↑' : '↓'}
    </span>
  ) : null;

  return (
    <th 
      className={`table-header ${sortable ? 'sortable' : ''} ${className}`}
      onClick={handleClick}
    >
      {children}
      {sortIndicator}
    </th>
  );
};

export const TableBody = ({ children, className = '' }) => {
  const { data } = useContext(TableContext);

  return (
    <tbody className={className}>
      {data.map((item, index) => (
        <React.Fragment key={item.id || index}>
          {children(item)}
        </React.Fragment>
      ))}
    </tbody>
  );
};

export const TableRow = ({ children, item, className = '' }) => (
  <tr className={`table-row ${className}`}>
    {children(item)}
  </tr>
);

/**
 * 渲染道具模式 - 灵活的组件复用
 */
export const DataRenderer = ({ 
  data, 
  loading, 
  error, 
  renderLoading, 
  renderError, 
  renderEmpty,
  renderItem,
  className = ''
}) => {
  if (loading) {
    return renderLoading ? renderLoading() : <div className="loading-state">Loading...</div>;
  }

  if (error) {
    return renderError ? renderError(error) : <div className="error-state">Error: {error.message}</div>;
  }

  if (!data || data.length === 0) {
    return renderEmpty ? renderEmpty() : <div className="empty-state">No data available</div>;
  }

  return (
    <div className={`data-renderer ${className}`}>
      {data.map((item, index) => renderItem({ item, index }))}
    </div>
  );
};

/**
 * 提供者模式 - 状态管理集成
 */
export function createStateProvider(reducer, initialState) {
  const StateContext = createContext();
  const DispatchContext = createContext();

  const StateProvider = ({ children }) => {
    const [state, dispatch] = useReducer(reducer, initialState);

    // 记忆化上下文值
    const stateValue = useMemo(() => state, [state]);
    const dispatchValue = useMemo(() => dispatch, [dispatch]);

    return (
      <StateContext.Provider value={stateValue}>
        <DispatchContext.Provider value={dispatchValue}>
          {children}
        </DispatchContext.Provider>
      </StateContext.Provider>
    );
  };

  const useState = () => {
    const context = useContext(StateContext);
    if (context === undefined) {
      throw new Error('useState must be used within a StateProvider');
    }
    return context;
  };

  const useDispatch = () => {
    const context = useContext(DispatchContext);
    if (context === undefined) {
      throw new Error('useDispatch must be used within a StateProvider');
    }
    return context;
  };

  return [StateProvider, useState, useDispatch];
}

三、状态管理与数据流

3.1 现代化状态管理

import React, { useCallback, useMemo } from 'react';

/**
 * 高性能状态存储
 */
export function createStore(initialState) {
  let state = { ...initialState };
  const listeners = new Set();

  const store = {
    getState() {
      return state;
    },

    setState(newState) {
      state = { ...state, ...newState };
      listeners.forEach(listener => listener());
    },

    subscribe(listener) {
      listeners.add(listener);
      return () => listeners.delete(listener);
    }
  };

  // React Hook 绑定
  const useStore = (selector = state => state) => {
    const [localState, setLocalState] = React.useState(() => selector(store.getState()));

    React.useEffect(() => {
      const checkForUpdates = () => {
        const newState = selector(store.getState());
        setLocalState(prevState => {
          if (prevState !== newState) {
            return newState;
          }
          return prevState;
        });
      };

      const unsubscribe = store.subscribe(checkForUpdates);
      return unsubscribe;
    }, [selector]);

    return localState;
  };

  const useAction = (action) => {
    return useCallback((...args) => {
      const result = action(...args);
      if (result instanceof Function) {
        return result(store.getState(), store.setState.bind(store));
      }
      return result;
    }, [action]);
  };

  return [store, useStore, useAction];
}

// 示例 store
const [userStore, useUserStore, useUserAction] = createStore({
  user: null,
  preferences: {},
  isAuthenticated: false
});

// Actions
const userActions = {
  login: (userData) => (state, setState) => {
    setState({
      user: userData,
      isAuthenticated: true
    });
  },
  
  logout: () => (state, setState) => {
    setState({
      user: null,
      isAuthenticated: false
    });
  },
  
  updatePreferences: (preferences) => (state, setState) => {
    setState({
      preferences: { ...state.preferences, ...preferences }
    });
  }
};

/**
 * 异步操作管理
 */
export function useAsyncOperation(operation, options = {}) {
  const {
    onSuccess,
    onError,
    onSettled
  } = options;

  const [state, setState] = React.useState({
    status: 'idle',
    data: null,
    error: null
  });

  const execute = useCallback(async (...args) => {
    setState({ status: 'loading', data: null, error: null });

    try {
      const data = await operation(...args);
      setState({ status: 'success', data, error: null });
      onSuccess?.(data);
      return data;
    } catch (error) {
      setState({ status: 'error', data: null, error });
      onError?.(error);
      throw error;
    } finally {
      onSettled?.();
    }
  }, [operation, onSuccess, onError, onSettled]);

  const reset = useCallback(() => {
    setState({ status: 'idle', data: null, error: null });
  }, []);

  return {
    ...state,
    execute,
    reset,
    isIdle: state.status === 'idle',
    isLoading: state.status === 'loading',
    isSuccess: state.status === 'success',
    isError: state.status === 'error'
  };
}

/**
 * 乐观更新 Hook
 */
export function useOptimisticMutation(mutation, options = {}) {
  const {
    onMutate,
    onError,
    onSettled
  } = options;

  const [state, setState] = React.useState({
    status: 'idle',
    data: null,
    error: null,
    isOptimistic: false
  });

  const mutate = useCallback(async (variables, optimisticData) => {
    // 乐观更新
    if (optimisticData) {
      setState(prev => ({
        ...prev,
        status: 'loading',
        data: optimisticData,
        isOptimistic: true
      }));
    }

    // 执行实际的变更
    try {
      const context = onMutate?.(variables);
      const data = await mutation(variables);
      
      setState({
        status: 'success',
        data,
        error: null,
        isOptimistic: false
      });
      
      return data;
    } catch (error) {
      // 回滚乐观更新
      setState({
        status: 'error',
        data: null,
        error,
        isOptimistic: false
      });
      
      onError?.(error, variables, context);
      throw error;
    } finally {
      onSettled?.(data, error, variables, context);
    }
  }, [mutation, onMutate, onError, onSettled]);

  return {
    ...state,
    mutate,
    isIdle: state.status === 'idle',
    isLoading: state.status === 'loading',
    isSuccess: state.status === 'success',
    isError: state.status === 'error'
  };
}

四、完整应用示例

4.1 高性能用户列表应用

import React, { useCallback, useMemo, useState } from 'react';
import { 
  useAdvancedFetch, 
  VirtualList, 
  LazyImage,
  OptimizedInput,
  DataRenderer
} from './advanced-hooks';

// 用户列表组件
export const UserList = () => {
  const [searchTerm, setSearchTerm] = useState('');
  const debouncedSearch = useDebounce(searchTerm, 300);
  
  const { 
    data: users, 
    isLoading, 
    error, 
    refetch 
  } = useAdvancedFetch(
    `https://jsonplaceholder.typicode.com/users${debouncedSearch ? `?name_like=${debouncedSearch}` : ''}`,
    {
      retryCount: 3,
      onSuccess: (data) => console.log('Users loaded:', data.length),
      onError: (error) => console.error('Failed to load users:', error)
    }
  );

  const filteredUsers = useMemo(() => {
    if (!users) return [];
    return users.filter(user => 
      user.name.toLowerCase().includes(debouncedSearch.toLowerCase()) ||
      user.email.toLowerCase().includes(debouncedSearch.toLowerCase())
    );
  }, [users, debouncedSearch]);

  const renderUserItem = useCallback(({ item: user, index }) => (
    <div className="user-item">
      <LazyImage
        src={`https://i.pravatar.cc/150?img=${index + 1}`}
        alt={user.name}
        className="user-avatar"
      />
      <div className="user-info">
        <h3 className="user-name">{user.name}</h3>
        <p className="user-email">{user.email}</p>
        <p className="user-company">{user.company?.name}</p>
      </div>
    </div>
  ), []);

  return (
    <div className="user-list-container">
      <div className="user-list-header">
        <h1>用户列表 ({filteredUsers.length})</h1>
        <div className="controls">
          <OptimizedInput
            value={searchTerm}
            onChange={setSearchTerm}
            placeholder="搜索用户..."
            debounceMs={300}
            className="search-input"
          />
          <button 
            onClick={refetch}
            disabled={isLoading}
            className="refresh-button"
          >
            {isLoading ? '刷新中...' : '刷新'}
          </button>
        </div>
      </div>

      <DataRenderer
        data={filteredUsers}
        loading={isLoading}
        error={error}
        renderLoading={() => (
          <div className="loading-spinner">
            <div className="spinner"></div>
            <p>加载用户数据中...</p>
          </div>
        )}
        renderError={(error) => (
          <div className="error-message">
            <h3>加载失败</h3>
            <p>{error.message}</p>
            <button onClick={refetch}>重试</button>
          </div>
        )}
        renderEmpty={() => (
          <div className="empty-state">
            <p>没有找到匹配的用户</p>
          </div>
        )}
        renderItem={renderUserItem}
        className="user-list"
      />
    </div>
  );
};

// 使用复合组件的表格示例
export const UserTable = ({ users }) => {
  const handleSort = useCallback((key, direction) => {
    console.log(`Sorting by ${key} in ${direction} order`);
  }, []);

  return (
    <Table data={users} onSort={handleSort} className="user-table">
      <thead>
        <tr>
          <TableHeader columnKey="id" sortable>ID</TableHeader>
          <TableHeader columnKey="name" sortable>姓名</TableHeader>
          <TableHeader columnKey="email" sortable>邮箱</TableHeader>
          <TableHeader columnKey="company.name" sortable>公司</TableHeader>
        </tr>
      </thead>
      <TableBody>
        {(user) => (
          <TableRow item={user}>
            {(user) => (
              <>
                <td>{user.id}</td>
                <td>{user.name}</td>
                <td>{user.email}</td>
                <td>{user.company?.name}</td>
              </>
            )}
          </TableRow>
        )}
      </TableBody>
    </Table>
  );
};

// 主应用组件
export const App = () => {
  return (
    <div className="app">
      <header className="app-header">
        <h1>React 高级模式实战</h1>
      </header>
      <main className="app-main">
        <UserList />
      </main>
    </div>
  );
};

export default App;

4.2 CSS 样式(配套使用)

/* 虚拟列表样式 */
.virtual-list {
  border: 1px solid #e1e5e9;
  border-radius: 8px;
  background: white;
}

.user-item {
  display: flex;
  align-items: center;
  padding: 12px 16px;
  border-bottom: 1px solid #f0f0f0;
  transition: background-color 0.2s;
}

.user-item:hover {
  background-color: #f8f9fa;
}

.user-avatar {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  margin-right: 12px;
  object-fit: cover;
}

.user-info h3 {
  margin: 0 0 4px 0;
  font-size: 14px;
  font-weight: 600;
}

.user-info p {
  margin: 0;
  font-size: 12px;
  color: #666;
}

/* 懒加载图片 */
.lazy-image {
  opacity: 0;
  transition: opacity 0.3s ease-in-out;
}

.lazy-image.loaded {
  opacity: 1;
}

.lazy-image.loading {
  background: #f0f0f0;
}

/* 优化输入框 */
.optimized-input {
  padding: 8px 12px;
  border: 1px solid #d1d5db;
  border-radius: 6px;
  font-size: 14px;
  transition: border-color 0.2s;
}

.optimized-input:focus {
  outline: none;
  border-color: #3b82f6;
  box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
}

/* 表格样式 */
.advanced-table {
  width: 100%;
  border-collapse: collapse;
  background: white;
  border-radius: 8px;
  overflow: hidden;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}

.table-header {
  padding: 12px 16px;
  text-align: left;
  font-weight: 600;
  background: #f8f9fa;
  border-bottom: 1px solid #e9ecef;
}

.table-header.sortable {
  cursor: pointer;
  user-select: none;
}

.table-header.sortable:hover {
  background: #e9ecef;
}

.sort-indicator {
  margin-left: 4px;
  font-weight: bold;
}

.table-row {
  transition: background-color 0.2s;
}

.table-row:hover {
  background-color: #f8f9fa;
}

.table-row td {
  padding: 12px 16px;
  border-bottom: 1px solid #e9ecef;
}

/* 加载状态 */
.loading-spinner {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 40px;
  color: #666;
}

.spinner {
  width: 32px;
  height: 32px;
  border: 3px solid #f3f3f3;
  border-top: 3px solid #3b82f6;
  border-radius: 50%;
  animation: spin 1s linear infinite;
  margin-bottom: 12px;
}

@keyframes spin {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}

/* 错误状态 */
.error-message {
  padding: 40px;
  text-align: center;
  color: #dc2626;
}

.error-message button {
  margin-top: 12px;
  padding: 8px 16px;
  background: #dc2626;
  color: white;
  border: none;
  border-radius: 6px;
  cursor: pointer;
}

.error-message button:hover {
  background: #b91c1c;
}

/* 空状态 */
.empty-state {
  padding: 40px;
  text-align: center;
  color: #666;
}

/* 响应式设计 */
@media (max-width: 768px) {
  .user-list-header {
    flex-direction: column;
    gap: 12px;
  }
  
  .controls {
    width: 100%;
  }
  
  .search-input {
    width: 100%;
  }
  
  .user-item {
    flex-direction: column;
    text-align: center;
  }
  
  .user-avatar {
    margin-right: 0;
    margin-bottom: 8px;
  }
}

总结

通过本文的完整实现,我们构建了一个现代化的 React 应用架构,具备以下特点:

  1. 性能优化 – 虚拟列表、懒加载、防抖、记忆化等优化技术
  2. 高级模式 – 复合组件、渲染道具、提供者等设计模式
  3. 状态管理 – 自定义状态管理、乐观更新、异步操作管理
  4. 代码复用 – 可复用的自定义 Hooks 和组件
  5. 开发体验 – 类型安全、错误处理、加载状态等完整特性

这套架构可以支撑大型 React 应用的开发,确保应用的性能和可维护性。

© 版权声明
THE END
喜欢就支持一下吧
点赞14 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容