useContext:React 跨组件数据共享的优雅解决方案

关键点

  • useContext:React 提供的 Hook,用于在组件树中共享全局状态,简化跨组件数据传递。
  • 应用场景:主题切换、用户认证、语言设置和全局配置管理。
  • 实现方式:结合 createContextuseContext,实现灵活的状态共享。
  • 优化策略:包括性能优化、状态分割、可访问性和手机端适配。
  • 常见问题:过度使用导致耦合、不必要的重渲染和复杂状态管理。
  • 实践场景:通过一个多语言文档管理应用,展示 useContext 的实现与优化。

引言

在 React 应用中,组件间的数据共享是常见需求。传统的 Props 传递方式在深层组件树中可能导致“Props 钻透”(Prop Drilling),增加代码复杂性和维护成本。useContext 作为 React 的内置 Hook,结合 createContextProvider,提供了一种优雅的解决方案,用于在组件树中共享全局状态,无需逐层传递 Props。useContext 适用于主题切换、用户认证、语言设置等场景,极大地简化了状态管理。

然而,useContext 的使用并非没有挑战。过度使用可能导致组件耦合,不合理的状态设计可能引发性能问题。本文通过构建一个基于 React 的多语言文档管理应用,深入探讨 useContext 的工作原理、实现方式和优化实践。我们将实现主题切换、语言设置和用户认证功能,结合性能优化、可访问性和手机端适配,提供详细的代码示例和场景分析,帮助开发者掌握 useContext 的核心技术和最佳实践。

在现代 React 应用中,组件间的数据共享是构建动态用户界面的核心需求。传统的 Props 传递方式虽然简单,但在深层嵌套的组件树中,逐层传递 Props 会导致代码冗余和维护困难,这种现象被称为“Props 钻透”。React 的 useContext Hook 结合 createContextProvider,提供了一种高效的全局状态共享机制,允许开发者在组件树中的任何位置访问共享数据,无需显式传递 Props。

useContext 适用于多种场景,如主题切换、用户认证、语言设置和全局配置管理。它的简洁性和灵活性使其成为中小型项目的理想选择。然而,useContext 的使用需要注意性能优化、状态设计和组件耦合问题。本文通过一个基于 React 的多语言文档管理应用,全面探讨 useContext 的工作原理、实现方式和优化策略。我们将实现多语言切换、主题管理和用户认证功能,并提供性能优化、可访问性和手机端适配的实践方案。

通过本项目,您将学习到:

  • useContext 基础:创建和使用 Context 实现数据共享。
  • 高级功能:结合 useReduceruseContext 实现复杂状态管理。
  • 性能优化:使用状态分割和 React.memo 减少重渲染。
  • 可访问性:为动态内容添加 ARIA 属性,支持屏幕阅读器。
  • 手机端适配:优化响应式布局和触控交互。
  • 部署:将应用部署到 Vercel,支持高可用性和 CDN 加速。

本文面向有经验的开发者,假设您熟悉 HTML、CSS、JavaScript、React 和 TypeScript 基础知识。内容详实且实用,适合深入学习 useContext 的应用和优化。


需求分析

在动手编码之前,我们需要明确多语言文档管理应用的功能需求。一个清晰的需求清单能指导开发过程并帮助我们优化 useContext 的使用。以下是项目的核心需求:

  1. 多语言支持
    • 支持切换语言(如中文、英文、西班牙文)。
    • 动态更新 UI 文本(如按钮、标题)。
    • 持久化语言设置(如存储到 localStorage)。
  2. 主题管理
    • 支持切换亮色和暗色主题。
    • 动态应用主题样式(如背景色、文本色)。
  3. 用户认证
    • 管理用户登录状态(如用户名、角色)。
    • 支持登录和注销功能。
  4. 文档管理
    • 显示文档列表,支持过滤和搜索。
    • 提供文档预览功能。
  5. React 集成
    • 使用 useContext 共享语言、主题和用户状态。
    • 结合 useReducer 管理复杂状态逻辑。
  6. 性能优化
    • 避免不必要的重渲染。
    • 分割 Context 减少组件依赖。
  7. 可访问性(a11y)
    • 为动态内容添加 ARIA 属性。
    • 支持键盘导航和屏幕阅读器。
  8. 手机端适配
    • 响应式布局,适配不同屏幕尺寸。
    • 优化触控交互(如点击、滑动)。
  9. 部署
    • 集成到 Vite 项目,部署到 Vercel。
    • 支持 CDN 加速静态资源加载。

