React框架的基础代码使用

React 商品搜索与 API 演示项目全解析

这是一篇基于 React + Vite + React Router 构建的完整前端项目博客。

项目实现了:商品搜索(前端静态数据)、多页面路由、深色主题 UI、以及通过 GET/POST 请求调用公开 API 的功能。

本文将逐文件分析代码逻辑、样式设计以及关键 React 概念(Hooks、路由、状态管理、网络请求)。


📁 项目结构

text

text 复制代码
src/
├── assets/               # 静态资源(未使用)
├── data/
│   └── products.js       # 商品模拟数据
├── pages/
│   ├── Home.jsx          # 首页组件
│   ├── Home.css
│   ├── Search.jsx        # 商品搜索页组件
│   ├── Search.css
│   ├── ApiPage.jsx       # API 请求演示页组件
│   └── ApiPage.css
├── App.jsx               # 根组件(路由配置 + 导航栏)
├── App.css               # 全局导航栏样式
├── index.css             # 全局重置 + 深色主题
├── main.jsx              # 入口文件(挂载路由)
└── ...

🔧 一、项目入口与全局配置

1. main.jsx -- React 根渲染与路由包裹

jsx 复制代码
// src/main.jsx
import React from "react";
import ReactDOM from "react-dom/client";
import { BrowserRouter } from "react-router-dom";
import App from "./App";
import "./index.css";

ReactDOM.createRoot(document.getElementById("root")).render(
  <React.StrictMode>
    <BrowserRouter>
      <App />
    </BrowserRouter>
  </React.StrictMode>
);

解释

  • 使用 ReactDOM.createRoot 开启 React 18 的并发渲染。
  • <BrowserRouter> 提供基于 HTML5 History API 的路由上下文,使 <App /> 内部可以使用 useRoutesLink 等路由钩子。
  • 全局引入 index.css 作为基础样式。

2. index.css -- 全局深色主题与重置

css 复制代码
/* src/index.css */
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

html, body, #root {
  width: 100%;
  min-height: 100%;
  background: #0a0f1c;  /* 深蓝黑背景 */
}

body {
  font-family: 'Segoe UI', 'Inter', system-ui, sans-serif;
  color: #eef2ff;
  line-height: 1.5;
}

::-webkit-scrollbar {
  width: 8px;
}
::-webkit-scrollbar-track {
  background: #1e293b;
}
::-webkit-scrollbar-thumb {
  background: #3b82f6;
  border-radius: 4px;
}

a {
  text-decoration: none;
  color: inherit;
}

