React项目实战案例reactjs-interview-questions:电商平台完整实现

【免费下载链接】reactjs-interview-questions List of top 500 ReactJS Interview Questions & Answers....Coding exercise questions are coming soon!! 【免费下载链接】reactjs-interview-questions 项目地址: https://gitcode.com/GitHub_Trending/re/reactjs-interview-questions

前言:为什么选择React构建电商平台?

在当今的前端开发领域,电商平台面临着高并发访问复杂交互逻辑用户体验优化等多重挑战。React凭借其组件化架构虚拟DOM(Virtual DOM)单向数据流等特性,成为构建现代化电商平台的理想选择。

通过本实战教程,你将掌握:

  • ✅ React核心概念在电商场景下的应用
  • ✅ 组件化开发的最佳实践
  • ✅ 状态管理的完整解决方案
  • ✅ 性能优化和用户体验提升技巧
  • ✅ 完整的项目架构和部署流程

一、项目架构设计

1.1 技术栈选择

mermaid

1.2 目录结构规划

src/
├── components/          # 通用组件
│   ├── ui/             # UI基础组件
│   ├── layout/         # 布局组件
│   └── common/         # 公共组件
├── pages/              # 页面组件
│   ├── Home/           # 首页
│   ├── Product/        # 商品详情
│   ├── Cart/           # 购物车
│   └── Checkout/       # 结算页
├── store/              # 状态管理
│   ├── slices/         # Redux切片
│   └── hooks/          # 自定义Hooks
├── services/           # API服务
├── utils/              # 工具函数
├── assets/             # 静态资源
└── styles/             # 全局样式

二、核心功能实现

2.1 商品列表组件