需求背后的意义

这些需求覆盖了 useContext 的核心应用场景,同时为学习状态管理和优化提供了实践机会:

  • 多语言支持:展示 useContext 在全局配置中的应用。
  • 主题管理:实现动态 UI 更新,优化用户体验。
  • 用户认证:管理全局用户状态,模拟真实业务场景。
  • 性能优化:解决 Context 导致的重渲染问题。
  • 可访问性:满足无障碍标准,扩大用户覆盖。
  • 手机端适配:适配移动设备,提升用户体验。

技术栈选择

在实现多语言文档管理应用之前,我们需要选择合适的技术栈。以下是本项目使用的工具和技术,以及选择它们的理由:

  • React 18
    核心前端框架,支持组件化开发和并发渲染,适合动态应用。
  • TypeScript
    提供类型安全,增强代码可维护性和 IDE 补全,适合复杂项目。
  • Vite
    构建工具,提供快速的开发服务器和高效的打包能力。
  • React Query
    数据获取和状态管理库,简化异步数据处理。
  • Tailwind CSS
    提供灵活的样式解决方案,支持响应式设计。
  • Vercel
    用于部署应用,提供高可用性和全球 CDN 支持。

技术栈优势

  • React 18:支持并发渲染,优化复杂应用性能。
  • TypeScript:提升代码质量,减少运行时错误。
  • Vite:启动速度快,热更新体验优越。
  • React Query:简化异步数据管理,优化文档加载。
  • Tailwind CSS:简化样式开发,支持响应式设计。
  • Vercel:与 React 生态深度整合,部署简单。

这些工具组合不仅易于上手,还能帮助开发者掌握 useContext 的最佳实践。


项目实现

现在进入核心部分——代码实现。我们将从项目搭建开始,逐步实现多语言切换、主题管理、用户认证和文档管理,结合性能优化、可访问性和部署。

1. 项目搭建

使用 Vite 创建一个 React + TypeScript 项目:

npm create vite@latest doc-manager -- --template react-ts
cd doc-manager
npm install
npm run dev

安装必要的依赖:

npm install @tanstack/react-query tailwindcss postcss autoprefixer

初始化 Tailwind CSS:

npx tailwindcss init -p

编辑 tailwind.config.js

/** @type {import('tailwindcss').Config} */
export default {content: ["./index.html","./src/**/*.{js,ts,jsx,tsx}",],theme: {extend: {},},plugins: [],
}

src/index.css 中引入 Tailwind:

@tailwind base;
@tailwind components;
@tailwind utilities;

2. 组件拆分

我们将应用拆分为以下组件:

  • App:根组件,负责整体布局和 Context 提供者。
  • DocumentList:显示文档列表,支持过滤和搜索。
  • DocumentPreview:预览选中的文档。
  • ThemeToggle:切换亮色和暗色主题。
  • LanguageSelector:选择语言,动态更新 UI。
  • AuthPanel:管理用户登录和注销。
  • AccessibilityPanel:管理可访问性设置。
文件结构
src/
├── components/
│   ├── DocumentList.tsx
│   ├── DocumentPreview.tsx
│   ├── ThemeToggle.tsx
│   ├── LanguageSelector.tsx
│   ├── AuthPanel.tsx
│   └── AccessibilityPanel.tsx
├── contexts/
│   ├── ThemeContext.ts
│   ├── LanguageContext.ts
│   ├── AuthContext.ts
├── hooks/
│   └── useDocuments.ts
├── types/
│   └── index.ts
├── App.tsx
├── main.tsx
└── index.css

3. Context 实现

3.1 主题管理

src/contexts/ThemeContext.ts

