React作为当今最流行的前端框架之一,其组件化思想和声明式编程模式彻底改变了现代Web开发。本教程将通过构建一个完整的天气查询应用,系统性地讲解React的核心概念和最佳实践。项目将覆盖React 18最新特性,使用Vite构建工具,并整合现代前端开发工作流。
城市天气实时查询
温度单位切换(℃/℉)
最近查询历史记录
天气图标动态展示
响应式布局适配
加载状态与错误处理
React 18(函数组件 + Hooks)
React Router v6
Axios
OpenWeatherMap API
CSS Modules
Vite 5
ES6+基础(箭头函数、解构赋值、模块化)
基础HTML/CSS
npm/yarn基础用法
REST API概念
项目搭建
npm create vite@latest weather-app -- --template react
cd weather-app
npm install react-router-dom axios react-icons
// src/main.jsx
import { createRoot } from 'react-dom/client'
import { BrowserRouter } from 'react-router-dom'
import App from './App'
createRoot(document.getElementById('root')).render(
<BrowserRouter>
<App />
</BrowserRouter>
)
// src/context/WeatherContext.jsx
import { createContext, useContext, useReducer } from 'react'
const WeatherContext = createContext()
const initialState = {
unit: 'metric',
history: [],
loading: false,
error: null
}
const reducer = (state, action) => {
switch (action.type) {
case 'SET_UNIT':
return { ...state, unit: action.payload }
case 'ADD_HISTORY':
return { ...state, history: [action.payload, ...state.history.slice(0, 4)] }
case 'SET_LOADING':
return { ...state, loading: action.payload }
case 'SET_ERROR':
return { ...state, error: action.payload }
default:
return state
}
}
export const WeatherProvider = ({ children }) => {
const [state, dispatch] = useReducer(reducer, initialState)
return (
<WeatherContext.Provider value={{ ...state, dispatch }}>
{children}
</WeatherContext.Provider>
)
}
export const useWeather = () => useContext(WeatherContext)
// src/components/WeatherSearch.jsx
import { useState } from 'react'
import { useWeather } from '../context/WeatherContext'
import axios from 'axios'
const WeatherSearch = () => {
const [city, setCity] = useState('')
const { dispatch } = useWeather()
const handleSearch = async () => {
try {
dispatch({ type: 'SET_LOADING', payload: true })
const response = await axios.get(
`https://api.openweathermap.org/data/2.5/weather?q=${city}&units=metric&appid=${import.meta.env.VITE_API_KEY}`
)
dispatch({
type: 'ADD_HISTORY',
payload: {
city: response.data.name,
time: new Date().toLocaleString(),
temp: response.data.main.temp
}
})
} catch (error) {
dispatch({ type: 'SET_ERROR', payload: error.message })
} finally {
dispatch({ type: 'SET_LOADING', payload: false })
}
}
return (
<div className="search-container">
<input
type="text"
value={city}
onChange={(e) => setCity(e.target.value)}
placeholder="Enter city name"
/>
<button onClick={handleSearch} disabled={!city.trim()}>
Search
</button>
</div>
)
}
// src/components/UnitToggle.jsx
import { useWeather } from '../context/WeatherContext'
const UnitToggle = () => {
const { unit, dispatch } = useWeather()
return (
<div className="unit-toggle">
<button
onClick={() => dispatch({ type: 'SET_UNIT', payload: 'metric' })}
className={unit === 'metric' ? 'active' : ''}
>
℃
</button>
<button
onClick={() => dispatch({ type: 'SET_UNIT', payload: 'imperial' })}
className={unit === 'imperial' ? 'active' : ''}
>
℉
</button>
</div>
)
}
// 使用React.memo优化组件渲染
const MemoizedComponent = React.memo(({ data }) => {
/* render logic */
})
// 使用useCallback缓存回调函数
const memoizedCallback = useCallback(() => {
doSomething(a, b)
}, [a, b])
错误边界处理
jsx
class ErrorBoundary extends Component {
state = { hasError: false }
static getDerivedStateFromError(error) {
return { hasError: true }
}
componentDidCatch(error, info) {
logErrorToService(error, info)
}
render() {
return this.state.hasError ? <FallbackUI /> : this.props.children
}
}
添加Redux Toolkit进行复杂状态管理
集成TypeScript类型系统
实现PWA离线功能
添加Jest + React Testing Library测试
使用Chart.js实现天气趋势可视化
部署指南
npm run build
部署到Vercel
npm install -g vercel
vercel deploy
通过本项目的实践,我们系统性地掌握了:
React函数组件与Hooks的核心用法
上下文状态管理的最佳实践
现代前端工程化配置
第三方API集成方法
性能优化关键策略
如果你有什么疑问,欢迎你私信与我交流沟通