import React, { useState, useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { fetchProducts } from '../store/slices/productSlice';
import ProductCard from '../components/ProductCard';
import LoadingSpinner from '../components/ui/LoadingSpinner';

const ProductList = () => {
  const dispatch = useDispatch();
  const { products, loading, error } = useSelector(state => state.products);
  const [filters, setFilters] = useState({
    category: '',
    priceRange: [0, 1000],
    sortBy: 'name'
  });

  useEffect(() => {
    dispatch(fetchProducts(filters));
  }, [dispatch, filters]);

  if (loading) return <LoadingSpinner />;
  if (error) return <div>Error: {error}</div>;

  return (
    <div className="product-grid">
      <FilterPanel filters={filters} onFilterChange={setFilters} />
      <div className="products-container">
        {products.map(product => (
          <ProductCard key={product.id} product={product} />
        ))}
      </div>
      <Pagination />
    </div>
  );
};

export default ProductList;

2.2 购物车状态管理

// store/slices/cartSlice.js
import { createSlice } from '@reduxjs/toolkit';

const cartSlice = createSlice({
  name: 'cart',
  initialState: {
    items: [],
    total: 0,
    itemCount: 0
  },
  reducers: {
    addToCart: (state, action) => {
      const existingItem = state.items.find(item => item.id === action.payload.id);
      if (existingItem) {
        existingItem.quantity += 1;
      } else {
        state.items.push({ ...action.payload, quantity: 1 });
      }
      state.total += action.payload.price;
      state.itemCount += 1;
    },
    removeFromCart: (state, action) => {
      const index = state.items.findIndex(item => item.id === action.payload);
      if (index !== -1) {
        const item = state.items[index];
        state.total -= item.price * item.quantity;
        state.itemCount -= item.quantity;
        state.items.splice(index, 1);
      }
    },
    updateQuantity: (state, action) => {
      const { id, quantity } = action.payload;
      const item = state.items.find(item => item.id === id);
      if (item) {
        const quantityDiff = quantity - item.quantity;
        state.total += item.price * quantityDiff;
        state.itemCount += quantityDiff;
        item.quantity = quantity;
      }
    },
    clearCart: state => {
      state.items = [];
      state.total = 0;
      state.itemCount = 0;
    }
  }
});

export const { addToCart, removeFromCart, updateQuantity, clearCart } = cartSlice.actions;
export default cartSlice.reducer;

2.3 路由配置

// App.js
import React from 'react';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import { Provider } from 'react-redux';
import { store } from './store';
import Layout from './components/layout/Layout';
import Home from './pages/Home';
import ProductDetail from './pages/ProductDetail';
import Cart from './pages/Cart';
import Checkout from './pages/Checkout';
import Login from './pages/Login';
import NotFound from './pages/NotFound';

function App() {
  return (
    <Provider store={store}>
      <Router>
        <Layout>
          <Routes>
            <Route path="/" element={<Home />} />
            <Route path="/product/:id" element={<ProductDetail />} />
            <Route path="/cart" element={<Cart />} />
            <Route path="/checkout" element={<Checkout />} />
            <Route path="/login" element={<Login />} />
            <Route path="*" element={<NotFound />} />
          </Routes>
        </Layout>
      </Router>
    </Provider>
  );
}

export default App;

三、性能优化策略

3.1 组件优化技术对比

优化技术 适用场景 实现方式 效果
React.memo 纯展示组件 包裹函数组件 避免不必要的重渲染
useMemo 复杂计算 缓存计算结果 减少重复计算
useCallback 函数传递 缓存函数引用 避免子组件重渲染
Lazy Loading 路由组件 React.lazy + Suspense 减少初始包大小
Virtualization 长列表 react-window 提升渲染性能

3.2 代码分割实现

import React, { Suspense, lazy } from 'react';
import LoadingSpinner from './components/ui/LoadingSpinner';

// 懒加载页面组件
const Home = lazy(() => import('./pages/Home'));
const ProductDetail = lazy(() => import('./pages/ProductDetail'));
const Cart = lazy(() => import('./pages/Cart'));

const App = () => {
  return (
    <Suspense fallback={<LoadingSpinner />}>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/product/:id" element={<ProductDetail />} />
        <Route path="/cart" element={<Cart />} />
      </Routes>
    </Suspense>
  );
};

3.3 图片懒加载优化

import React, { useState, useRef, useEffect } from 'react';

const LazyImage = ({ src, alt, className }) => {
  const [isLoaded, setIsLoaded] = useState(false);
  const imgRef = useRef();

  useEffect(() => {
    const observer = new IntersectionObserver(
      ([entry]) => {
        if (entry.isIntersecting) {
          setIsLoaded(true);
          observer.disconnect();
        }
      },
      { threshold: 0.1 }
    );

    if (imgRef.current) {
      observer.observe(imgRef.current);
    }

    return () => observer.disconnect();
  }, []);

  return (
    <div ref={imgRef} className={className}>
      {isLoaded ? (
        <img src={src} alt={alt} />
      ) : (
        <div className="image-placeholder">Loading...</div>
      )}
    </div>
  );
};

export default LazyImage;

四、状态管理最佳实践

4.1 Redux Toolkit配置

// store/index.js
import { configureStore } from '@reduxjs/toolkit';
import productReducer from './slices/productSlice';
import cartReducer from './slices/cartSlice';
import userReducer from './slices/userSlice';

export const store = configureStore({
  reducer: {
    products: productReducer,
    cart: cartReducer,
    user: userReducer
  },
  middleware: getDefaultMiddleware =>
    getDefaultMiddleware({
      serializableCheck: {
        ignoredActions: ['persist/PERSIST']
      }
    })
});

export default store;

4.2 异步操作处理

// store/slices/productSlice.js
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
import { productAPI } from '../../services/api';

export const fetchProducts = createAsyncThunk(
  'products/fetchProducts',
  async (filters, { rejectWithValue }) => {
    try {
      const response = await productAPI.getProducts(filters);
      return response.data;
    } catch (error) {
      return rejectWithValue(error.response.data);
    }
  }
);

const productSlice = createSlice({
  name: 'products',
  initialState: {
    items: [],
    loading: false,
    error: null,
    pagination: {
      page: 1,
      totalPages: 1,
      totalItems: 0
    }
  },
  reducers: {
    clearError: state => {
      state.error = null;
    }
  },
  extraReducers: builder => {
    builder
      .addCase(fetchProducts.pending, state => {
        state.loading = true;
        state.error = null;
      })
      .addCase(fetchProducts.fulfilled, (state, action) => {
        state.loading = false;
        state.items = action.payload.products;
        state.pagination = action.payload.pagination;
      })
      .addCase(fetchProducts.rejected, (state, action) => {
        state.loading = false;
        state.error = action.payload;
      });
  }
});

export const { clearError } = productSlice.actions;
export default productSlice.reducer;

五、用户体验优化

5.1 加载状态管理

import React from 'react';

const LoadingStates = () => {
  return (
    <div className="loading-states">
      {/* 骨架屏 */}
      <div className="skeleton-loader">
        <div className="skeleton-image"></div>
        <div className="skeleton-text"></div>
        <div className="skeleton-text short"></div>
      </div>
      
      {/* 加载动画 */}
      <div className="spinner-container">
        <div className="spinner"></div>
        <p>加载中...</p>
      </div>
      
      {/* 错误状态 */}
      <div className="error-state">
        <span>⚠️</span>
        <p>加载失败,请重试</p>
        <button>重新加载</button>
      </div>
    </div>
  );
};

export default LoadingStates;

5.2 搜索功能实现

import React, { useState, useMemo, useCallback } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { searchProducts } from '../store/slices/productSlice';
import DebouncedInput from '../components/ui/DebouncedInput';

const SearchBar = () => {
  const dispatch = useDispatch();
  const [query, setQuery] = useState('');
  const { searchResults, searchLoading } = useSelector(state => state.products);

  const handleSearch = useCallback((searchQuery) => {
    if (searchQuery.trim()) {
      dispatch(searchProducts(searchQuery));
    }
  }, [dispatch]);

  const filteredResults = useMemo(() => {
    return searchResults.filter(product =>
      product.name.toLowerCase().includes(query.toLowerCase()) ||
      product.description.toLowerCase().includes(query.toLowerCase())
    );
  }, [searchResults, query]);

  return (
    <div className="search-container">
      <DebouncedInput
        value={query}
        onChange={setQuery}
        onDebounce={handleSearch}
        placeholder="搜索商品..."
        delay={300}
      />
      
      {searchLoading && <div className="search-loading">搜索中...</div>}
      
      {query && filteredResults.length > 0 && (
        <div className="search-results">
          {filteredResults.slice(0, 5).map(product => (
            <div key={product.id} className="search-result-item">
              <img src={product.image} alt={product.name} />
              <div className="result-info">
                <h4>{product.name}</h4>
                <p>¥{product.price}</p>
              </div>
            </div>
          ))}
        </div>
      )}
    </div>
  );
};

export default SearchBar;

六、测试策略

6.1 单元测试示例

// __tests__/cartSlice.test.js
import cartReducer, { addToCart, removeFromCart } from '../store/slices/cartSlice';

describe('cart slice', () => {
  const initialState = {
    items: [],
    total: 0,
    itemCount: 0
  };

  const mockProduct = {
    id: 1,
    name: 'Test Product',
    price: 100,
    image: 'test.jpg'
  };

  it('should handle adding item to cart', () => {
    const actual = cartReducer(initialState, addToCart(mockProduct));
    expect(actual.items).toHaveLength(1);
    expect(actual.items[0]).toEqual({ ...mockProduct, quantity: 1 });
    expect(actual.total).toBe(100);
    expect(actual.itemCount).toBe(1);
  });

  it('should handle removing item from cart', () => {
    const stateWithItem = {
      items: [{ ...mockProduct, quantity: 2 }],
      total: 200,
      itemCount: 2
    };
    
    const actual = cartReducer(stateWithItem, removeFromCart(1));
    expect(actual.items).toHaveLength(0);
    expect(actual.total).toBe(0);
    expect(actual.itemCount).toBe(0);
  });
});

6.2 组件测试

// __tests__/ProductCard.test.js
import React from 'react';
import { render, screen, fireEvent } from '@testing-library/react';
import { Provider } from 'react-redux';
import { configureStore } from '@reduxjs/toolkit';
import ProductCard from '../components/ProductCard';
import cartReducer from '../store/slices/cartSlice';

const mockStore = configureStore({
  reducer: {
    cart: cartReducer
  }
});

const mockProduct = {
  id: 1,
  name: 'Test Product',
  price: 99.99,
  image: 'test.jpg',
  description: 'Test description'
};

describe('ProductCard', () => {
  it('renders product information correctly', () => {
    render(
      <Provider store={mockStore}>
        <ProductCard product={mockProduct} />
      </Provider>
    );

    expect(screen.getByText('Test Product')).toBeInTheDocument();
    expect(screen.getByText('¥99.99')).toBeInTheDocument();
    expect(screen.getByAltText('Test Product')).toHaveAttribute('src', 'test.jpg');
  });

  it('dispatches addToCart action when add button is clicked', () => {
    const store = configureStore({
      reducer: {
        cart: cartReducer
      }
    });

    render(
      <Provider store={store}>
        <ProductCard product={mockProduct} />
      </Provider>
    );

    fireEvent.click(screen.getByText('加入购物车'));
    
    const state = store.getState();
    expect(state.cart.items).toHaveLength(1);
    expect(state.cart.items[0].id).toBe(1);
  });
});

七、部署和监控

7.1 生产环境配置

// webpack.config.js (简化版)
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');

module.exports = {
  mode: 'production',
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].[contenthash].js',
    clean: true
  },
  optimization: {
    splitChunks: {
      chunks: 'all',
      cacheGroups: {
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendors',
          chunks: 'all'
        }
      }
    }
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './public/index.html',
      minify: {
        removeComments: true,
        collapseWhitespace: true,
        removeRedundantAttributes: true
      }
    }),
    new BundleAnalyzerPlugin({
      analyzerMode: 'static',
      openAnalyzer: false
    })
  ]
};

7.2 性能监控配置

【免费下载链接】reactjs-interview-questions List of top 500 ReactJS Interview Questions & Answers....Coding exercise questions are coming soon!! 【免费下载链接】reactjs-interview-questions 项目地址: https://gitcode.com/GitHub_Trending/re/reactjs-interview-questions

Logo

电商企业物流数字化转型必备!快递鸟 API 接口,72 小时快速完成物流系统集成。全流程实战1V1指导,营造开放的API技术生态圈。

更多推荐