import { createContext, useContext, useState, useEffect } from 'react';interface ThemeContextType {theme: 'light' | 'dark';toggleTheme: () => void;
}const ThemeContext = createContext<ThemeContextType | undefined>(undefined);export function ThemeProvider({ children }: { children: React.ReactNode }) {const [theme, setTheme] = useState<'light' | 'dark'>('light');useEffect(() => {const savedTheme = localStorage.getItem('theme') as 'light' | 'dark' | null;if (savedTheme) {setTheme(savedTheme);document.documentElement.classList.toggle('dark', savedTheme === 'dark');}}, []);const toggleTheme = () => {setTheme(prev => {const newTheme = prev === 'light' ? 'dark' : 'light';localStorage.setItem('theme', newTheme);document.documentElement.classList.toggle('dark', newTheme === 'dark');return newTheme;});};return (<ThemeContext.Provider value={{ theme, toggleTheme }}>{children}</ThemeContext.Provider>);
}export function useTheme() {const context = useContext(ThemeContext);if (!context) {throw new Error('useTheme 必须在 ThemeProvider 内使用');}return context;
}

src/components/ThemeToggle.tsx

import { useTheme } from '../contexts/ThemeContext';function ThemeToggle() {const { theme, toggleTheme } = useTheme();return (<div className="p-4 bg-white dark:bg-gray-800 rounded-lg shadow"><h2 className="text-xl font-bold mb-4 text-gray-900 dark:text-white">主题切换</h2><buttononClick={toggleTheme}className="px-4 py-2 bg-blue-500 text-white rounded-lg"aria-label={`切换到${theme === 'light' ? '暗色模式' : '亮色模式'}`}>{theme === 'light' ? '暗色模式' : '亮色模式'}</button></div>);
}export default ThemeToggle;

实现过程

  • 创建 ThemeContext 共享主题状态和切换函数。
  • 使用 useEffect 持久化主题设置。
  • 动态更新 document.documentElement 的类名。

避坑

  • 确保 useContextProvider 内部使用。
  • 使用 Tailwind 的 dark: 类实现主题样式。
3.2 语言管理

src/contexts/LanguageContext.ts

import { createContext, useContext, useState, useEffect } from 'react';interface LanguageContextType {language: 'zh' | 'en' | 'es';setLanguage: (lang: 'zh' | 'en' | 'es') => void;t: (key: string) => string;
}const translations = {zh: {title: '文档管理器',search: '搜索文档',login: '登录',logout: '注销',},en: {title: 'Document Manager',search: 'Search Documents',login: 'Login',logout: 'Logout',},es: {title: 'Gestor de Documentos',search: 'Buscar Documentos',login: 'Iniciar Sesión',logout: 'Cerrar Sesión',},
};const LanguageContext = createContext<LanguageContextType | undefined>(undefined);export function LanguageProvider({ children }: { children: React.ReactNode }) {const [language, setLanguage] = useState<'zh' | 'en' | 'es'>('zh');useEffect(() => {const savedLang = localStorage.getItem('language') as 'zh' | 'en' | 'es' | null;if (savedLang) {setLanguage(savedLang);}}, []);const t = (key: string) => translations[language][key] || key;return (<LanguageContext.Provider value={{ language, setLanguage, t }}>{children}</LanguageContext.Provider>);
}export function useLanguage() {const context = useContext(LanguageContext);if (!context) {throw new Error('useLanguage 必须在 LanguageProvider 内使用');}return context;
}

src/components/LanguageSelector.tsx

import { useLanguage } from '../contexts/LanguageContext';function LanguageSelector() {const { language, setLanguage, t } = useLanguage();return (<div className="p-4 bg-white dark:bg-gray-800 rounded-lg shadow"><h2 className="text-xl font-bold mb-4 text-gray-900 dark:text-white">{t('title')}</h2><selectvalue={language}onChange={e => setLanguage(e.target.value as 'zh' | 'en' | 'es')}className="p-2 border rounded-lg"aria-label="选择语言"><option value="zh">中文</option><option value="en">English</option><option value="es">Español</option></select></div>);
}export default LanguageSelector;

避坑

  • 提供默认翻译,防止未定义键。
  • 持久化语言设置到 localStorage。
3.3 用户认证

src/contexts/AuthContext.ts