解释

  • 全局盒模型重置,避免边距混乱。
  • 设置全屏深色背景(#0a0f1c),与后续组件风格统一。
  • 自定义滚动条样式,增加蓝绿色点缀。
  • 移除链接默认下划线,保持视觉干净。

🧭 二、根组件与路由导航

3. App.jsx -- 导航栏与路由配置

jsx 复制代码
// src/App.jsx
import { Routes, Route, Link } from "react-router-dom";
import Home from "./pages/Home";
import Search from "./pages/Search";
import ApiPage from "./pages/ApiPage";
import "./App.css";

function App() {
  return (
    <div>
      <nav className="navbar">
        <Link to="/">🏠 首页</Link>
        <Link to="/search">🔍 商品搜索</Link>
        <Link to="/api">✨ 开放数据</Link>
      </nav>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/search" element={<Search />} />
        <Route path="/api" element={<ApiPage />} />
      </Routes>
    </div>
  );
}

export default App;

解释

  • 使用 react-router-domLink 组件实现无刷新跳转。
  • 导航栏使用 className="navbar",样式在 App.css 中定义。
  • <Routes> 包裹多个 <Route>,匹配当前路径并渲染对应页面组件。
  • 三个页面:首页(Home)、商品搜索(Search)、API 演示(ApiPage)。

4. App.css -- 导航栏毛玻璃效果

css 复制代码
/* src/App.css */
.navbar {
  background: rgba(15, 23, 42, 0.9);
  backdrop-filter: blur(12px);
  padding: 1rem 2rem;
  display: flex;
  gap: 2rem;
  border-bottom: 1px solid rgba(59, 130, 246, 0.4);
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
  position: sticky;
  top: 0;
  z-index: 10;
}

.navbar a {
  font-weight: 600;
  font-size: 1.1rem;
  padding: 0.5rem 1rem;
  border-radius: 40px;
  transition: all 0.2s ease;
  background: rgba(255,255,255,0.05);
}

.navbar a:hover {
  background: linear-gradient(135deg, #3b82f6, #10b981);
  color: white;
  transform: translateY(-2px);
  box-shadow: 0 4px 12px rgba(59,130,246,0.3);
}

解释

  • backdrop-filter: blur(12px) 实现毛玻璃效果,半透明背景。
  • sticky 定位使导航栏固定在顶部。
  • 每个导航链接带圆角、半透背景,hover 时展示蓝绿渐变并轻微上浮。
  • 整体风格与深色主题完美融合。

🏠 三、首页(Home)组件 -- 产品介绍与特性卡片

5. Home.jsx -- 首页结构

jsx 复制代码
// src/pages/Home.jsx
import { Link } from "react-router-dom";
import "./Home.css";

function Home() {
  return (
    <div className="home-page">
      <div className="hero">
        <h1 className="glow-text">✨ React 商品探索</h1>
        <p className="subtitle">基于 React Router + Hooks 的静态商品搜索演示</p>
        <Link to="/search">
          <button className="cta-button">🔍 立即搜索商品 →</button>
        </Link>
      </div>

      <div className="features">
        <div className="feature-card">
          <div className="feature-icon">⚛️</div>
          <h3>React 函数组件</h3>
          <p>使用 useState 管理状态,useEffect 概念预留扩展</p>
        </div>
        <div className="feature-card">
          <div className="feature-icon">🧭</div>
          <h3>React Router v6</h3>
          <p>声明式路由,支持多页面导航与动态路由</p>
        </div>
        <div className="feature-card">
          <div className="feature-icon">🔎</div>
          <h3>前端模糊搜索</h3>
          <p>本地商品数据,实时筛选不区分大小写</p>
        </div>
      </div>

      <footer className="home-footer">
        <p>💡 提示:点击上方导航或按钮进入搜索页,试试搜索"手机"、"苹果"</p>
      </footer>
    </div>
  );
}

export default Home;

解释

  • 使用 flex 布局实现垂直居中(配合 .home-page 样式)。
  • 标题使用渐变文字(CSS 中定义),带有微弱光晕。
  • 三个特性卡片(feature-card)采用毛玻璃效果、hover 上浮动画。
  • 底部 footer 提供简短的使用提示。
  • 通过 Link 跳转到搜索页。

6. Home.css -- 渐变标题、卡片动画

css 复制代码
/* src/pages/Home.css */
.home-page {
  min-height: calc(100vh - 70px);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 2rem;
}

.hero h1 {
  font-size: 3.5rem;
  background: linear-gradient(135deg, #60a5fa, #34d399);
  background-clip: text;
  -webkit-background-clip: text;
  color: transparent;
}

.cta-button {
  background: linear-gradient(95deg, #3b82f6, #10b981);
  border: none;
  padding: 12px 32px;
  border-radius: 40px;
  color: white;
  box-shadow: 0 8px 20px rgba(59,130,246,0.4);
  transition: transform 0.2s, box-shadow 0.2s;
}
.cta-button:hover {
  transform: scale(1.03);
  box-shadow: 0 12px 28px rgba(59,130,246,0.6);
}

.feature-card {
  background: rgba(30, 41, 59, 0.6);
  backdrop-filter: blur(8px);
  border-radius: 28px;
  padding: 1.8rem;
  transition: all 0.3s;
}
.feature-card:hover {
  transform: translateY(-6px);
  border-color: #3b82f6;
}

解释

  • 标题文字采用 background-clip: text 实现蓝绿渐变。
  • 按钮采用相同渐变色,hover 放大并增强阴影,营造"发光"感。
  • 卡片 hover 时向上平移,并增加蓝色边框,交互感强。

🔍 四、商品搜索页(Search) -- 静态数据 + 模糊搜索

7. 模拟商品数据 products.js

js 复制代码
// src/data/products.js
export const products = [
  { id: 1, name: "苹果 iPhone 15", category: "手机", price: 5999 },
  { id: 2, name: "华为 Mate 60", category: "手机", price: 5499 },
  { id: 3, name: "小米 14 Pro", category: "手机", price: 4999 },
  { id: 4, name: "联想 ThinkPad X1", category: "笔记本", price: 8999 },
  { id: 5, name: "戴尔 XPS 13", category: "笔记本", price: 9999 },
  { id: 6, name: "索尼 WH-1000XM5", category: "耳机", price: 1999 },
  { id: 7, name: "苹果 AirPods Pro 2", category: "耳机", price: 1899 }
];

解释

  • 前端静态数据,模拟商品列表。包含 idnamecategoryprice
  • 搜索时基于 name 字段进行模糊匹配。

8. Search.jsx -- 核心搜索逻辑

jsx 复制代码
// src/pages/Search.jsx
import { useState } from "react";
import { products } from "../data/products";
import "./Search.css";

function Search() {
  const [keyword, setKeyword] = useState("");
  const [searchResults, setSearchResults] = useState([]);
  const [hasSearched, setHasSearched] = useState(false);

  const handleSearch = () => {
    if (!keyword.trim()) {
      setSearchResults([]);
      setHasSearched(true);
      return;
    }
    const filtered = products.filter(product =>
      product.name.toLowerCase().includes(keyword.toLowerCase())
    );
    setSearchResults(filtered);
    setHasSearched(true);
  };

  const quickSearch = (term) => {
    setKeyword(term);
    const filtered = products.filter(product =>
      product.name.toLowerCase().includes(term.toLowerCase())
    );
    setSearchResults(filtered);
    setHasSearched(true);
  };

  return (
    <div className="search-page">
      <div className="search-container">
        <div className="search-header">
          <h2>✨ 商品搜索</h2>
        </div>
        <div className="search-box">
          <input
            type="text"
            placeholder="输入商品名称,例如:iPhone"
            value={keyword}
            onChange={(e) => setKeyword(e.target.value)}
            onKeyPress={(e) => e.key === "Enter" && handleSearch()}
          />
          <button onClick={handleSearch}>搜索</button>
        </div>
        {hasSearched && (
          <div className="results">
            <div className="result-count">共找到 {searchResults.length} 件商品</div>
            {searchResults.length === 0 ? (
              <div className="empty-result">😞 没有找到相关商品</div>
            ) : (
              <ul className="product-list">
                {searchResults.map(product => (
                  <li key={product.id} className="product-item">
                    <span className="product-name">{product.name}</span>
                    <span className="product-category">{product.category}</span>
                    <span className="product-price">¥{product.price}</span>
                  </li>
                ))}
              </ul>
            )}
          </div>
        )}
        <div className="demo-tips">
          <span>⚡ 试试快速搜索:</span>
          <button onClick={() => quickSearch("手机")}>手机</button>
          <button onClick={() => quickSearch("苹果")}>苹果</button>
          <button onClick={() => quickSearch("笔记本")}>笔记本</button>
        </div>
      </div>
    </div>
  );
}

export default Search;

解释

  • 使用 useState 管理关键词、搜索结果、是否已搜索过。
  • handleSearch 执行模糊匹配(转小写后 includes),更新结果。
  • 按回车键(onKeyPress)同样触发搜索。
  • quickSearch 函数在设置关键词的同时立即执行搜索,避免异步状态延迟。
  • 搜索结果渲染为自定义列表项,每个商品展示名称、分类、价格。
  • 快速搜索按钮方便演示。

9. Search.css -- 深色卡片与搜索结果样式

css 复制代码
.search-box {
  display: flex;
  gap: 12px;
  background: rgba(30, 41, 59, 0.5);
  backdrop-filter: blur(8px);
  border-radius: 60px;
  border: 1px solid rgba(59,130,246,0.4);
}
.search-box input {
  background: transparent;
  color: #f1f5f9;
}
.product-item {
  background: rgba(30, 41, 59, 0.7);
  border-left: 4px solid #3b82f6;
  transition: 0.2s;
}
.product-item:hover {
  transform: translateX(6px);
  border-left-color: #10b981;
}
.product-price {
  color: #34d399;
  font-weight: bold;
}

解释

  • 搜索框毛玻璃风格,输入框透明,按钮渐变色。
  • 每个商品卡片带左侧蓝色条,hover 时右移并变为绿色。
  • 价格使用绿色高亮(#34d399),分类使用圆角标签。
  • 整体保持深色半透明背景,与全局主题一致。

🌐 五、API 演示页(ApiPage) -- GET / POST 请求

10. ApiPage.jsx -- 动态请求多种公开 API

jsx 复制代码
// src/pages/ApiPage.jsx
import { useState, useEffect } from "react";
import "./ApiPage.css";

const apiList = [
  { id: 'posts', name: 'GET 示例数据 (JSONPlaceholder)', url: 'https://jsonplaceholder.typicode.com/posts', method: 'GET' },
  { id: 'countries', name: 'GET 国家信息 (REST Countries)', url: 'https://restcountries.com/v3.1/all', method: 'GET' },
  { id: 'spacex', name: 'GET SpaceX 最新发射', url: 'https://api.spacexdata.com/v5/launches/latest', method: 'GET' },
  { 
    id: 'post', 
    name: 'POST 创建新数据 (JSONPlaceholder)', 
    url: 'https://jsonplaceholder.typicode.com/posts', 
    method: 'POST',
    bodyData: { title: 'React Demo 商品', body: '这是一条通过 POST 请求创建的商品记录', userId: 1 }
  }
];

function ApiPage() {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);
  const [selectedApi, setSelectedApi] = useState(apiList[0]);

  const fetchData = async () => {
    setLoading(true);
    setError(null);
    try {
      const { url, method, bodyData } = selectedApi;
      let response;
      if (method === 'GET') {
        response = await fetch(url);
      } else if (method === 'POST') {
        response = await fetch(url, {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify(bodyData),
        });
      }
      if (!response.ok) throw new Error(`HTTP ${response.status}`);
      const result = await response.json();
      const formattedData = method === 'GET' && Array.isArray(result) ? result.slice(0, 5) : result;
      setData({ method, url, requestBody: method === 'POST' ? bodyData : null, response: formattedData });
    } catch (err) {
      setError(err.message);
    } finally {
      setLoading(false);
    }
  };

  useEffect(() => { fetchData(); }, [selectedApi]);

  return (
    <div className="api-page">
      <div className="api-container">
        <div className="api-header">
          <h2>📡 探索开放数据 (GET + POST)</h2>
          <p>从免费的公共 API 获取真实数据,并尝试向服务器发送新数据</p>
        </div>
        <div className="api-selector">
          <label>选择数据源:</label>
          <select value={selectedApi.id} onChange={(e) => setSelectedApi(apiList.find(api => api.id === e.target.value))}>
            {apiList.map(api => (<option key={api.id} value={api.id}>{api.method} -- {api.name}</option>))}
          </select>
          <button onClick={fetchData} disabled={loading}>{loading ? '加载中...' : '重新获取'}</button>
        </div>
        <div className="api-result">
          {loading && <div className="loading-state"><div className="spinner"></div><p>正在执行 {selectedApi.method} 请求...</p></div>}
          {error && <div className="error-state"><p>❌ {error}</p><button onClick={fetchData}>重试</button></div>}
          {data && (
            <div className="data-display">
              <div className="data-meta">
                <span>请求方法: {data.method}</span>
                <span>请求地址: {data.url}</span>
              </div>
              {data.method === 'POST' && data.requestBody && (
                <div className="request-preview">
                  <div className="section-title">📤 发送的请求体 (JSON):</div>
                  <pre className="json-viewer request-body">{JSON.stringify(data.requestBody, null, 2)}</pre>
                </div>
              )}
              <div className="section-title">📥 服务器响应:</div>
              <pre className="json-viewer">{JSON.stringify(data.response, null, 2)}</pre>
            </div>
          )}
        </div>
      </div>
    </div>
  );
}

export default ApiPage;

解释

  • 定义 apiList 包含 GET(示例数据、国家信息、SpaceX 发射)和 POST(创建新数据)接口。
  • 使用 useState 管理数据、加载状态、错误以及当前选中的 API。
  • fetchData 根据 method 分别构造 fetch 请求,POST 时携带 JSON 体。
  • 对 GET 返回的数组只显示前 5 条,避免页面过长。
  • useEffect 监听 selectedApi 变化,自动请求新数据。
  • 界面包含下拉选择器、重新获取按钮,以及美观的 JSON 查看器(深色代码块)。
  • 错误处理:若请求失败,显示错误信息并提供重试按钮。

11. ApiPage.css -- 响应式深色代码块

css 复制代码
.api-page {
  min-height: 100vh;
  background: #0a0f1c;
  padding: 2rem;
}
.api-container {
  max-width: 1200px;
  margin: 0 auto;
  background: rgba(15, 23, 42, 0.85);
  backdrop-filter: blur(10px);
  border-radius: 28px;
  padding: 2rem;
  border: 1px solid rgba(59,130,246,0.3);
}
.json-viewer {
  background: #0f172a;
  padding: 1rem;
  border-radius: 16px;
  font-family: monospace;
  color: #e2e8f0;
  white-space: pre-wrap;
  word-break: break-word;
}
.spinner {
  width: 40px;
  height: 40px;
  border: 3px solid rgba(59,130,246,0.3);
  border-top-color: #3b82f6;
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
}

解释

  • 背景与全局一致,容器采用毛玻璃 + 大圆角。
  • JSON 数据使用深色代码块,等宽字体,自动换行。
  • 加载动画(spinner)用纯 CSS 实现旋转效果。
  • 响应式布局:在小屏幕上调整 padding 和选择器排列方向。

🧪 六、关键 React 概念总结

概念 体现位置 说明
函数组件 所有 .jsx 文件 React 推荐写法,配合 Hooks 实现状态与副作用
useState Search, ApiPage 管理输入框值、搜索结果、加载状态等
useEffect ApiPage 监听 selectedApi 变化,自动发起网络请求
事件处理 Search, ApiPage onClick, onChange, onKeyPress
条件渲染 Search, ApiPage 三元运算符 / && 控制加载、错误、结果区域的显示
列表渲染 & key Search products.map() 生成商品列表,key 使用唯一 id
路由 (React Router) App.jsx, main.jsx BrowserRouter, Routes, Route, Link
Props 传递 未显式使用(但可以通过扩展轻松加入) 组件通信基础
网络请求 (fetch) ApiPage GET / POST 异步请求,处理响应与错误

🎨 七、样式设计亮点

  • 深色主题 :全局背景 #0a0f1c,文字 #eef2ff,对比舒适。
  • 毛玻璃效果backdrop-filter: blur() 应用于导航栏、卡片、API 容器。
  • 蓝绿渐变点缀 :标题、按钮、hover 状态使用 linear-gradient(135deg, #60a5fa, #34d399)
  • 微交互动画:按钮缩放、卡片平移、边框颜色切换,提升用户体验。
  • 响应式设计 :移动端适配(@media)调整内边距和布局方向。

🚀 八、运行与扩展建议

运行项目

bash 复制代码
npm install
npm run dev

访问:

  • http://localhost:5173/ → 首页
  • /search → 商品搜索
  • /api → API 演示(默认 GET JSONPlaceholder)

扩展方向

  1. 添加更多 API :在 apiList 中增加其他公开接口(如天气、加密货币)。
  2. 实现用户自定义 POST 数据 :添加表单输入框,动态构造 bodyData
  3. 加入状态管理:当项目变大时,引入 Redux 或 Zustand 管理全局用户偏好。
  4. 商品数据接入后端:将商品搜索改为调用真实接口,并增加分页功能。
  5. 表单处理库 :使用 react-hook-form 优化搜索输入。

📝 结语

本文详细解析了一个完整的 React 演示项目,涵盖:

  • 路由与导航
  • 组件化开发与 Hooks 使用
  • 前端静态搜索
  • 调用第三方 API(GET / POST)
  • 现代化深色 UI 与交互动效

所有代码均已提供并附有行内注释与解释,你可以直接复制使用,也可以作为学习 React 的实践范例。如果有任何疑问或改进建议,欢迎在评论区留言!

Happy coding! ⚛️

相关推荐
叫我少年1 小时前
Vue 3 集成 Vue Router:从基础配置到项目实践
前端·路由
Highcharts.js1 小时前
Highcharts React 5.0 正式版:支持 ES 模块化、组件更精简、开发体验全面升级
前端·javascript·react.js·elasticsearch·前端框架·highcharts
大江东去浪淘尽千古风流人物1 小时前
【X-Restormer++】全天候图像恢复赛冠军方案:三项创新解析及对VIO/SLAM前端的工程价值
前端
LaughingZhu1 小时前
Claude Code 时代的写作:为什么 HTML 正在取代 Markdown
前端·人工智能·html
Shadow(⊙o⊙)1 小时前
qt中自定义槽函数 内部继承逻辑、GUI+CLI协同1.0
开发语言·前端·c++·qt
hexu_blog1 小时前
前端VUE后端java实现智能抠图
前端·javascript·vue.js·java处理抠图·vue实现智能抠图
__log2 小时前
AI前端工程化实战指南:10大核心场景的“解题思路“与“避坑指南“
前端·人工智能
ljt27249606612 小时前
Vue笔记(一)--模板
前端·vue.js·笔记
广州华水科技2 小时前
单北斗GNSS变形监测一体机在水库安全监测中的应用与优势
前端