React进阶:状态管理选择题
React进阶:状态管理选择题
引言
随着React应用复杂度增加,选择合适的状态管理方案成为我们面临的关键决策。
状态管理本质上是解决"谁来存储数据"以及"如何更新和分发这些数据"的问题。在React生态中,随着应用规模扩大,不同组件间的状态共享和同步变得越来越复杂,这促使了众多状态管理库的诞生。
状态管理方案全景
React内置状态管理
React本身提供了多种内置状态管理机制,包括组件局部状态和Context API。这些原生解决方案是理解其他状态管理库的基础。
useState:组件局部状态
useState
钩子提供了最简单的状态管理方式,适用于组件内部状态。它遵循不可变性原则,每次状态更新都会触发组件重新渲染。
function Counter() {// 局部组件状态const [count, setCount] = useState(0);return (<div><p>Count: {count}</p><button onClick={() => setCount(count + 1)}>+</button><button onClick={() => setCount(prevCount => prevCount - 1)}>-</button></div>);
}
useState
的关键特性是简单直观,但当状态逻辑复杂时会变得难以维护。例如,当多个状态相互依赖时,使用多个useState
调用会导致代码变得零散且难以追踪状态变化。
useReducer:复杂状态逻辑
useReducer
提供了更结构化的状态管理方式,特别适合处理包含多个子值的复杂状态逻辑。它借鉴了Redux的设计理念,通过分发action来更新状态。
function complexCounter() {// 复杂状态逻辑const [state, dispatch] = useReducer((state, action) => {switch (action.type) {case 'increment': return {count: state.count + 1};case 'decrement': return {count: state.count - 1};case 'reset': return {count: 0};case 'set': return {count: action.payload};default: return state;}}, {count: 0});return (<div><p>Count: {state.count}</p><button onClick={() => dispatch({type: 'increment'})}>+</button><button onClick={() => dispatch({type: 'decrement'})}>-</button><button onClick={() => dispatch({type: 'reset'})}>Reset</button><button onClick={() => dispatch({type: 'set', payload: 100})}>Set to 100</button></div>);
}
useReducer
的优势在于集中管理相关状态逻辑,使状态变化更可预测。然而,它仍然局限于单个组件或通过props传递,无法直接解决跨组件状态共享问题。
Context API:跨组件状态共享
Context API提供了一种在组件树中共享状态的方式,无需通过props层层传递。它是React内置的"依赖注入"系统。
// 创建上下文
const ThemeContext = React.createContext({theme: 'light',setTheme: () => {}
});function App() {const [theme, setTheme] = useState('light');// 提供上下文值给整个组件树return (<ThemeContext.Provider value={{theme, setTheme}}><Header /><Main /><Footer /></ThemeContext.Provider>);
}function ThemedButton() {// 消费上下文,无需通过props传递const {theme, setTheme} = useContext(ThemeContext);return (<button style={{background: theme === 'light' ? '#fff' : '#000', color: theme === 'light' ? '#000' : '#fff'}}onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>当前主题: {theme} (点击切换)</button>);
}
Context API解决了props drilling问题,但它本身并不是一个完整的状态管理解决方案。每当Provider的值变化时,所有消费该Context的组件都会重新渲染,这可能导致性能问题。此外,大型应用中使用多个Context会导致组件嵌套过深,降低代码可读性。
适用场景:组件树深度适中,状态共享需求简单的中小型应用。典型用例包括主题切换、用户偏好设置、多语言支持等全局配置。
优势:
- 零依赖,React官方支持,保证长期维护
- 学习成本低,API简洁,与React组件模型一致
- 无需额外库,减小打包体积,降低项目复杂度
- 无需额外配置,可即时使用
局限性:
- Context触发的重渲染优化困难,可能导致性能问题
- 多Context组合使用导致嵌套地狱,降低代码可读性
- 缺乏专门的开发者工具支持,调试体验较差
- 状态变化追踪困难,无法实现时间旅行调试
- 难以实现状态持久化、中间件等高级功能
Redux生态系统
Redux作为React生态中最成熟的状态管理解决方案,通过单一数据源、不可变数据和纯函数reducer实现可预测的状态管理。现代Redux通过Redux Toolkit(RTK)大幅简化了开发体验。
Redux核心概念
Redux基于三个基本原则:
- 单一数据源:整个应用的状态存储在单个store的对象树中
- 状态只读:唯一改变状态的方法是触发action
- 使用纯函数进行修改:reducer是纯函数,接收先前的状态和action,返回新状态
传统Redux实现需要手动创建action creators、reducers和store,代码量较大:
// 定义action类型
const INCREMENT = 'INCREMENT';
const DECREMENT = 'DECREMENT';// 创建action creators
const increment = () => ({ type: INCREMENT });
const decrement = () => ({ type: DECREMENT });// 创建reducer
const counterReducer = (state = { value: 0 }, action) => {switch (action.type) {case INCREMENT:return { ...state, value: state.value + 1 };case DECREMENT:return { ...state, value: state.value - 1 };default:return state;}
};// 创建store
const store = createStore(counterReducer);// 组件中使用
function Counter() {const count = useSelector(state => state.value);const dispatch = useDispatch();return (<div><p>Count: {count}</p><button onClick={() => dispatch(increment())}>+</button><button onClick={() => dispatch(decrement())}>-</button></div>);
}
Redux Toolkit:现代Redux开发
Redux Toolkit极大简化了Redux开发,减少了样板代码,提供了更现代化的API:
// 创建slice
import { createSlice, configureStore } from '@reduxjs/toolkit';const counterSlice = createSlice({name: 'counter',initialState: { value: 0 },reducers: {increment: state => {// RTK使用Immer库,允许"直接修改"状态// 实际上Immer会在底层确保不可变性state.value += 1;},decrement: state => {state.value -= 1;},incrementByAmount: (state, action) => {state.value += action.payload;}}
});// 导出action creators
export const { increment, decrement, incrementByAmount } = counterSlice.actions;// 创建store
const store = configureStore({reducer: {counter: counterSlice.reducer}
});// 组件中使用
function Counter() {// 使用选择器获取状态片段const count = useSelector(state => state.counter.value);const dispatch = useDispatch();return (<div><p>Count: {count}</p><button onClick={() => dispatch(increment())}>+</button><button onClick={() => dispatch(decrement())}>-</button><button onClick={() => dispatch(incrementByAmount(10))}>+10</button></div>);
}
RTK不仅简化了Redux的使用,还内置了多种开发工具和最佳实践,包括Immer(简化不可变更新)、Thunk(处理异步逻辑)和DevTools扩展支持。
异步操作处理
处理API请求等异步操作是状态管理的重要部分。Redux通过中间件处理异步逻辑,RTK提供了createAsyncThunk
简化这一过程:
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';// 创建异步thunk
export const fetchUserById = createAsyncThunk('users/fetchById',async (userId, { rejectWithValue }) => {try {const response = await fetch(`https://api.example.com/users/${userId}`);if (!response.ok) throw new Error('Server Error');return await response.json();} catch (error) {return rejectWithValue(error.message);}}
);const userSlice = createSlice({name: 'user',initialState: {data: null,status: 'idle', // 'idle' | 'loading' | 'succeeded' | 'failed'error: null},reducers: {},extraReducers: (builder) => {builder.addCase(fetchUserById.pending, (state) => {state.status = 'loading';}).addCase(fetchUserById.fulfilled, (state, action) => {state.status = 'succeeded';state.data = action.payload;}).addCase(fetchUserById.rejected, (state, action) => {state.status = 'failed';state.error = action.payload;});}
});// 组件中使用
function UserProfile({ userId }) {const dispatch = useDispatch();const { data: user, status, error } = useSelector(state => state.user);useEffect(() => {if (status === 'idle') {dispatch(fetchUserById(userId));}}, [userId, status, dispatch]);if (status === 'loading') return <div>Loading...</div>;if (status === 'failed') return <div>Error: {error}</div>;if (!user) return null;return (<div><h1>{user.name}</h1><p>Email: {user.email}</p></div>);
}
这种模式提供了一致的方式处理异步操作生命周期(开始、成功、失败),使状态管理更加可预测。
适用场景:大型企业级应用,复杂的状态逻辑,需要严格状态追踪的场景,多团队协作的项目。典型用例包括后台管理系统、复杂表单应用、大型电商平台等。
优势:
- 成熟的生态系统,大量中间件和第三方集成
- 强大的开发者工具,时间旅行调试使问题定位更容易
- 状态变化可预测,严格的单向数据流提高代码可维护性
- RTK极大简化了模板代码,提升开发效率
- 与React Router、React Query等库良好集成
- 丰富的文档和社区支持,学习资源丰富
- 适合大型团队协作,状态管理规范统一
局限性:
- 学习曲线相对陡峭,概念较多
- 即使使用RTK,模板代码量仍高于其他轻量级方案
- 小型应用可能显得过度设计
- 引入额外的运行时开销和打包体积
- 可能引入额外的性能考量,需要手动优化选择器
Zustand:轻量级状态管理
Zustand是一个极简的状态管理库,专注于简化API并保持高性能。它结合了Redux的可预测性和React hooks的简洁性,提供了更现代化的开发体验。
创建和使用Store
Zustand的核心API非常简洁,使用闭包创建store并通过hook访问状态:
import create from 'zustand';// 创建store
const useStore = create(set => ({// 初始状态count: 0,// 更新状态的操作increment: () => set(state => ({ count: state.count + 1 })),decrement: () => set(state => ({ count: state.count - 1 })),reset: () => set({ count: 0 }),incrementByAmount: (amount) => set(state => ({ count: state.count + amount }))
}));// 组件中使用
function Counter() {// 解构需要的状态和操作const { count, increment, decrement, reset, incrementByAmount } = useStore();// 也可以选择性获取部分状态,优化重渲染// const count = useStore(state => state.count);// const increment = useStore(state => state.increment);return (<div><p>Count: {count}</p><button onClick={increment}>+</button><button onClick={decrement}>-</button><button onClick={reset}>Reset</button><button onClick={() => incrementByAmount(10)}>+10</button></div>);
}
与Redux相比,Zustand省略了action types、reducers和dispatch等概念,直接通过函数更新状态。这种简化的API大大降低了学习成本和代码量。
中间件和持久化
尽管API简单,Zustand仍支持中间件系统,可以扩展store功能:
import create from 'zustand';
import { persist, devtools } from 'zustand/middleware';// 使用中间件
const useStore = create(devtools( // 开发者工具支持persist( // 状态持久化(set, get) => ({count: 0,increment: () => set(state => ({ count: state.count + 1 })),incrementAsync: () => {setTimeout(() => {// 可以使用get获取当前状态set({ count: get().count + 1 });}, 1000);}}),{name: 'counter-storage', // 持久化存储的键名getStorage: () => localStorage, // 使用localStorage}))
);
这种模块化的中间件设计允许灵活组合不同功能,同时保持API简洁。
状态切片和组合
对于大型应用,Zustand支持将状态分割为多个"切片",然后组合使用:
// 用户相关状态
const createUserSlice = (set) => ({user: null,loading: false,error: null,login: async (credentials) => {set({ loading: true });try {const user = await loginApi(credentials);set({ user, loading: false, error: null });} catch (error) {set({ error: error.message, loading: false });}},logout: () => set({ user: null })
});// 购物车相关状态
const createCartSlice = (set, get) => ({items: [],totalItems: 0,addItem: (item) => {set((state) => ({items: [...state.items, item],totalItems: state.totalItems + 1}));},removeItem: (itemId) => {set((state) => ({items: state.items.filter(item => item.id !== itemId),totalItems: state.totalItems - 1}));},clearCart: () => set({ items: [], totalItems: 0 })
});// 组合多个状态切片
const useStore = create((set, get) => ({...createUserSlice(set, get),...createCartSlice(set, get)
}));// 组件中使用
function ShoppingCart() {const { items, addItem, removeItem, user } = useStore();return (<div>{user ? (<><h2>{user.name}的购物车</h2><ul>{items.map(item => (<li key={item.id}>{item.name} - ${item.price}<button onClick={() => removeItem(item.id)}>移除</button></li>))}</ul></>) : (<p>请先登录</p>)}</div>);
}
这种组合方式可以在保持简洁API的同时,实现与Redux类似的代码组织结构。
适用场景:中小型应用,需要全局状态但希望保持简单API的项目,对Redux感到疲惫的团队。典型用例包括交互性应用、单页应用、工具类网站等。
优势:
- 极简API,几乎零配置,减少样板代码
- 基于hook的直观使用方式,无需Provider包裹
- 自动优化渲染,仅在使用的状态变化时更新
- 支持中间件、持久化等扩展功能
- 体积小(约3KB),学习成本低
- 类型支持良好,TypeScript开发体验优秀
- 可以逐步采用,无需重构整个应用
局限性:
- 缺乏时间旅行调试等高级调试功能(虽然有Redux DevTools集成)
- 大型应用可能需要更严格的状态组织方式
- 异步操作处理相对基础,缺少内置的异步状态管理模式
- 不支持状态间复杂依赖关系的自动计算
- 社区规模小于Redux,第三方扩展较少
Recoil:原子化状态管理
Recoil是Facebook推出的状态管理库,设计理念基于"原子状态"和"选择器",提供了细粒度的状态控制和状态派生能力。
原子(Atoms)
原子是Recoil中最小的状态单位,类似于React的useState,但可以在组件间共享:
import { atom, useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil';// 定义原子状态
const countAtom = atom({key: 'countAtom', // 全局唯一的标识符default: 0, // 默认值
});function Counter() {// 类似useState的用法const [count, setCount] = useRecoilState(countAtom);return (<div><p>Count: {count}</p><button onClick={() => setCount(count + 1)}>+</button></div>);
}function DisplayCount() {// 只读状态,不会触发不必要的重渲染const count = useRecoilValue(countAtom);return <div>Current count: {count}</div>;
}function CounterControls() {// 只获取setter函数,不订阅状态变化const setCount = useSetRecoilState(countAtom);return (<div><button onClick={() => setCount(0)}>Reset</button><button onClick={() => setCount(c => c + 5)}>+5</button></div>);
}
原子的关键特性是可以精确控制哪些组件订阅哪些状态,从而优化渲染性能。每个原子变化只会触发使用该原子的组件重新渲染。
选择器(Selectors)
选择器是从原子或其他选择器派生的计算状态,类似于Redux的选择器或Vue的计算属性:
import { atom, selector, useRecoilValue } from 'recoil';// 基础原子
const countAtom = atom({key: 'countAtom',default: 0,
});// 派生状态
const doubleCountSelector = selector({key: 'doubleCount',get: ({get}) => {const count = get(countAtom); // 获取依赖的原子状态return count * 2;},
});// 多atom依赖的复杂选择器
const todoListAtom = atom({key: 'todoList',default: [],
});const todoFilterAtom = atom({key: 'todoFilter',default: 'all', // 'all' | 'completed' | 'uncompleted'
});const filteredTodoListSelector = selector({key: 'filteredTodoList',get: ({get}) => {const filter = get(todoFilterAtom);const list = get(todoListAtom);switch (filter) {case 'completed':return list.filter(item => item.completed);case 'uncompleted':return list.filter(item => !item.completed);default:return list;}},
});function TodoStats() {const count = useRecoilValue(countAtom);const doubleCount = useRecoilValue(doubleCountSelector);const filteredTodos = useRecoilValue(filteredTodoListSelector);return (<div><p>Count: {count}</p><p>Double: {doubleCount}</p><p>Filtered todos: {filteredTodos.length}</p></div>);
}
选择器自动追踪依赖关系,当依赖的原子状态变化时,选择器会重新计算并通知使用该选择器的组件更新。
异步选择器
Recoil支持异步选择器,可以直接处理API请求等异步操作:
const userInfoQuery = selectorFamily({key: 'userInfo',get: (userId) => async () => {const response = await fetch(`https://api.example.com/users/${userId}`);if (!response.ok) throw new Error('Failed to fetch user');return await response.json();},
});function UserProfile({ userId }) {const userInfo = useRecoilValue(userInfoQuery(userId));return (<div><h1>{userInfo.name}</h1><p>Email: {userInfo.email}</p></div>);
}
使用Suspense和ErrorBoundary可以优雅处理加载和错误状态:
function App() {return (<RecoilRoot><ErrorBoundary fallback={<div>Error loading user</div>}><React.Suspense fallback={<div>Loading...</div>}><UserProfile userId="123" /></React.Suspense></ErrorBoundary></RecoilRoot>);
}
适用场景:需要细粒度状态控制与派生状态的复杂应用,特别是有大量相互依赖状态的场景。典型用例包括数据分析应用、复杂表单、实时协作工具等。
优势:
- 原子化状态设计,提供最细粒度的控制
- 强大的派生状态(selector)能力,自动追踪依赖
- 异步状态处理简洁,与React Suspense集成良好
- Facebook官方支持,与React理念一致
- 出色的状态依赖追踪,优化渲染性能
- 基于钩子的简洁API,学习曲线平缓
- 天然支持代码分割,适合大型应用
局限性:
- API相对复杂,概念较多
- 需要RecoilRoot包裹,额外的设置步骤
- 相比其他方案生态系统不够成熟
- 文档和最佳实践相对缺乏
- 持久化等高级功能需要额外实现
- 尚未达到1.0版本,API可能变动
Jotai:轻量级原子状态
Jotai是受Recoil启发的轻量级原子状态管理库,提供了更简化的API和更小的体积。
基本使用
Jotai的基本API比Recoil更简洁:
import { atom, useAtom } from 'jotai';// 创建原子
const countAtom = atom(0);// 派生原子
const doubleAtom = atom(get => get(countAtom) * 2);// 可写的派生原子
const incrementAtom = atom(get => get(countAtom),(get, set, arg) => set(countAtom, get(countAtom) + 1)
);function Counter() {const [count, setCount] = useAtom(countAtom);const [doubleCount] = useAtom(doubleAtom);const [, increment] = useAtom(incrementAtom); // 只使用写入器return (<div><p>Count: {count}</p><p>Double: {doubleCount}</p><button onClick={() => setCount(c => c + 1)}>+</button><button onClick={increment}>Increment</button></div>);
}
Jotai的原子可以依赖其他原子,创建复杂的状态依赖图,同时保持API简洁。
异步原子
Jotai支持异步原子,可以直接处理Promise:
const userAtom = atom(async () => {const response = await fetch('https://api.example.com/user');return response.json();
});// 或者使用已有原子创建异步派生
const userIdAtom = atom('123');
const userByIdAtom = atom(async get => {const id = get(userIdAtom);const response = await fetch(`https://api.example.com/users/${id}`);return response.json();
});function UserProfile() {const [user] = useAtom(userByIdAtom);return (<div>{user.loading ? (<p>Loading...</p>) : (<><h1>{user.name}</h1><p>Email: {user.email}</p></>)}</div>);
}
与Recoil类似,Jotai也可以与React Suspense结合使用。
适用场景:需要Recoil类似功能但希望更轻量的应用,对状态颗粒度有高要求的项目。典型用例包括交互性强的UI组件、需要细粒度状态控制的小型应用等。
优势:
- 极简API,核心概念少
- 体积小(约2.5KB),性能优秀
- 无需Provider包裹(可选)
- 良好的TypeScript支持
- 原子化设计,优化渲染性能
- 与React Suspense兼容
- 支持与Immer集成,简化不可变更新
局限性:
- 相对较新,生态不够成熟
- 大型应用状态组织能力有限
- 文档和示例相对简单
- 缺乏专门的调试工具
- 社区支持不如Redux和Recoil
实际项目选型决策框架
项目规模与复杂度评估
选择状态管理方案首先需要考虑项目规模和复杂度,不同规模的项目有不同的最佳实践:
小型应用(组件数<20):
-
推荐:React内置状态管理(useState + useContext)
- 理由:无需额外依赖,学习成本低,对于简单应用足够用
- 实施方案:创建几个针对性的Context,避免全局Context过大导致性能问题
-
替代:Zustand(需要更好的开发体验)
- 理由:API简单,几乎零配置,可以逐步采用
- 实施方案:创建单一store,根据功能模块组织状态
// 小型应用使用Context的最佳实践
// 按照功能拆分多个Context,而不是使用单一全局Context
const AuthContext = React.createContext(null);
const ThemeContext = React.createContext(null);
const FeatureFlagsContext = React.createContext(null);function App() {const [user, setUser] = useState(null);const [theme, setTheme] = useState('light');const [features, setFeatures] = useState({newDashboard: false,betaFeatures: false});// 登录逻辑const login = async (credentials) => {// 实现登录逻辑const user = await loginApi(credentials);setUser(user);};const logout = () => setUser(null);return (<AuthContext.Provider value={{ user, login, logout }}><ThemeContext.Provider value={{ theme, setTheme }}><FeatureFlagsContext.Provider value={{ features, setFeatures }}><MainApp /></FeatureFlagsContext.Provider></ThemeContext.Provider></AuthContext.Provider>);
}
中型应用(组件数20-100):
-
推荐:Zustand或Jotai
- 理由:平衡了简洁性和功能性,学习成本适中
- 实施方案:按功能域划分状态切片,避免单一巨大store
-
替代:Redux Toolkit(预计未来会扩展)
- 理由:为未来应用增长提供扩展性,规范化的状态管理
- 实施方案:使用RTK,按照领域模型组织状态,避免过度设计
// 中型应用使用Zustand的状态组织最佳实践
// 1. 按功能划分状态切片
const createAuthSlice = (set, get) => ({user: null,loading: false,error: null,login: async (credentials) => {set({ loading: true, error: null });try {const response = await fetch('/api/login', {method: 'POST',body: JSON.stringify(credentials),headers: { 'Content-Type': 'application/json' }});if (!response.ok) throw new Error('Login failed');const user = await response.json();set({ user, loading: false });return user;} catch (error) {set({ error: error.message, loading: false });throw error;}},logout: () => set({ user: null })
});const createProductsSlice = (set, get) => ({products: [],loading: false,error: null,fetchProducts: async (category) => {set({ loading: true, error: null });try {const response = await fetch(`/api/products?category=${category}`);if (!response.ok) throw new Error('Failed to fetch products');const products = await response.json();set({ products, loading: false });} catch (error) {set({ error: error.message, loading: false });}}
});// 2. 组合所有切片
import create from 'zustand';
import { persist } from 'zustand/middleware';const useStore = create(persist((set, get) => ({...createAuthSlice(set, get),...createProductsSlice(set, get)}),{ name: 'app-store' } // 持久化到localStorage)
);// 3. 在组件中使用,只订阅需要的部分状态
function ProductList({ category }) {// 只订阅products相关状态,auth状态变化不会导致此组件重渲染const { products, loading, error, fetchProducts } = useStore(state => ({products: state.products,loading: state.loading,error: state.error,fetchProducts: state.fetchProducts}));useEffect(() => {fetchProducts(category);}, [category, fetchProducts]);if (loading) return <div>Loading products...</div>;if (error) return <div>Error: {error}</div>;return (<div className="product-list">{products.map(product => (<ProductCard key={product.id} product={product} />))}</div>);
}
大型应用(组件数>100):
-
推荐:Redux Toolkit
- 理由:成熟的生态系统,严格的状态更新规则,利于团队协作
- 实施方案:按领域模型设计状态结构,规范化状态管理流程
-
替代:Recoil(特别是对原子化状态有需求时)
- 理由:细粒度状态控制,适合复杂UI和频繁局部更新
- 实施方案:按功能设计原子族,利用选择器优化派生状态
// 大型应用使用Redux Toolkit的组织方式
// 1. 按领域模型划分状态切片
// auth/authSlice.js
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';export const loginUser = createAsyncThunk('auth/login',async (credentials, { rejectWithValue }) => {try {const response = await fetch('/api/login', {method: 'POST',body: JSON.stringify(credentials),headers: { 'Content-Type': 'application/json' }});if (!response.ok) throw new Error('Login failed');return await response.json();} catch (error) {return rejectWithValue(error.message);}}
);const authSlice = createSlice({name: 'auth',initialState: {user: null,status: 'idle', // 'idle' | 'loading' | 'succeeded' | 'failed'error: null},reducers: {logout: (state) => {state.user = null;state.status = 'idle';}},extraReducers: (builder) => {builder.addCase(loginUser.pending, (state) => {state.status = 'loading';}).addCase(loginUser.fulfilled, (state, action) => {state.status = 'succeeded';state.user = action.payload;state.error = null;}).addCase(loginUser.rejected, (state, action) => {state.status = 'failed';state.error = action.payload;});}
});export const { logout } = authSlice.actions;
export default authSlice.reducer;// products/productsSlice.js, orders/ordersSlice.js 等按类似方式实现// 2. 组合所有slice到store
// store.js
import { configureStore } from '@reduxjs/toolkit';
import authReducer from './auth/authSlice';
import productsReducer from './products/productsSlice';
import ordersReducer from './orders/ordersSlice';export const store = configureStore({reducer: {auth: authReducer,products: productsReducer,orders: ordersReducer}
});// 3. 提供选择器函数优化组件重渲染
// auth/selectors.js
export const selectUser = state => state.auth.user;
export const selectAuthStatus = state => state.auth.status;
export const selectAuthError = state => state.auth.error;// 4. 组件中使用
import { useSelector, useDispatch } from 'react-redux';
import { loginUser } from './auth/authSlice';
import { selectUser, selectAuthStatus, selectAuthError } from './auth/selectors';function LoginPage() {const dispatch = useDispatch();const user = useSelector(selectUser);const status = useSelector(selectAuthStatus);const error = useSelector(selectAuthError);const handleSubmit = async (event) => {event.preventDefault();const credentials = {email: event.target.email.value,password: event.target.password.value};await dispatch(loginUser(credentials));};// 渲染逻辑...
}
团队因素考量
技术选型不仅取决于技术本身,还与团队组成和技术背景密切相关:
React新手团队:
- 推荐:内置状态管理 → Zustand
- 理由:学习曲线平缓,API直观,减少认知负担
- 过渡策略:先掌握React基础,再逐步引入状态管理
- 避免:直接上手复杂的Redux或Recoil
有Redux经验团队:
- 推荐:继续使用Redux Toolkit
- 理由:团队已有经验,迁移成本低,RTK提供更现代的开发体验
- 现代化策略:从传统Redux迁移到RTK,利用新API简化代码
- 考虑:在新模块尝试Zustand等轻量方案,对比开发体验
技术前沿团队:
- 推荐:尝试Jotai或Recoil等新兴方案
- 理由:探索更现代的状态管理范式,提升开发体验
- 策略:核心模块使用成熟方案,新功能尝试新技术
- 平衡:创新与稳定性,避免过度追求新技术
开发体验与工具链集成
状态管理的选择还应考虑与现有工具链的集成体验:
TypeScript支持:
- Redux Toolkit、Zustand和Jotai对TypeScript支持良好
- Recoil的类型推导在复杂场景可能需要额外类型标注
开发者工具:
- Redux DevTools支持最完善,提供时间旅行调试、action记录等
- Zustand和Jotai可以集成Redux DevTools,但功能相对有限
- Recoil有专门的开发者工具,但功能不如Redux DevTools成熟
热重载支持:
- 所有方案都支持React的热重载
- Redux需要额外配置保持热重载时的状态
- Zustand和Jotai通常无需额外配置
案例分析:电商平台状态管理
以典型电商平台为例,不同模块可能需要不同的状态管理方案,体现了实际项目中的混合策略:
用户认证模块:
- 选择:Redux Toolkit
- 原因:
- 需要在整个应用中共享用户状态
- 有复杂的中间件处理(JWT刷新、权限检查)
- 状态变化需要追踪审计
- 与路由守卫等功能集成
// Redux实现用户认证
const authSlice = createSlice({name: 'auth',initialState: {user: null,token: localStorage.getItem('token'),refreshToken: localStorage.getItem('refreshToken'),isLoading: false,error: null,permissions: []},reducers: {loginStart: state => {state.isLoading = true;},loginSuccess: (state, action) => {state.isLoading = false;state.user = action.payload.user;state.token = action.payload.token;state.refreshToken = action.payload.refreshToken;state.permissions = action.payload.permissions || [];state.error = null;// 保存到localStoragelocalStorage.setItem('token', action.payload.token);localStorage.setItem('refreshToken', action.payload.refreshToken);},loginFailure: (state, action) => {state.isLoading = false;state.error = action.payload;},logout: state => {state.user = null;state.token = null;state.refreshToken = null;state.permissions = [];// 清除localStoragelocalStorage.removeItem('token');localStorage.removeItem('refreshToken');},// 更新token(用于刷新token流程)updateToken: (state, action) => {state.token = action.payload;localStorage.setItem('token', action.payload);}}
});// Token刷新中间件
const refreshTokenMiddleware = store => next => async action => {const result = next(action);// 当接收到401错误时,尝试刷新tokenif (action.type === 'api/requestFailed' && action.payload?.status === 401 &&store.getState().auth.refreshToken) {try {const response = await fetch('/api/refresh-token', {method: 'POST',headers: { 'Content-Type': 'application/json' },body: JSON.stringify({ refreshToken: store.getState().auth.refreshToken })});if (response.ok) {const data = await response.json();// 更新tokenstore.dispatch(updateToken(data.token));// 重试失败的请求store.dispatch(action.meta.originalRequest);} else {// 刷新失败,登出用户store.dispatch(logout());}} catch (error) {store.dispatch(logout());}}return result;
};// 在configureStore中添加中间件
const store = configureStore({reducer: {auth: authSlice.reducer,// 其他reducer...},middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(refreshTokenMiddleware)
});
购物车模块:
- 选择:Zustand + 持久化
- 原因:
- 需要频繁更新且有持久化需求
- 逻辑相对独立,与其他状态耦合较少
- 需要高性能更新,避免整个应用重渲染
- 用户体验要求购物车在刷新页面后保持状态
// Zustand实现购物车
import create from 'zustand';
import { persist } from 'zustand/middleware';const useCartStore = create(persist((set, get) => ({items: [],totalItems: 0,totalPrice: 0,// 添加商品到购物车addItem: (product) => set(state => {const existingItem = state.items.find(item => item.id === product.id);if (existingItem) {const updatedItems = state.items.map(item => item.id === product.id ? {...item, quantity: item.quantity + 1} : item);return {items: updatedItems,totalItems: state.totalItems + 1,totalPrice: state.totalPrice + product.price};}return {items: [...state.items, {...product, quantity: 1}],totalItems: state.totalItems + 1,totalPrice: state.totalPrice + product.price};}),// 从购物车移除商品removeItem: (productId) => set(state => {const itemToRemove = state.items.find(item => item.id === productId);if (!itemToRemove) return state;return {items: state.items.filter(item => item.id !== productId),totalItems: state.totalItems - itemToRemove.quantity,totalPrice: state.totalPrice - (itemToRemove.price * itemToRemove.quantity)};}),// 更新商品数量updateQuantity: (productId, quantity) => set(state => {const item = state.items.find(item => item.id === productId);if (!item) return state;const quantityDiff = quantity - item.quantity;const priceDiff = item.price * quantityDiff;return {items: state.items.map(item =>item.id === productId? { ...item, quantity }: item),totalItems: state.totalItems + quantityDiff,totalPrice: state.totalPrice + priceDiff};}),// 清空购物车clearCart: () => set({items: [],totalItems: 0,totalPrice: 0}),// 应用优惠券applyDiscount: (discountPercent) => set(state => ({totalPrice: state.totalPrice * (1 - discountPercent / 100)}))}),{name: 'cart-storage', // localStorage的键名getStorage: () => localStorage, // 使用localStoragepartialize: state => ({ // 只持久化这些字段,忽略计算字段items: state.items,totalItems: state.totalItems,totalPrice: state.totalPrice})})
);// 使用购物车状态
function CartSummary() {const { totalItems, totalPrice } = useCartStore();return (<div className="cart-summary"><span>购物车: {totalItems} 件商品</span><span>总价: ¥{totalPrice.toFixed(2)}</span></div>);
}function CartDetails() {const { items, removeItem, updateQuantity, clearCart } = useCartStore();return (<div className="cart-details"><h2>购物车</h2>{items.length === 0 ? (<p>购物车为空</p>) : (<><ul>{items.map(item => (<li key={item.id}><img src={item.image} alt={item.name} /><div><h3>{item.name}</h3><p>¥{item.price.toFixed(2)} × {item.quantity}</p><div className="quantity-controls"><button onClick={() => updateQuantity(item.id, Math.max(1, item.quantity - 1))}>-</button><span>{item.quantity}</span><button onClick={() => updateQuantity(item.id, item.quantity + 1)}>+</button></div></div><button onClick={() => removeItem(item.id)}>删除</button></li>))}</ul><div className="cart-actions"><button onClick={clearCart}>清空购物车</button><button>结算</button></div></>)}</div>);
}
产品详情页:
- 选择:React Query + 局部useState
- 原因:
- 主要是服务器状态(产品数据)
- 局部UI状态相对简单(选中的变体、数量等)
- 需要缓存和预取数据,减少重复请求
- 自动处理加载和错误状态
中型应用(组件数20-100):
-
推荐:Zustand或Jotai
- 理由:平衡了简洁性和功能性,学习成本适中
- 实施方案:按功能域划分状态切片,避免单一巨大store
-
替代:Redux Toolkit(预计未来会扩展)
- 理由:为未来应用增长提供扩展性,规范化的状态管理
- 实施方案:使用RTK,按照领域模型组织状态,避免过度设计
// 1. 按功能划分状态切片
const createAuthSlice = (set, get) => ({user: null,loading: false,error: null,login: async (credentials) => {set({ loading: true, error: null });try {const response = await fetch('/api/login', {method: 'POST',body: JSON.stringify(credentials),headers: { 'Content-Type': 'application/json' }});if (!response.ok) throw new Error('Login failed');const user = await response.json();set({ user, loading: false });return user;} catch (error) {set({ error: error.message, loading: false });throw error;}},logout: () => set({ user: null })
});const createProductsSlice = (set, get) => ({products: [],loading: false,error: null,fetchProducts: async (category) => {set({ loading: true, error: null });try {const response = await fetch(`/api/products?category=${category}`);if (!response.ok) throw new Error('Failed to fetch products');const products = await response.json();set({ products, loading: false });} catch (error) {set({ error: error.message, loading: false });}}
});// 2. 组合所有切片
import create from 'zustand';
import { persist } from 'zustand/middleware';const useStore = create(persist((set, get) => ({...createAuthSlice(set, get),...createProductsSlice(set, get)}),{ name: 'app-store' } // 持久化到localStorage)
);
大型应用(组件数>100):
-
推荐:Redux Toolkit
- 理由:成熟的生态系统,严格的状态更新规则,利于团队协作
- 实施方案:按领域模型设计状态结构,规范化状态管理流程
-
替代:Recoil(特别是对原子化状态有需求时)
- 理由:细粒度状态控制,适合复杂UI和频繁局部更新
- 实施方案:按功能设计原子族,利用选择器优化派生状态
// 大型应用使用Redux Toolkit的组织方式
// auth/authSlice.js
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';export const loginUser = createAsyncThunk('auth/login',async (credentials, { rejectWithValue }) => {try {const response = await fetch('/api/login', {method: 'POST',body: JSON.stringify(credentials),headers: { 'Content-Type': 'application/json' }});if (!response.ok) throw new Error('Login failed');return await response.json();} catch (error) {return rejectWithValue(error.message);}}
);const authSlice = createSlice({name: 'auth',initialState: {user: null,status: 'idle', // 'idle' | 'loading' | 'succeeded' | 'failed'error: null},reducers: {logout: (state) => {state.user = null;state.status = 'idle';}},extraReducers: (builder) => {builder.addCase(loginUser.pending, (state) => {state.status = 'loading';}).addCase(loginUser.fulfilled, (state, action) => {state.status = 'succeeded';state.user = action.payload;state.error = null;}).addCase(loginUser.rejected, (state, action) => {state.status = 'failed';state.error = action.payload;});}
});
团队因素考量
技术选型不仅取决于技术本身,还与团队组成和技术背景密切相关:
React新手团队:
- 推荐:内置状态管理 → Zustand
- 理由:学习曲线平缓,API直观,减少认知负担
- 过渡策略:先掌握React基础,再逐步引入状态管理
- 避免:直接上手复杂的Redux或Recoil
有Redux经验团队:
- 推荐:继续使用Redux Toolkit
- 理由:团队已有经验,迁移成本低,RTK提供更现代的开发体验
- 现代化策略:从传统Redux迁移到RTK,利用新API简化代码
- 考虑:在新模块尝试Zustand等轻量方案,对比开发体验
技术前沿团队:
- 推荐:尝试Jotai或Recoil等新兴方案
- 理由:探索更现代的状态管理范式,提升开发体验
- 策略:核心模块使用成熟方案,新功能尝试新技术
- 平衡:创新与稳定性,避免过度追求新技术
案例分析:电商平台状态管理
以典型电商平台为例,不同模块可能需要不同的状态管理方案,体现了实际项目中的混合策略:
用户认证模块:
- 选择:Redux Toolkit
- 原因:
- 需要在整个应用中共享用户状态
- 有复杂的中间件处理(JWT刷新、权限检查)
- 状态变化需要追踪审计
- 与路由守卫等功能集成
// Redux实现用户认证
const authSlice = createSlice({name: 'auth',initialState: {user: null,token: localStorage.getItem('token'),refreshToken: localStorage.getItem('refreshToken'),isLoading: false,error: null,permissions: []},reducers: {loginStart: state => {state.isLoading = true;},loginSuccess: (state, action) => {state.isLoading = false;state.user = action.payload.user;state.token = action.payload.token;state.refreshToken = action.payload.refreshToken;state.permissions = action.payload.permissions || [];state.error = null;// 保存到localStoragelocalStorage.setItem('token', action.payload.token);localStorage.setItem('refreshToken', action.payload.refreshToken);},loginFailure: (state, action) => {state.isLoading = false;state.error = action.payload;},logout: state => {state.user = null;state.token = null;state.refreshToken = null;state.permissions = [];// 清除localStoragelocalStorage.removeItem('token');localStorage.removeItem('refreshToken');}}
});
购物车模块:
- 选择:Zustand + 持久化
- 原因:
- 需要频繁更新且有持久化需求
- 逻辑相对独立,与其他状态耦合较少
- 需要高性能更新,避免整个应用重渲染
- 用户体验要求购物车在刷新页面后保持状态
// Zustand实现购物车
import create from 'zustand';
import { persist } from 'zustand/middleware';const useCartStore = create(persist((set, get) => ({items: [],totalItems: 0,totalPrice: 0,// 添加商品到购物车addItem: (product) => set(state => {const existingItem = state.items.find(item => item.id === product.id);if (existingItem) {const updatedItems = state.items.map(item => item.id === product.id ? {...item, quantity: item.quantity + 1} : item);return {items: updatedItems,totalItems: state.totalItems + 1,totalPrice: state.totalPrice + product.price};}return {items: [...state.items, {...product, quantity: 1}],totalItems: state.totalItems + 1,totalPrice: state.totalPrice + product.price};}),// 从购物车移除商品removeItem: (productId) => set(state => {const itemToRemove = state.items.find(item => item.id === productId);if (!itemToRemove) return state;return {items: state.items.filter(item => item.id !== productId),totalItems: state.totalItems - itemToRemove.quantity,totalPrice: state.totalPrice - (itemToRemove.price * itemToRemove.quantity)};})}),{name: 'cart-storage', // localStorage的键名getStorage: () => localStorage // 使用localStorage})
);
产品详情页:
- 选择:React Query + 局部useState
- 原因:
- 主要是服务器状态(产品数据)
- 局部UI状态相对简单(选中的变体、数量等)
- 需要缓存和预取数据,减少重复请求
- 自动处理加载和错误状态
import { useQuery } from 'react-query';
import { useState } from 'react';function ProductDetail({ productId }) {// 服务器状态管理 - React Queryconst { data: product, isLoading, error } = useQuery(['product', productId],() => fetchProductById(productId),{staleTime: 5 * 60 * 1000, // 5分钟内数据不过期cacheTime: 60 * 60 * 1000 // 缓存1小时});// 局部UI状态 - React useStateconst [selectedVariant, setSelectedVariant] = useState(null);const [quantity, setQuantity] = useState(1);// 购物车状态(来自Zustand)const addItem = useCartStore(state => state.addItem);useEffect(() => {// 产品加载完成后,默认选择第一个变体if (product?.variants?.length > 0) {setSelectedVariant(product.variants[0]);}}, [product]);if (isLoading) return <LoadingSpinner />;if (error) return <ErrorMessage error={error} />;if (!product) return <NotFoundMessage />;const handleAddToCart = () => {if (!selectedVariant) return;addItem({id: `${product.id}-${selectedVariant.id}`,productId: product.id,variantId: selectedVariant.id,name: product.name,variantName: selectedVariant.name,price: selectedVariant.price,image: product.images[0]});// 显示添加成功提示toast.success('已添加到购物车');};return (<div className="product-detail"><div className="product-images"><ProductImageGallery images={product.images} /></div><div className="product-info"><h1>{product.name}</h1><div className="product-price">{selectedVariant ? (<span>¥{selectedVariant.price.toFixed(2)}</span>) : (<span>¥{product.price.toFixed(2)}</span>)}</div>{product.variants.length > 0 && (<div className="variant-selector"><h3>选择规格</h3><div className="variant-options">{product.variants.map(variant => (<buttonkey={variant.id}className={selectedVariant?.id === variant.id ? 'selected' : ''}onClick={() => setSelectedVariant(variant)}>{variant.name}</button>))}</div></div>)}<div className="quantity-selector"><h3>数量</h3><div className="quantity-control"><button onClick={() => setQuantity(Math.max(1, quantity - 1))}disabled={quantity <= 1}>-</button><span>{quantity}</span><button onClick={() => setQuantity(quantity + 1)}>+</button></div></div><button className="add-to-cart-button"onClick={handleAddToCart}disabled={!selectedVariant}>加入购物车</button></div><div className="product-description"><h2>商品详情</h2><div dangerouslySetInnerHTML={{ __html: product.description }} /></div></div>);
}
商品列表和筛选:
- 选择:Jotai
- 原因:
- 需要细粒度的状态更新(价格范围、分类筛选等)
- 页面组件较多,需要避免不必要的重渲染
- 筛选条件需要在多个组件间共享
- URL参数与状态需要同步
import { atom, useAtom } from 'jotai';
import { useUpdateEffect } from 'react-use';
import { useNavigate, useLocation } from 'react-router-dom';// 定义原子状态
const categoryAtom = atom('all');
const priceRangeAtom = atom({ min: 0, max: 10000 });
const sortByAtom = atom('popularity'); // 'popularity', 'price-low', 'price-high', 'newest'
const pageAtom = atom(1);
const pageSizeAtom = atom(20);// 派生状态 - URL查询参数
const queryParamsAtom = atom(get => {const category = get(categoryAtom);const { min, max } = get(priceRangeAtom);const sortBy = get(sortByAtom);const page = get(pageAtom);const pageSize = get(pageSizeAtom);const params = new URLSearchParams();if (category !== 'all') params.set('category', category);if (min > 0) params.set('min_price', min.toString());if (max < 10000) params.set('max_price', max.toString());if (sortBy !== 'popularity') params.set('sort', sortBy);if (page > 1) params.set('page', page.toString());if (pageSize !== 20) params.set('page_size', pageSize.toString());return params.toString();}
);function ProductListPage() {// Jotai状态const [category, setCategory] = useAtom(categoryAtom);const [priceRange, setPriceRange] = useAtom(priceRangeAtom);const [sortBy, setSortBy] = useAtom(sortByAtom);const [page, setPage] = useAtom(pageAtom);const [queryParams] = useAtom(queryParamsAtom);// 路由和URL参数const navigate = useNavigate();const location = useLocation();// 从URL同步状态useEffect(() => {const params = new URLSearchParams(location.search);const categoryParam = params.get('category');if (categoryParam) setCategory(categoryParam);const minPrice = params.get('min_price');const maxPrice = params.get('max_price');if (minPrice || maxPrice) {setPriceRange({min: minPrice ? parseInt(minPrice) : 0,max: maxPrice ? parseInt(maxPrice) : 10000});}const sortParam = params.get('sort');if (sortParam) setSortBy(sortParam);const pageParam = params.get('page');if (pageParam) setPage(parseInt(pageParam));}, []);// 同步状态到URLuseUpdateEffect(() => {navigate({ search: queryParams }, { replace: true });}, [queryParams, navigate]);// 使用React Query获取产品数据const { data, isLoading, error } = useQuery(['products', queryParams],() => fetchProducts(queryParams),{ keepPreviousData: true });return (<div className="product-list-page"><div className="filters-sidebar"><CategoryFilterselectedCategory={category}onChange={setCategory}/><PriceRangeFiltervalue={priceRange}onChange={setPriceRange}/><SortOptionsvalue={sortBy}onChange={setSortBy}/></div><div className="product-grid">{isLoading ? (<LoadingSpinner />) : error ? (<ErrorMessage error={error} />) : (<>{data.products.map(product => (<ProductCard key={product.id} product={product} />))}<PaginationcurrentPage={page}totalPages={data.totalPages}onPageChange={setPage}/></>)}</div></div>);
}// 过滤器组件
function CategoryFilter({ selectedCategory, onChange }) {const { data: categories } = useQuery('categories',fetchCategories);if (!categories) return null;return (<div className="filter-section"><h3>商品分类</h3><ul><li><label><inputtype="radio"checked={selectedCategory === 'all'}onChange={() => onChange('all')}/>全部商品</label></li>{categories.map(category => (<li key={category.id}><label><inputtype="radio"checked={selectedCategory === category.id}onChange={() => onChange(category.id)}/>{category.name}</label></li>))}</ul></div>);
}
总结与决策
选择合适的状态管理方案没有放之四海而皆准的答案,需要根据项目规模、团队经验、性能需求和未来可维护性综合考量。基于前文的分析,我们可以提炼出以下核心决策原则:
-
渐进式采用:从简单方案开始,随需求复杂度增加逐步引入强大工具。对于新项目,优先考虑内置状态管理,当确实需要全局状态时再引入专门的库。
-
混合策略:针对不同类型状态选择最适合的管理方式:
- 服务器状态:优先考虑React Query/SWR
- 全局UI状态:Redux/Zustand/Recoil
- 局部UI状态:useState/useReducer
- 表单状态:专用表单库
-
团队一致性:避免在一个项目中使用过多不同的状态管理库,造成代码风格不一致和维护困难。选择符合团队技术栈和经验水平的方案。
-
关注开发体验:选择有良好开发者工具支持的方案,能显著提高调试效率和代码质量。Redux DevTools和React DevTools是不可或缺的工具。
-
权衡取舍:在代码复杂度、性能、学习成本之间找到平衡点,不要盲目追求最新技术。每个方案都有其优缺点,没有完美解决方案。
-
考虑长期维护:评估库的社区活跃度、更新频率和长期支持情况,避免选择可能被废弃的技术。
-
关注性能影响:状态管理方案会直接影响应用性能,尤其是组件重渲染,应进行充分测试评估。
最终,我们会发现,最好的状态管理方案是能够让团队高效工作、保持代码可维护性、提供良好用户体验的方案。技术选型应该服务于业务目标,而非成为目标本身。
参考资源
官方文档
- React 官方文档 - 状态管理
- Redux Toolkit 文档
- Zustand GitHub
- Recoil 官方文档
- Jotai 官方文档
- React Query 文档
深入学习资料
- 状态管理方案对比分析
- 性能优化最佳实践
工具与扩展
- Redux DevTools Extension
- React Developer Tools
- Immer.js - 简化不可变状态更新
- Reselect - 选择器库,优化状态派生
社区资源
- React Status 周刊
- Redux GitHub 讨论区
- React Reddit 社区
- React 状态管理主题演讲
如果你觉得这篇文章有帮助,欢迎点赞收藏,也期待在评论区看到你的想法和建议!👇
终身学习,共同成长。
咱们下一期见
💻
相关文章:
React进阶:状态管理选择题
React进阶:状态管理选择题 引言 随着React应用复杂度增加,选择合适的状态管理方案成为我们面临的关键决策。 状态管理本质上是解决"谁来存储数据"以及"如何更新和分发这些数据"的问题。在React生态中,随着应用规模扩大…...

h5的aliplayer-min.js 加密视频会走到debugger
h5的aliplayer-min.js 如果 https://g.alicdn.com/apsara-media-box/imp-web-player/2.19.0/aliplayer-min.js走加密视频的话会有debugger 更换aliplayer-min.js版本解决了 https://g.alicdn.com/apsara-media-box/imp-web-player/2.25.1/aliplayer-min.js 对应css:…...
第5篇《中间件负载均衡与连接池管理机制设计》
5.1 章节导读 在数据库中间件中,如何高效地管理数据库连接与请求调度至关重要。 本节围绕两个核心模块展开: 连接池管理:提升连接复用能力,避免频繁建立/断开连接。 负载均衡策略:合理调度 SQL 请求,提升…...

DashBoard安装使用
DashBoard安装使用 一、实验目的 1、掌握dashboard 的安装部署 2、熟悉图像化部署任务:产生pod---定义服务--验证访问 二、实验内容: 1、配置步骤 1.1、Helm安装 离线安装(适用于内网/离线环境) # 根据系统架构选择版本&am…...

极客大挑战 2019 EasySQL 1(万能账号密码,SQL注入,HackBar)
题目 做法 启动靶机,打开给出的网址 随便输点东西进去,测试一下 输入1、1’、1"判断SQL语句闭合方式 输入以上两个都是以下结果 但是,输入1’时,出现的是另外结果 输入1,1"时,SQL语句没有…...
C# CallerMemberName特性
当你在一个方法运用了CallerMemberName特性,编译器会自动将调用该方法的方法或属性的名称作为该参数的默认值,可应用于MVVM框架。 代码: using System.ComponentModel; using System.Runtime.CompilerServices;public class Person : INoti…...

采用 Docker GPU 部署的 Ubuntu 或者 windows 桌面环境
# 国内下载不了 docker pull gezp/ubuntu-desktop:24.04-cu12.6.2# 阿里云镜像 docker pull registry.cn-hongkong.aliyuncs.com/gezp/ubuntu-desktop:24.04-cu12.6.2# create container with nomachine docker run -d --restarton-failure --name myubuntu --shm-size1024m -e…...

关于面试找工作的总结(四)
不同情况下收到offer后的处理方法 1.不会去的,只是面试练手2.还有疑问,考虑中3.offer/职位不满足期望的4.已确认,但又收到更好的5.还想挽回之前的offer6.确认,准备入职7.还想拖一下的1.不会去的,只是面试练手 HR您好,非常荣幸收到贵司的offer,非常感谢一直以来您的帮助,…...

分布式拜占庭容错算法——实现工作量证明(PoW)算法详解
Java 实现工作量证明(PoW)算法详解 一、PoW 核心原理 #mermaid-svg-AAj0Pvst1PVcVy5v {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-AAj0Pvst1PVcVy5v .error-icon{fill:#552222;}#mermaid…...

深度解析Mysql中MVCC的工作机制
MVCC,多版本并发控制 定义:维护一个数据的多个版本,使读写操作没有冲突,依赖于:隐藏字段,undo log日志,readView MVCC会为每条版本记录保存三个隐藏字段 DB_TRX_ID: 记录最近插入或修改该记录的事务IDDB_R…...

MP4文件声音与视频分离
最近学习PR剪辑 要添加视频文件和音频文件 但是直接给MP4文件 得到的是一个整体 不管怎么切分 都是无法得到单独的整体 这就需要将视频文件和音频文件分离 我推荐使用ffmpeg工具进行分离 迅雷链接:https://pan.xunlei.com/s/VORu5x64jjL-gXFd_VTpYjRPA1?pwd8wec#…...

接口自动化测试之pytest 运行方式及前置后置封装
🍅 点击文末小卡片,免费获取软件测试全套资料,资料在手,涨薪更快 一、Pytest 优点认知 1.可以结合所有的自动化测试工具 2.跳过失败用例以及失败重跑 3.结合allure生产美观报告 4.和Jenkins持续集成 5.很多强大的插件 pytest-htm…...
服务器被攻击了怎么办
可以上一个高防IP或者AI云防护都是可以的。(有效防御CC、APl接口、http、tcp、WEB应用扫描/爬虫、SYN、WAF、DDOS、UDP、入侵、渗透、SQL注入、XSS跨站脚本攻击、远程恶意代码执行、session fixation、Webshell攻击、恶意请求,恶意扫描、暴力破解、CSRF等…...

06-排序
排序 1. 排序的概念及其应用 1.1 排序的概念 排序:所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。 稳定性:假定在待排序的记录序列中,存在多个具有相同的关键…...
python,shell,linux,bash概念的不同和对比联系
一、基本概念理解 1. Linux 是一个 操作系统内核,常与 GNU 工具集成组成完整的 Linux 操作系统。 提供对硬件的管理能力与系统调用接口。 用户通过 Shell 或 GUI 与 Linux 交互。 2. Shell 是用户与 Linux 内核之间的 命令行解释器(CLI)…...

FPGA管脚类型,及选择
fpga的IO Type选择,如下: 具体的定义:...

如何在 Ubuntu22.04 上安装并开始使用 RabbitMQ
单体架构学的差不多了,可以朝着微服务进军了,笔者打算实操一下 RabbitMQ(这个和 Redis 一样重要的组件) 笔者这里采用的是本地 wsl2 的 Ubuntu22.04 先按指定的博客进行前置操作 Ubuntu22.04 安装 RabbitMQ 解决 ARM Ubuntu 22.04 缺少 libs…...

R-CNN 模型算法流程梳理
目录 一、R-CNN整体流程 二、需要注意的地方 论文连接:[1311.2524] Rich feature hierarchies for accurate object detection and semantic segmentation 如果你之前了解过RNN,很容易混淆认为R-CNN也具有RNN的时序循环功能,这种理解是错误…...
细说C语言将格式化输出到FILE *stream流的函数fprintf、_fprintf_I、fwprintf、_fwprintf_I
目录 1、将格式化数据输出到FILE *stream流基本型 (1)语法 (2)参数 (3)示例 2、将格式化数据输出到FILE *stream流并启用并启用在格式字符串中使用参数的顺序的规范 (1)语法 …...

本地日记本,用于记录日常。
文章目录 想法程序说明展望 想法 本人想要复盘以前的事情,所以就想着写一个小程序,记录一下一天发生了什么事情。以后如果忘记了可以随时查看。写日记的想法来自我看的一本书,里面有一段话说的意思是,经验从来都不是随着年龄增长…...

[蓝桥杯]格子刷油漆
格子刷油漆 题目描述 X 国的一段古城墙的顶端可以看成 2N2N 个格子组成的矩形(如下图所示),现需要把这些格子刷上保护漆。 你可以从任意一个格子刷起,刷完一格,可以移动到和它相邻的格子(对角相邻也算数&…...
Monorepo架构: 项目管理工具介绍、需求分析与技术选型
概述 如何实现 monorepo,以及在项目中如何管理多个包,在进行具体项目开发前,有必要强调一个重要思维 — 全局观 即看待技术方案时,要从需求角度出发,综合考量该方案能否长远满足项目或团队需求 为什么要有全局观呢&a…...

ubuntu下libguestfs-tools
在ubuntu下,使用libguestfs-tools工具挂载其他磁盘和分区。 首先安装libguestfs-tools将vmx虚拟磁盘共享:sudo vmhgfs-fuse .host:/ /mnt/hgfs -o allow_other执行如下命令查看分区名称:virt-filesystems -a /mnt/hgfs/D/vmware/FGT_VM64-v7…...

Authentication failed(切换了新的远程仓库tld)
启用 Git Credential Manager git config --global credential.helper manager 强制弹出凭据输入窗口 git config --global credential.helper.modalprompt true 指定 TFS 服务器使用基础认证(Basic Auth) git config --global credential.https://…...

【Web应用】若依框架:基础篇14 源码阅读-后端代码分析-课程管理模块前后端代码分析
文章目录 一、课程管理模块前端代码截图二、前端代码及分析index.vuecourse.js 三、前端执行流程1. 组件初始化2. 查询操作3. 列表操作4. 对话框操作5. API 请求6. 执行流程总结关键点 四、课程管理模块后端代码截图五、后端代码块CourseControllerICourseServiceCourseMapperC…...
在 Linux 上安装 `pgvector`(这是一个 PostgreSQL 的向量类型扩展,常用于处理嵌入向量,便于进行向量相似度搜索)
1. 安装 PostgreSQL 确保你已经安装好 PostgreSQL 数据库。 例如在 Ubuntu 上: sudo apt update sudo apt install postgresql postgresql-contrib2. 安装依赖 pgvector 扩展用的是 make、gcc 等开发工具,因此你需要先安装 PostgreSQL 的开发包和编译…...
09.MySQL内外连接
09.MySQL内外连接 文章目录 MySQL内外连接 内连接 外连接 左外连接 右外连接 简单案例 MySQL内外连接 在数据库操作中,表的连接是一个非常重要的概念。简单来说,连接就是将两个或多个表中的数据按照某种规则结合起来,从而获取我们所需要的…...
Python爬虫实战:研究Scrapy-Splash库相关技术
1 引言 1.1 研究背景与意义 网络爬虫作为一种自动获取互联网信息的技术,在数据挖掘、信息检索、舆情分析等领域有着广泛的应用。然而,随着 Web 技术的不断发展,越来越多的网站采用 JavaScript 动态渲染技术,如 React、Vue 等框架构建的单页应用 (SPA)。这些网站的内容通常…...

智能升级:中国新能源汽车充电桩规模化建设与充电桩智慧管理方案
近年来,中国新能源汽车产业快速发展,市场规模持续扩大,但充电基础设施的建设与管理仍面临布局不均、利用率低、智能化水平不足等问题。为推动新能源汽车普及,国家正加速充电桩的规模化建设,并通过智慧化管理提升运营效…...
AlphaFold3服务器安装与使用(非docker)(1)
1. 服务器显卡驱动准备 这部分我会详细记录一下我踩过的坑及怎样拯救的,原谅啰嗦啦 ^_^ 1.1 服务器旧配置 1.1.1 nvidia-smi [xxxxxxlocalhost ~]# nvidia-smi Thu May 29 20:54:00 2025 -------------------------------------------------------------…...