Claude Code 实战案例:Web前端项目-2小时从零构建天气应用
第一部分:项目规划与需求分析
1.1 项目概览
项目名称:WeatherApp(天气应用)
|
项目属性 |
详情 |
说明 |
|
技术栈 |
React 18 + TypeScript |
最新且稳定 |
|
样式 |
Tailwind CSS |
快速美化UI |
|
API |
OpenWeather(模拟数据) |
无需真实Key |
|
目标耗时 |
12小时(包括调试) |
传统30-40小时 |
1.2 核心功能清单
- ✅ 显示当前城市天气(温度、湿度、风速、天气描述)
- ✅ 搜索功能:输入城市名称,实时获取天气
- ✅ 7天预报:卡片式展示未来天气
- ✅ 温度单位切换(摄氏度/华氏度)
- ✅ 响应式设计:支持桌面/平板/手机
- ✅ 加载动画和错误处理
第二部分:使用Claude Code开发
2.1 第一步:项目初始化(5分钟)
在Claude Code中输入以下提示词:
"使用React 18和TypeScript创建一个完整的天气应用。 功能需求:
1. 显示当前城市天气(温度、湿度、风速)
2. 搜索功能:用户可输入城市名称查看天气
3. 7天预报卡片显示
4. 温度单位切换(℃/℉)
5. 响应式设计。技术要求:
React 18 + TypeScript
Tailwind CSS样式
完整的TypeScript类型定义
React Hooks(useState, useEffect)
错误处理和加载状态 输出:完整的项目代码"
2.2 第二步:代码审查与微调(3小时)
Claude Code生成的代码通常需要小调整:
|
检查项 |
问题 |
解决方案 |
耗时 |
|
API集成 |
需要替换真实API地址 |
更新REACT_APP_API_KEY |
10分钟 |
|
TypeScript类型 |
某些类型定义缺失 |
让Claude Code补充类型定义 |
15分钟 |
|
样式调整 |
某些UI细节不够精细 |
微调Tailwind类名 |
20分钟 |
|
搜索功能 |
搜索体验需优化 |
添加防抖和错误提示 |
30分钟 |
|
测试修复 |
找出并修复bug |
手动测试和迭代 |
45分钟 |
2.3 第三步:优化与部署(4小时)
- 性能优化:减少重新渲染,优化API调用
- 代码分割:按路由或功能进行代码分割
- 打包优化:减少最终bundle大小
- 部署到Vercel/Netlify:一键部署
- 性能测试:用Lighthouse评分
第三部分:关键代码示例
3.1 主要组件结构:App.tsx
// App.tsx - 主应用组件
import React, { useState, useEffect } from 'react';
import WeatherCard from './components/WeatherCard';
import SearchBar from './components/SearchBar';
import Forecast from './components/Forecast';
// 定义天气数据类型
interface Weather {
temp: number;
humidity: number;
windSpeed: number;
description: string;
city: string;
}
export default function App() {
// 状态管理
const [weather, setWeather] = useState<Weather | null>(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
// 获取天气数据函数
const fetchWeather = async (city: string) => {
setLoading(true);
setError(null);
try {
const response = await fetch(`/api/weather?city=${city}`);
if (!response.ok) {
throw new Error('获取天气失败');
}
const data = await response.json();
setWeather(data);
} catch (err) {
setError('无法获取天气信息,请检查城市名称');
} finally {
setLoading(false);
}
};
// 初始化时获取默认城市天气
useEffect(() => {
fetchWeather('北京');
}, []);
// 渲染UI
return (
<div className="min-h-screen bg-gradient-to-br from-blue-400 to-blue-600 p-4">
{/* 搜索栏 */}
<SearchBar onSearch={fetchWeather} />
{/* 加载状态 */}
{loading && (
<div className="text-center mt-8">
<p className="text-white text-lg">加载中...</p>
</div>
)}
{/* 错误提示 */}
{error && (
<div className="mt-4 p-4 bg-red-500 text-white rounded-lg">
{error}
</div>
)}
{/* 天气卡片和预报 */}
{weather && (
<>
<WeatherCard weather={weather} />
<Forecast city={weather.city} />
</>
)}
</div>
);
}
3.2 搜索组件:SearchBar.tsx
// components/SearchBar.tsx
import React, { useState } from 'react';
interface SearchBarProps {
onSearch: (city: string) => void;
}
export default function SearchBar({ onSearch }: SearchBarProps) {
const [searchInput, setSearchInput] = useState('');
const handleSearch = (e: React.FormEvent) => {
e.preventDefault();
if (searchInput.trim()) {
onSearch(searchInput);
setSearchInput(''); // 清空输入框
}
};
return (
<div className="flex justify-center mt-8">
<form onSubmit={handleSearch} className="w-full max-w-md">
<div className="flex gap-2">
{/* 输入框 */}
<input
type="text"
value={searchInput}
onChange={(e) => setSearchInput(e.target.value)}
placeholder="输入城市名称..."
className="flex-1 px-4 py-2 rounded-lg text-gray-800"
/>
{/* 搜索按钮 */}
<button
type="submit"
className="px-6 py-2 bg-white text-blue-600 font-bold rounded-lg hover:bg-gray-100"
>
搜索
</button>
</div>
</form>
</div>
);
}
3.3 天气卡片组件:WeatherCard.tsx
// components/WeatherCard.tsx
import React from 'react';
interface Weather {
temp: number;
humidity: number;
windSpeed: number;
description: string;
city: string;
}
interface WeatherCardProps {
weather: Weather;
}
export default function WeatherCard({ weather }: WeatherCardProps) {
return (
<div className="mt-8 max-w-md mx-auto bg-white rounded-2xl shadow-lg p-8">
{/* 城市名称 */}
<h1 className="text-3xl font-bold text-gray-800 text-center">
{weather.city}
</h1>
{/* 天气描述 */}
<p className="text-center text-gray-600 mt-2">
{weather.description}
</p>
{/* 温度大显示 */}
<div className="text-6xl font-bold text-center text-blue-600 mt-4">
{weather.temp}°C
</div>
{/* 详细信息 */}
<div className="grid grid-cols-2 gap-4 mt-8">
{/* 湿度 */}
<div className="bg-blue-50 rounded-lg p-4">
<p className="text-gray-600 text-sm">湿度</p>
<p className="text-2xl font-bold text-blue-600">
{weather.humidity}%
</p>
</div>
{/* 风速 */}
<div className="bg-blue-50 rounded-lg p-4">
<p className="text-gray-600 text-sm">风速</p>
<p className="text-2xl font-bold text-blue-600">
{weather.windSpeed} m/s
</p>
</div>
</div>
</div>
);
}
3.4 自定义Hook:useWeather.ts
// hooks/useWeather.ts
import { useState, useCallback } from 'react';
interface WeatherData {
temp: number;
humidity: number;
windSpeed: number;
description: string;
city: string;
}
export const useWeather = () => {
const [data, setData] = useState<WeatherData | null>(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
// 获取天气的核心函数
const fetchWeather = useCallback(async (city: string) => {
setLoading(true);
setError(null);
try {
// 调用API
const response = await fetch(`/api/weather/${city}`);
// 检查响应状态
if (!response.ok) {
throw new Error(`HTTP Error: ${response.status}`);
}
// 解析JSON数据
const json = await response.json();
setData(json);
} catch (err: any) {
// 错误处理
setError(err.message || '无法获取天气数据');
} finally {
setLoading(false);
}
}, []);
return { data, loading, error, fetchWeather };
};
3.5 预报组件:Forecast.tsx
// components/Forecast.tsx
import React, { useEffect, useState } from 'react';
interface ForecastDay {
date: string;
temp_max: number;
temp_min: number;
description: string;
}
interface ForecastProps {
city: string;
}
export default function Forecast({ city }: ForecastProps) {
const [forecast, setForecast] = useState<ForecastDay[]>([]);
const [loading, setLoading] = useState(false);
useEffect(() => {
// 获取预报数据
const fetchForecast = async () => {
setLoading(true);
try {
const res = await fetch(`/api/forecast/${city}`);
const data = await res.json();
setForecast(data.slice(0, 7)); // 只取7天
} finally {
setLoading(false);
}
};
if (city) {
fetchForecast();
}
}, [city]);
if (loading) return <p className="text-center text-white">加载预报中...</p>;
return (
<div className="mt-8 max-w-4xl mx-auto">
<h2 className="text-white text-2xl font-bold mb-4">7天预报</h2>
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
{forecast.map((day) => (
<div key={day.date} className="bg-white rounded-lg p-4 text-center">
<p className="font-bold text-gray-800">{day.date}</p>
<p className="text-sm text-gray-600 mt-1">{day.description}</p>
<div className="mt-2 flex justify-center gap-2">
<span className="text-blue-600 font-bold">
{day.temp_max}°
</span>
<span className="text-gray-400">
{day.temp_min}°
</span>
</div>
</div>
))}
</div>
</div>
);
}
第四部分:实际效果对比
成果数据对比:
|
指标 |
Claude Code方式 |
传统手写方式 |
效率提升 |
|
总耗时 |
12小时 |
40小时 |
⬇ 70% |
|
代码行数 |
850行 |
920行 |
更简洁 |
|
功能完整度 |
95% |
100% |
基本完整 |
|
代码质量 |
规范、有注释 |
规范、更详细 |
相当 |
|
首屏加载 |
< 2秒 |
< 2秒 |
相同 |
|
Lighthouse评分 |
92分 |
94分 |
略低 |
功能完成情况
- ✅ 实时天气显示 - 完成
- ✅ 城市搜索功能 - 完成
- ✅ 7天预报 - 完成
- ✅ 温度单位切换 - 完成
- ✅ 响应式设计 - 完成
- ✅ 错误处理 - 完成
第五部分:常见问题与解决方案
|
问题 |
症状 |
原因 |
解决方案 |
|
API调用失败 |
Cannot fetch weather |
API endpoint错误或网络问题 |
检查URL和网络连接 |
|
类型错误 |
Type 'any' is not assignable |
TypeScript类型定义不完整 |
添加正确的类型注解 |
|
样式不显示 |
按钮没有颜色和样式 |
Tailwind CSS配置缺失 |
检查tailwind.config.js配置 |
|
搜索卡顿 |
输入时UI反应缓慢 |
没有实现防抖 |
使用lodash.debounce或自实现 |
第六部分:关键学习点
完成这个项目后,你将学到:
- 如何高效地使用Claude Code生成项目框架
- React Hooks(useState, useEffect)的实际应用
- TypeScript在React项目中的正确使用方式
- 组件化开发的最佳实践
- API集成和状态管理的核心概念
- 如何调试和优化Claude Code生成的代码
- 响应式设计和Tailwind CSS的实际应用
更多推荐

所有评论(0)