import { createContext, useContext, useReducer } from 'react';interface AuthState {isAuthenticated: boolean;user: { username: string; role: 'admin' | 'user' } | null;
}type AuthAction =| { type: 'LOGIN'; payload: { username: string; role: 'admin' | 'user' } }| { type: 'LOGOUT' };interface AuthContextType {state: AuthState;login: (username: string, role: 'admin' | 'user') => void;logout: () => void;
}const AuthContext = createContext<AuthContextType | undefined>(undefined);function authReducer(state: AuthState, action: AuthAction): AuthState {switch (action.type) {case 'LOGIN':return { isAuthenticated: true, user: action.payload };case 'LOGOUT':return { isAuthenticated: false, user: null };default:return state;}
}export function AuthProvider({ children }: { children: React.ReactNode }) {const [state, dispatch] = useReducer(authReducer, { isAuthenticated: false, user: null });const login = (username: string, role: 'admin' | 'user') => {dispatch({ type: 'LOGIN', payload: { username, role } });};const logout = () => {dispatch({ type: 'LOGOUT' });};return (<AuthContext.Provider value={{ state, login, logout }}>{children}</AuthContext.Provider>);
}export function useAuth() {const context = useContext(AuthContext);if (!context) {throw new Error('useAuth 必须在 AuthProvider 内使用');}return context;
}

src/components/AuthPanel.tsx

import { useState } from 'react';
import { useAuth } from '../contexts/AuthContext';
import { useLanguage } from '../contexts/LanguageContext';function AuthPanel() {const { state, login, logout } = useAuth();const { t } = useLanguage();const [username, setUsername] = useState('');const handleLogin = () => {if (username) {login(username, 'user');}};return (<div className="p-4 bg-white dark:bg-gray-800 rounded-lg shadow"><h2 className="text-xl font-bold mb-4 text-gray-900 dark:text-white">用户认证</h2>{state.isAuthenticated ? (<div><p aria-live="polite">欢迎, {state.user?.username}</p><buttononClick={logout}className="px-4 py-2 bg-red-500 text-white rounded-lg"aria-label={t('logout')}>{t('logout')}</button></div>) : (<div className="flex flex-col space-y-4"><inputtype="text"value={username}onChange={e => setUsername(e.target.value)}className="p-2 border rounded-lg"placeholder="用户名"aria-label="输入用户名"/><buttononClick={handleLogin}className="px-4 py-2 bg-blue-500 text-white rounded-lg"aria-label={t('login')}>{t('login')}</button></div>)}</div>);
}export default AuthPanel;

避坑

  • 使用 useReducer 管理复杂状态逻辑。
  • 确保状态更新触发正确的重渲染。

4. 文档管理

src/hooks/useDocuments.ts

