第一部分:项目规划与需求分析

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小时)

  1. 性能优化:减少重新渲染,优化API调用
  2. 代码分割:按路由或功能进行代码分割
  3. 打包优化:减少最终bundle大小
  4. 部署到Vercel/Netlify:一键部署
  5. 性能测试:用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或自实现

第六部分:关键学习点

完成这个项目后,你将学到:

  1. 如何高效地使用Claude Code生成项目框架
  2. React Hooks(useState, useEffect)的实际应用
  3. TypeScript在React项目中的正确使用方式
  4. 组件化开发的最佳实践
  5. API集成和状态管理的核心概念
  6. 如何调试和优化Claude Code生成的代码
  7. 响应式设计和Tailwind CSS的实际应用

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