import { useQuery } from '@tanstack/react-query';
import type { Document } from '../types';export function useDocuments() {return useQuery<Document[]>({queryKey: ['documents'],queryFn: async () => {// 模拟 API 调用await new Promise(resolve => setTimeout(resolve, 1000));return [{ id: 1, title: '报告 A', content: '这是报告 A 的内容' },{ id: 2, title: '报告 B', content: '这是报告 B 的内容' },];},});
}

src/types/index.ts

export interface Document {id: number;title: string;content: string;
}

src/components/DocumentList.tsx

import { memo } from 'react';
import { useDocuments } from '../hooks/useDocuments';
import { useLanguage } from '../contexts/LanguageContext';function DocumentList({ onSelect }: { onSelect: (doc: Document) => void }) {const { data: documents, isLoading } = useDocuments();const { t } = useLanguage();if (isLoading) {return <div className="p-4">加载中...</div>;}return (<div className="p-4 bg-white dark:bg-gray-800 rounded-lg shadow"><h2 className="text-xl font-bold mb-4 text-gray-900 dark:text-white">{t('title')}</h2><inputtype="text"className="p-2 border rounded-lg mb-4 w-full"placeholder={t('search')}aria-label={t('search')}/><ul>{documents?.map(doc => (<likey={doc.id}className="p-2 cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-700"onClick={() => onSelect(doc)}role="button"aria-label={`查看文档 ${doc.title}`}>{doc.title}</li>))}</ul></div>);
}export default memo(DocumentList);

src/components/DocumentPreview.tsx

import { memo } from 'react';
import type { Document } from '../types';function DocumentPreview({ document }: { document: Document | null }) {if (!document) {return <div className="p-4">请选择一个文档</div>;}return (<div className="p-4 bg-white dark:bg-gray-800 rounded-lg shadow"><h2 className="text-xl font-bold mb-4 text-gray-900 dark:text-white">{document.title}</h2><p>{document.content}</p></div>);
}export default memo(DocumentPreview);

避坑

  • 使用 React.memo 防止不必要的重渲染。
  • 结合 React Query 管理异步数据。

5. 性能优化

5.1 分割 Context

ThemeContextLanguageContextAuthContext 分开,避免单一 Context 导致所有消费者重渲染。

5.2 使用 React.memo

src/components/DocumentList.tsxDocumentPreview.tsx 已使用 memo 包裹,防止 Props 未变化时的重渲染。

5.3 优化状态更新

src/contexts/AuthContext.ts(使用 useReducer):

  • 集中管理状态逻辑,减少直接 setState 调用。
  • 确保状态更新精准,避免全局重渲染。

避坑

  • 仅在必要时更新 Context 值。
  • 使用 useMemo 包装复杂对象:
    const value = useMemo(() => ({ theme, toggleTheme }), [theme]);
    

6. 可访问性(a11y)

src/components/AccessibilityPanel.tsx

import { useState } from 'react';
import { useTheme } from '../contexts/ThemeContext';function AccessibilityPanel() {const [highContrast, setHighContrast] = useState(false);const { theme } = useTheme();return (<div className="p-4 bg-white dark:bg-gray-800 rounded-lg shadow"><h2 className="text-xl font-bold mb-4 text-gray-900 dark:text-white">可访问性设置</h2><label className="flex items-center space-x-2"><inputtype="checkbox"checked={highContrast}onChange={() => setHighContrast(!highContrast)}className="p-2"aria-label="启用高对比度模式"/><span>高对比度模式</span></label><div className={highContrast ? 'bg-black text-white' : ''}><p aria-live="polite">测试文本: {highContrast ? '高对比度' : '正常'}</p></div></div>);
}export default AccessibilityPanel;

避坑

  • 为动态内容添加 aria-live 属性。
  • 测试屏幕阅读器(如 NVDA、VoiceOver)。

7. 手机端适配

src/App.tsx

import { useState } from 'react';
import { ThemeProvider } from './contexts/ThemeContext';
import { LanguageProvider } from './contexts/LanguageContext';
import { AuthProvider } from './contexts/AuthContext';
import DocumentList from './components/DocumentList';
import DocumentPreview from './components/DocumentPreview';
import ThemeToggle from './components/ThemeToggle';
import LanguageSelector from './components/LanguageSelector';
import AuthPanel from './components/AuthPanel';
import AccessibilityPanel from './components/AccessibilityPanel';
import type { Document } from './types';function App() {const [selectedDoc, setSelectedDoc] = useState<Document | null>(null);return (<ThemeProvider><LanguageProvider><AuthProvider><div className="min-h-screen bg-gray-100 dark:bg-gray-900 p-2 md:p-4"><h1 className="text-2xl md:text-3xl font-bold text-center p-4 text-gray-900 dark:text-white">文档管理器</h1><div className="grid grid-cols-1 md:grid-cols-2 gap-2 md:gap-4 max-w-5xl mx-auto"><DocumentList onSelect={setSelectedDoc} /><DocumentPreview document={selectedDoc} /><ThemeToggle /><LanguageSelector /><AuthPanel /><AccessibilityPanel /></div></div></AuthProvider></LanguageProvider></ThemeProvider>);
}export default App;

避坑

  • 使用 Tailwind 的响应式类(如 md:)适配屏幕。
  • 确保触控区域足够大(至少 48x48 像素)。

8. 部署

8.1 构建项目
npm run build
8.2 部署到 Vercel
  1. 注册 Vercel:访问 Vercel 官网并创建账号。
  2. 新建项目:选择“New Project”。
  3. 导入仓库:将项目推送至 GitHub 并导入。
  4. 配置构建
    • 构建命令:npm run build
    • 输出目录:dist
  5. 部署:点击“Deploy”.

避坑

  • 确保静态资源路径正确(使用相对路径)。
  • 使用 CDN 加速 Tailwind CSS 和其他资源。

常见问题与解决方案

9.1 不必要的重渲染

问题:Context 变化导致所有消费者重渲染。

解决方案

  • 分割 Context(如 ThemeContextAuthContext)。
  • 使用 React.memo 包裹消费者组件:
    export default memo(Component);
    

9.2 状态管理复杂性

问题:复杂状态逻辑导致 Context 难以维护。

解决方案

  • 使用 useReducer 集中管理状态:
    const [state, dispatch] = useReducer(authReducer, initialState);
    
  • 结合 React Query 管理异步数据。

9.3 组件耦合

问题:过度依赖 Context 导致组件难以复用。

解决方案

  • 将 Context 逻辑封装到 Hook:
    export function useTheme() {const context = useContext(ThemeContext);if (!context) throw new Error('useTheme 必须在 ThemeProvider 内');return context;
    }
    
  • 限制 Context 使用范围。

练习:添加文档过滤功能

为巩固所学,设计一个练习:为 DocumentList 添加动态过滤功能,使用 Context 共享过滤状态。

需求

  • 支持按标题过滤文档。
  • 使用 Context 共享过滤状态。
  • 动态更新文档列表。

实现步骤

1. 创建 Filter Context

src/contexts/FilterContext.ts

import { createContext, useContext, useState } from 'react';interface FilterContextType {filter: string;setFilter: (filter: string) => void;
}const FilterContext = createContext<FilterContextType | undefined>(undefined);export function FilterProvider({ children }: { children: React.ReactNode }) {const [filter, setFilter] = useState('');return (<FilterContext.Provider value={{ filter, setFilter }}>{children}</FilterContext.Provider>);
}export function useFilter() {const context = useContext(FilterContext);if (!context) {throw new Error('useFilter 必须在 FilterProvider 内使用');}return context;
}
2. 更新 DocumentList

src/components/DocumentList.tsx(更新):

import { memo } from 'react';
import { useDocuments } from '../hooks/useDocuments';
import { useLanguage } from '../contexts/LanguageContext';
import { useFilter } from '../contexts/FilterContext';function DocumentList({ onSelect }: { onSelect: (doc: Document) => void }) {const { data: documents, isLoading } = useDocuments();const { t } = useLanguage();const { filter, setFilter } = useFilter();if (isLoading) {return <div className="p-4">加载中...</div>;}const filteredDocs = documents?.filter(doc =>doc.title.toLowerCase().includes(filter.toLowerCase()));return (<div className="p-4 bg-white dark:bg-gray-800 rounded-lg shadow"><h2 className="text-xl font-bold mb-4 text-gray-900 dark:text-white">{t('title')}</h2><inputtype="text"value={filter}onChange={e => setFilter(e.target.value)}className="p-2 border rounded-lg mb-4 w-full"placeholder={t('search')}aria-label={t('search')}/><ul>{filteredDocs?.map(doc => (<likey={doc.id}className="p-2 cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-700"onClick={() => onSelect(doc)}role="button"aria-label={`查看文档 ${doc.title}`}>{doc.title}</li>))}</ul></div>);
}export default memo(DocumentList);
3. 更新 App

src/App.tsx(更新):

import { FilterProvider } from './contexts/FilterContext';function App() {const [selectedDoc, setSelectedDoc] = useState<Document | null>(null);return (<ThemeProvider><LanguageProvider><AuthProvider><FilterProvider><div className="min-h-screen bg-gray-100 dark:bg-gray-900 p-2 md:p-4"><h1 className="text-2xl md:text-3xl font-bold text-center p-4 text-gray-900 dark:text-white">文档管理器</h1><div className="grid grid-cols-1 md:grid-cols-2 gap-2 md:gap-4 max-w-5xl mx-auto"><DocumentList onSelect={setSelectedDoc} /><DocumentPreview document={selectedDoc} /><ThemeToggle /><LanguageSelector /><AuthPanel /><AccessibilityPanel /></div></div></FilterProvider></AuthProvider></LanguageProvider></ThemeProvider>);
}

目标

  • 学会使用 Context 共享动态过滤状态。
  • 优化过滤逻辑,减少重渲染。

注意事项

  • Context 配置:确保 useContextProvider 内部使用。
  • 性能优化:分割 Context 和使用 React.memo
  • 可访问性:为动态内容添加 ARIA 属性。
  • 学习建议:参考 React 文档、React Query 文档 和 Vite 文档.

结语

通过这个多语言文档管理应用,您深入掌握了 useContext 的使用方式,从基础数据共享到复杂状态管理,结合性能优化和可访问性实践。这些技能将帮助您构建高效、可维护的 React 应用,应对复杂业务场景。希望您继续探索 useContext 的高级应用,如结合 Redux 或服务器端状态管理,打造卓越的用户体验!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/88209.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/88209.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

20250706-8-Docker快速入门(下)-Dockerfile介绍与基本使用_笔记

一、Dockerfile构建镜像1. Dockerfile概述&#xfeff;定义&#xff1a;Dockerfile是一个用于自动构建镜像的文本文件&#xff0c;由一条条指令组成工作原理&#xff1a;指令逐步执行&#xff0c;每个指令完成不同功能典型指令示例&#xff1a;FROM centos:latest&#xff1a;基…

Git系列--3.分支管理

目录 一、理解分支 1.1图示 1.2 打印仓库下有哪些分支 1.3创建分支 1.4HEAD与切换分支 1.5合并分支 1.6流程图理解 二、删除分支 ​ 三、合并分支冲突 3.1.问题导入 3.2.解决 3.3合并图示 四、合并模式 4.1合并​编辑 4.2变基 五、bug分支 5.1背景建立 5.2解决步骤 5.2.1…

Vue.js TDD开发深度指南:工具链配置与精细化测试策略

“TDD不是测试优先的开发&#xff0c;而是设计优先的开发。” —— Robert C. Martin 引言 在Vue.js项目中实施测试驱动开发&#xff08;TDD&#xff09;是构建健壮应用的关键路径。但许多开发者在实践中常遇到&#xff1a; 工具链配置复杂导致放弃不同类型组件测试策略混淆测…

基于物联网的智能家居控制系统设计与实现

标题:基于物联网的智能家居控制系统设计与实现内容:1.摘要 随着物联网技术的飞速发展&#xff0c;智能家居逐渐成为人们关注的焦点。本文旨在设计并实现一个基于物联网的智能家居控制系统&#xff0c;以提高家居的智能化水平和用户的生活便利性。通过采用先进的传感器技术、通信…

Vue 中使用 Cesium 实现可拖拽点标记及坐标实时显示功能

在 Cesium 地图开发中&#xff0c;实现点标记的拖拽交互并实时显示坐标信息是一个常见的需求。本文将详细介绍如何在 Vue 框架中使用 Cesium 的 Primitive 方式创建点标记&#xff0c;并实现拖拽功能及坐标提示框跟随效果。先看效果图功能实现概述我们将实现的功能包括&#xf…

HTML 插件:构建网页的强大工具

HTML 插件:构建网页的强大工具 引言 HTML 插件是网页设计中不可或缺的一部分,它们为网页增添了丰富的交互性和动态效果。本文将深入探讨 HTML 插件的概念、类型、应用及其在网页开发中的重要性。 什么是 HTML 插件? HTML 插件,也称为 HTML 组件或 HTML 控件,是指嵌入到…

NeRF、3DGS、2DGS下三维重建相关方法介绍及以及在实景三维领域的最新实践

一、引言 在计算机视觉与图形学领域&#xff0c;三维重建技术正经历从传统几何建模向智能化神经表征的范式转变。近年来&#xff0c;随着深度学习算法的迭代、传感器技术的进步及计算硬件的升级&#xff0c;以神经辐射场&#xff08;NeRF&#xff09;和高斯泼溅&#xff08;2D…

rt thread studio 和 KEIL对于使用rt thread 的中间件和组件,哪个更方便

下面我从中间件/组件集成和开发体验两个角度&#xff0c;详细对比 RT-Thread Studio 和 Keil MDK 的便利性&#xff1a;1. 中间件和组件集成 RT-Thread Studio 集成RT-Thread生态&#xff1a;内置RT-Thread的包管理器&#xff08;RT-Thread Package Manager&#xff09;&#x…

Spring Boot 项目开发实战:入门应用部分原理示例讲解

前言Spring Boot 作为当前 Java 开发领域最流行的框架之一&#xff0c;以其 "约定优于配置" 的理念极大简化了企业级应用的开发流程。本文将基于《Spring Boot 项目开发教程&#xff08;慕课版&#xff09;》中的资产管理系统项目&#xff0c;深入解析 Spring Boot 的…

ByteBrain x 清华 VLDB25|时序多模态大语言模型 ChatTS

资料来源&#xff1a;火山引擎-开发者社区 近年来&#xff0c;多模态大语言模型&#xff08;MLLM&#xff09;发展迅速&#xff0c;并在图像、视频、音频等领域取得了突破性成果。然而&#xff0c;相较于这些研究较为成熟的模态&#xff0c;时间序列这一类型的数据与大模型结合…

WPF学习笔记(25)MVVM框架与项目实例

MVVM框架与项目实例一、MVVM框架1. 概述2. 核心组件与优势一、MVVM项目1.普通项目2. MVVM架构3. MVVM项目实例1. 项目准备2. LoginViewModel与Login2. MainWindowViewModel4. MVVM项目优化1. BaseViewModel2. RealyCommand3. 效果展示总结一、MVVM框架 1. 概述 官方文档&…

MySQL实操

## 基于MySQL#先启动MySQL服务#第一次登录[rootlocalhost ~]# mysql -uroot -P3306#密码登录[rootlocalhost ~]# mysql -uroot -pEnter password: Welcome to the MySQL monitor. Commands end with ; or \g.Your MySQL connection id is 9Server version: 8.0.41 Source dist…

ez_rust_writeup

一道简单的[[rust逆向]] #rust逆向 #位运算 题目信息 文件名&#xff1a;ezrust.exe 题目附件&#xff1a;https://wwfj.lanzoul.com/iczMR30k5j4h 密码:bueq 题目分析 1. 初步分析 这是一道Rust编写的逆向题目。通过IDA分析可以看到&#xff0c;这是一个典型的flag验证程序。 …

【QT】-隐式转换 explicit用法

通俗易懂的解释:隐式转换 vs 显式转换 什么是隐式转换? 隐式转换就是编译器偷偷帮你做的类型转换,你甚至都没意识到它发生了。 例子: cpp 运行 double x = 5; // 隐式:int → double(5 变成 5.0) int y = x * 2.5; // 隐式:double → int(截断小数部分) 构造函数的隐…

Django核心知识点详解:JSON、AJAX、Cookie、Session与用户认证

1. JSON数据格式详解1.1 什么是JSON&#xff1f;JSON&#xff08;JavaScript Object Notation&#xff09;是一种轻量级的数据交换格式&#xff0c;具有以下特点&#xff1a;独立于语言&#xff0c;几乎所有编程语言都支持易于人阅读和编写易于机器解析和生成基于文本&#xff…

[特殊字符] Python 实战 | 批量统计中文文档词频并导出 Excel

本文展示如何用 Python 脚本&#xff1a; 批量读取文件夹中的多篇中文文档&#xff1b; 用 jieba 分词并统计词频&#xff08;过滤停用词与单字符&#xff09;&#xff1b; 将各文档词频输出为对应 Excel 文件&#xff1b; 是文本分析、内容审查、报告编写中的实用技巧。 &…

共享打印机(详细操作+常见问题:需输入用户名密码、无法连接等)

文章目录一、设置打印机共享的准备工作二、Windows系统下打印机共享设置1. 启用主机打印机共享2. 客户端添加共享打印机三、我所遇到的问题及解决方法客户机遇到输入用户名、密码错误代码 0x0000011b一、错误代码 0x0000011b 的含义二、解决方法添加打印机没成功其他问题此次打…

在 Windows 系统上配置 [go-zero](https://go-zero.dev) 开发环境教程

&#x1f4bb; 在 Windows 系统上配置 go-zero 开发环境教程 本教程将详细介绍如何在 Windows 系统上配置 go-zero 微服务框架的开发环境&#xff0c;包括依赖安装、路径配置、常见问题等。 &#x1f9f1; 一、前置环境安装 1. 安装 Go 下载地址&#xff1a;https://go.dev/…

开源=白嫖?

国内有一个非常浓重的思想&#xff0c;开源&#xff0c;开源就是免费&#xff0c;就是白嫖&#xff0c;就是不花钱&#xff0c;白给。那么什么是开源&#xff1f;“源代码”是软件中大多数计算机用户从未见过的部分;它是计算机程序员可以操纵的代码&#xff0c;以改变一个软件(…

2048-控制台版本

2048控制台版 文章目录2048控制台版实现效果&#xff1a;在这里插入图片描述库函数使用&#xff1a;初始化变量功能函数实现&#xff1a;状态判断函数int Judge&#xff08;&#xff09;&#xff1b;数字生成函数 bool CtreateNumber&#xff08;&#xff09;打印游戏界面 void…