Appearance
Redux核心概念
概述
Redux是一个可预测的JavaScript状态容器,遵循单向数据流和函数式编程原则。虽然Redux Toolkit已成为推荐方式,但理解Redux核心概念对于掌握状态管理至关重要。
Redux三大原则
1. 单一数据源
整个应用的state存储在单一的store中:
jsx
// 整个应用只有一个store
const store = createStore(rootReducer);
// 状态树示例
const state = {
user: {
profile: { name: 'Alice', email: 'alice@example.com' },
isAuthenticated: true
},
todos: [
{ id: 1, text: 'Learn Redux', completed: false }
],
ui: {
theme: 'dark',
sidebarOpen: true
}
};2. State是只读的
唯一改变state的方法是触发action:
jsx
// 不能直接修改state
// state.user.name = 'Bob'; // ❌ 错误
// 必须dispatch action
store.dispatch({
type: 'user/updateName',
payload: 'Bob'
}); // ✅ 正确3. 使用纯函数执行修改
Reducer必须是纯函数,相同输入总是产生相同输出:
jsx
// ✅ 纯函数reducer
function counterReducer(state = 0, action) {
switch (action.type) {
case 'INCREMENT':
return state + 1; // 返回新值
case 'DECREMENT':
return state - 1;
default:
return state;
}
}
// ❌ 非纯函数(有副作用)
function badReducer(state = 0, action) {
if (action.type === 'INCREMENT') {
state++; // 直接修改state
console.log(state); // 副作用
return state;
}
return state;
}核心概念
1. Store
Store是保存应用state的容器:
jsx
import { createStore } from 'redux';
// 创建store
const store = createStore(reducer);
// Store API
store.getState(); // 获取当前state
store.dispatch(action); // 派发action
store.subscribe(listener); // 订阅state变化2. Action
Action是描述发生了什么的普通对象:
jsx
// 基础action
const incrementAction = {
type: 'INCREMENT'
};
// 带payload的action
const addTodoAction = {
type: 'ADD_TODO',
payload: {
id: 1,
text: 'Learn Redux',
completed: false
}
};
// Action Creator
function addTodo(text) {
return {
type: 'ADD_TODO',
payload: {
id: Date.now(),
text,
completed: false
}
};
}
// 使用
store.dispatch(addTodo('Learn React'));3. Reducer
Reducer是纯函数,接收state和action,返回新state:
jsx
// 基础reducer
function counterReducer(state = 0, action) {
switch (action.type) {
case 'INCREMENT':
return state + 1;
case 'DECREMENT':
return state - 1;
case 'INCREMENT_BY':
return state + action.payload;
default:
return state;
}
}
// 复杂reducer
const initialState = {
count: 0,
history: []
};
function counterWithHistoryReducer(state = initialState, action) {
switch (action.type) {
case 'INCREMENT':
return {
count: state.count + 1,
history: [...state.history, state.count + 1]
};
case 'DECREMENT':
return {
count: state.count - 1,
history: [...state.history, state.count - 1]
};
case 'RESET':
return initialState;
default:
return state;
}
}数据流
Redux的数据流是单向的:
View → Action → Reducer → Store → View完整数据流示例
jsx
// 1. 定义action types
const INCREMENT = 'INCREMENT';
const DECREMENT = 'DECREMENT';
// 2. 定义action creators
function increment() {
return { type: INCREMENT };
}
function decrement() {
return { type: DECREMENT };
}
// 3. 定义reducer
function counterReducer(state = 0, action) {
switch (action.type) {
case INCREMENT:
return state + 1;
case DECREMENT:
return state - 1;
default:
return state;
}
}
// 4. 创建store
const store = createStore(counterReducer);
// 5. 订阅变化
store.subscribe(() => {
console.log('State changed:', store.getState());
});
// 6. 派发actions
store.dispatch(increment()); // State changed: 1
store.dispatch(increment()); // State changed: 2
store.dispatch(decrement()); // State changed: 1Reducer组合
1. combineReducers
将多个reducer组合成一个:
jsx
import { combineReducers } from 'redux';
// 用户reducer
function userReducer(state = null, action) {
switch (action.type) {
case 'SET_USER':
return action.payload;
case 'LOGOUT':
return null;
default:
return state;
}
}
// Todos reducer
function todosReducer(state = [], action) {
switch (action.type) {
case 'ADD_TODO':
return [...state, action.payload];
case 'TOGGLE_TODO':
return state.map(todo =>
todo.id === action.payload
? { ...todo, completed: !todo.completed }
: todo
);
default:
return state;
}
}
// UI reducer
function uiReducer(state = { theme: 'light' }, action) {
switch (action.type) {
case 'SET_THEME':
return { ...state, theme: action.payload };
default:
return state;
}
}
// 组合reducers
const rootReducer = combineReducers({
user: userReducer,
todos: todosReducer,
ui: uiReducer
});
// 生成的state结构
const state = {
user: null,
todos: [],
ui: { theme: 'light' }
};2. 嵌套Reducer
jsx
// products reducer
function productsReducer(state = [], action) {
switch (action.type) {
case 'ADD_PRODUCT':
return [...state, action.payload];
default:
return state;
}
}
// cart reducer
function cartReducer(state = [], action) {
switch (action.type) {
case 'ADD_TO_CART':
return [...state, action.payload];
default:
return state;
}
}
// 组合shop reducer
const shopReducer = combineReducers({
products: productsReducer,
cart: cartReducer
});
// 组合根reducer
const rootReducer = combineReducers({
user: userReducer,
shop: shopReducer,
ui: uiReducer
});
// 最终state结构
const state = {
user: null,
shop: {
products: [],
cart: []
},
ui: { theme: 'light' }
};React集成
1. 基础集成
jsx
import { createStore } from 'redux';
import { Provider, useSelector, useDispatch } from 'react-redux';
// Store
const store = createStore(counterReducer);
// App组件
function App() {
return (
<Provider store={store}>
<Counter />
</Provider>
);
}
// Counter组件
function Counter() {
const count = useSelector(state => state);
const dispatch = useDispatch();
return (
<div>
<p>Count: {count}</p>
<button onClick={() => dispatch(increment())}>+</button>
<button onClick={() => dispatch(decrement())}>-</button>
</div>
);
}2. 选择器使用
jsx
import { useSelector } from 'react-redux';
function UserProfile() {
// 选择特定字段
const userName = useSelector(state => state.user.name);
const userEmail = useSelector(state => state.user.email);
// 选择派生数据
const completedTodos = useSelector(state =>
state.todos.filter(todo => todo.completed)
);
// 使用相等性检查
const user = useSelector(
state => state.user,
(left, right) => left.id === right.id // 自定义比较
);
return (
<div>
<h1>{userName}</h1>
<p>{userEmail}</p>
</div>
);
}中间件
1. 中间件概念
中间件扩展了dispatch功能,可以处理副作用:
jsx
// 日志中间件
const logger = store => next => action => {
console.log('dispatching', action);
let result = next(action);
console.log('next state', store.getState());
return result;
};
// 应用中间件
import { createStore, applyMiddleware } from 'redux';
const store = createStore(
reducer,
applyMiddleware(logger)
);2. Redux Thunk
处理异步操作:
jsx
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
const store = createStore(
reducer,
applyMiddleware(thunk)
);
// Thunk action creator
function fetchUser(userId) {
return async (dispatch, getState) => {
dispatch({ type: 'FETCH_USER_START' });
try {
const response = await fetch(`/api/users/${userId}`);
const user = await response.json();
dispatch({
type: 'FETCH_USER_SUCCESS',
payload: user
});
} catch (error) {
dispatch({
type: 'FETCH_USER_ERROR',
payload: error.message
});
}
};
}
// 使用
store.dispatch(fetchUser(1));最佳实践
1. Action Types常量
jsx
// constants/actionTypes.js
export const ADD_TODO = 'ADD_TODO';
export const TOGGLE_TODO = 'TOGGLE_TODO';
export const DELETE_TODO = 'DELETE_TODO';
// actions/todos.js
import { ADD_TODO, TOGGLE_TODO, DELETE_TODO } from '../constants/actionTypes';
export function addTodo(text) {
return {
type: ADD_TODO,
payload: { id: Date.now(), text, completed: false }
};
}
export function toggleTodo(id) {
return {
type: TOGGLE_TODO,
payload: id
};
}2. Reducer组织
jsx
// reducers/todos.js
import { ADD_TODO, TOGGLE_TODO, DELETE_TODO } from '../constants/actionTypes';
const initialState = [];
export function todosReducer(state = initialState, action) {
switch (action.type) {
case ADD_TODO:
return [...state, action.payload];
case TOGGLE_TODO:
return state.map(todo =>
todo.id === action.payload
? { ...todo, completed: !todo.completed }
: todo
);
case DELETE_TODO:
return state.filter(todo => todo.id !== action.payload);
default:
return state;
}
}
// reducers/index.js
import { combineReducers } from 'redux';
import { todosReducer } from './todos';
import { userReducer } from './user';
export const rootReducer = combineReducers({
todos: todosReducer,
user: userReducer
});3. 选择器模式
jsx
// selectors/todos.js
export const selectAllTodos = state => state.todos;
export const selectCompletedTodos = state =>
state.todos.filter(todo => todo.completed);
export const selectActiveTodos = state =>
state.todos.filter(todo => !todo.completed);
export const selectTodoById = (state, id) =>
state.todos.find(todo => todo.id === id);
// 组件中使用
function TodoList() {
const completedTodos = useSelector(selectCompletedTodos);
return (
<ul>
{completedTodos.map(todo => (
<li key={todo.id}>{todo.text}</li>
))}
</ul>
);
}不可变更新模式
1. 对象更新
jsx
// ❌ 错误:直接修改
function badReducer(state, action) {
state.name = action.payload;
return state;
}
// ✅ 正确:返回新对象
function goodReducer(state, action) {
return {
...state,
name: action.payload
};
}
// 深层更新
function updateNestedReducer(state, action) {
return {
...state,
user: {
...state.user,
profile: {
...state.user.profile,
name: action.payload
}
}
};
}2. 数组更新
jsx
function arrayReducer(state = [], action) {
switch (action.type) {
// 添加
case 'ADD_ITEM':
return [...state, action.payload];
// 删除
case 'REMOVE_ITEM':
return state.filter(item => item.id !== action.payload);
// 更新
case 'UPDATE_ITEM':
return state.map(item =>
item.id === action.payload.id
? { ...item, ...action.payload.updates }
: item
);
// 插入
case 'INSERT_ITEM':
return [
...state.slice(0, action.payload.index),
action.payload.item,
...state.slice(action.payload.index)
];
default:
return state;
}
}DevTools集成
jsx
import { createStore } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension';
const store = createStore(
rootReducer,
composeWithDevTools(
applyMiddleware(thunk, logger)
)
);
// 或使用增强器
const store = createStore(
rootReducer,
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);常见问题
1. State规范化
jsx
// ❌ 嵌套数据
const badState = {
posts: [
{
id: 1,
title: 'Post 1',
author: { id: 1, name: 'Alice' },
comments: [
{ id: 1, text: 'Comment 1', author: { id: 2, name: 'Bob' } }
]
}
]
};
// ✅ 规范化数据
const goodState = {
posts: {
byId: {
1: { id: 1, title: 'Post 1', authorId: 1, commentIds: [1] }
},
allIds: [1]
},
users: {
byId: {
1: { id: 1, name: 'Alice' },
2: { id: 2, name: 'Bob' }
},
allIds: [1, 2]
},
comments: {
byId: {
1: { id: 1, text: 'Comment 1', authorId: 2 }
},
allIds: [1]
}
};2. Reducer拆分
jsx
// 按功能拆分
const userReducer = (state = {}, action) => {
// 处理用户相关actions
};
const todosReducer = (state = [], action) => {
// 处理todos相关actions
};
// 按状态类型拆分
const loadingReducer = (state = {}, action) => {
// 处理所有loading状态
};
const errorsReducer = (state = {}, action) => {
// 处理所有errors
};总结
Redux核心概念是理解现代状态管理的基础:
- 三大原则:单一数据源、只读state、纯函数reducer
- 核心概念:Store、Action、Reducer
- 数据流:单向数据流保证可预测性
- 组合模式:combineReducers组合多个reducer
- 中间件:扩展dispatch处理副作用
- 不可变更新:始终返回新对象/数组
- 最佳实践:常量、选择器、规范化
虽然Redux Toolkit简化了很多操作,但理解这些核心概念对于掌握状态管理至关重要。
第四部分:高级Redux模式
4.1 Redux源码剖析
javascript
// createStore的简化实现
function createStore(reducer, preloadedState, enhancer) {
// 如果有enhancer(如applyMiddleware),应用它
if (typeof enhancer !== 'undefined') {
return enhancer(createStore)(reducer, preloadedState);
}
let currentState = preloadedState;
let currentReducer = reducer;
let currentListeners = [];
let nextListeners = currentListeners;
let isDispatching = false;
function getState() {
if (isDispatching) {
throw new Error('You may not call store.getState() while the reducer is executing.');
}
return currentState;
}
function subscribe(listener) {
if (isDispatching) {
throw new Error('You may not call store.subscribe() while the reducer is executing.');
}
let isSubscribed = true;
// 确保可以在dispatch过程中取消订阅
if (nextListeners === currentListeners) {
nextListeners = currentListeners.slice();
}
nextListeners.push(listener);
return function unsubscribe() {
if (!isSubscribed) return;
isSubscribed = false;
if (nextListeners === currentListeners) {
nextListeners = currentListeners.slice();
}
const index = nextListeners.indexOf(listener);
nextListeners.splice(index, 1);
currentListeners = null;
};
}
function dispatch(action) {
if (!isPlainObject(action)) {
throw new Error('Actions must be plain objects.');
}
if (typeof action.type === 'undefined') {
throw new Error('Actions may not have an undefined "type" property.');
}
if (isDispatching) {
throw new Error('Reducers may not dispatch actions.');
}
try {
isDispatching = true;
currentState = currentReducer(currentState, action);
} finally {
isDispatching = false;
}
const listeners = (currentListeners = nextListeners);
for (let i = 0; i < listeners.length; i++) {
const listener = listeners[i];
listener();
}
return action;
}
function replaceReducer(nextReducer) {
currentReducer = nextReducer;
dispatch({ type: '@@redux/REPLACE' });
}
// 初始化store
dispatch({ type: '@@redux/INIT' });
return {
dispatch,
subscribe,
getState,
replaceReducer
};
}
// combineReducers的简化实现
function combineReducers(reducers) {
const reducerKeys = Object.keys(reducers);
const finalReducers = {};
// 过滤掉非函数的reducer
for (let i = 0; i < reducerKeys.length; i++) {
const key = reducerKeys[i];
if (typeof reducers[key] === 'function') {
finalReducers[key] = reducers[key];
}
}
const finalReducerKeys = Object.keys(finalReducers);
return function combination(state = {}, action) {
let hasChanged = false;
const nextState = {};
for (let i = 0; i < finalReducerKeys.length; i++) {
const key = finalReducerKeys[i];
const reducer = finalReducers[key];
const previousStateForKey = state[key];
const nextStateForKey = reducer(previousStateForKey, action);
nextState[key] = nextStateForKey;
hasChanged = hasChanged || nextStateForKey !== previousStateForKey;
}
return hasChanged ? nextState : state;
};
}
// applyMiddleware的简化实现
function applyMiddleware(...middlewares) {
return createStore => (...args) => {
const store = createStore(...args);
let dispatch = () => {
throw new Error('Dispatching while constructing middleware is not allowed.');
};
const middlewareAPI = {
getState: store.getState,
dispatch: (...args) => dispatch(...args)
};
const chain = middlewares.map(middleware => middleware(middlewareAPI));
dispatch = compose(...chain)(store.dispatch);
return {
...store,
dispatch
};
};
}
// compose函数
function compose(...funcs) {
if (funcs.length === 0) {
return arg => arg;
}
if (funcs.length === 1) {
return funcs[0];
}
return funcs.reduce((a, b) => (...args) => a(b(...args)));
}4.2 高级中间件模式
javascript
// 1. 错误处理中间件
const errorHandler = store => next => action => {
try {
return next(action);
} catch (error) {
console.error('Caught an exception!', error);
// 发送错误到监控服务
trackError(error, {
action,
state: store.getState()
});
// 派发错误action
store.dispatch({
type: 'APP_ERROR',
payload: error.message
});
throw error;
}
};
// 2. 异步队列中间件
const asyncQueue = store => {
const queue = [];
let isProcessing = false;
async function processQueue() {
if (isProcessing || queue.length === 0) return;
isProcessing = true;
while (queue.length > 0) {
const { action, resolve, reject } = queue.shift();
try {
const result = await action(store.dispatch, store.getState);
resolve(result);
} catch (error) {
reject(error);
}
}
isProcessing = false;
}
return next => action => {
if (typeof action === 'function') {
return new Promise((resolve, reject) => {
queue.push({ action, resolve, reject });
processQueue();
});
}
return next(action);
};
};
// 3. 防抖中间件
const debounceMiddleware = store => {
const debounceMap = new Map();
return next => action => {
const { meta } = action;
if (!meta || !meta.debounce) {
return next(action);
}
const key = meta.debounce.key || action.type;
const delay = meta.debounce.delay || 300;
// 清除之前的定时器
if (debounceMap.has(key)) {
clearTimeout(debounceMap.get(key));
}
// 设置新的定时器
const timeoutId = setTimeout(() => {
next(action);
debounceMap.delete(key);
}, delay);
debounceMap.set(key, timeoutId);
};
};
// 4. 节流中间件
const throttleMiddleware = store => {
const throttleMap = new Map();
return next => action => {
const { meta } = action;
if (!meta || !meta.throttle) {
return next(action);
}
const key = meta.throttle.key || action.type;
const delay = meta.throttle.delay || 300;
if (throttleMap.has(key)) {
return; // 忽略
}
throttleMap.set(key, true);
setTimeout(() => {
throttleMap.delete(key);
}, delay);
return next(action);
};
};
// 5. 条件派发中间件
const conditionalDispatch = store => next => action => {
const { meta } = action;
if (meta && meta.condition) {
const shouldDispatch = meta.condition(store.getState(), action);
if (!shouldDispatch) {
return; // 不派发action
}
}
return next(action);
};
// 6. 批量派发中间件
const batchMiddleware = store => {
let batch = [];
let isBatching = false;
return next => action => {
if (action.type === 'BATCH_START') {
isBatching = true;
batch = [];
return;
}
if (action.type === 'BATCH_END') {
isBatching = false;
// 批量派发所有action
batch.forEach(a => next(a));
batch = [];
return;
}
if (isBatching) {
batch.push(action);
return;
}
return next(action);
};
};
// 使用示例
const store = createStore(
rootReducer,
applyMiddleware(
errorHandler,
asyncQueue,
debounceMiddleware,
throttleMiddleware,
conditionalDispatch,
batchMiddleware
)
);
// 使用防抖
store.dispatch({
type: 'SEARCH',
payload: 'query',
meta: {
debounce: { delay: 500 }
}
});
// 使用节流
store.dispatch({
type: 'SCROLL',
payload: { y: 100 },
meta: {
throttle: { delay: 100 }
}
});
// 条件派发
store.dispatch({
type: 'UPDATE_USER',
payload: { name: 'Alice' },
meta: {
condition: (state, action) => state.user.id !== null
}
});
// 批量派发
store.dispatch({ type: 'BATCH_START' });
store.dispatch({ type: 'ACTION_1' });
store.dispatch({ type: 'ACTION_2' });
store.dispatch({ type: 'ACTION_3' });
store.dispatch({ type: 'BATCH_END' });4.3 高级Reducer模式
javascript
// 1. Reducer工厂函数
function createListReducer(name) {
const initialState = {
items: [],
loading: false,
error: null
};
return (state = initialState, action) => {
switch (action.type) {
case `${name}/FETCH_START`:
return {
...state,
loading: true,
error: null
};
case `${name}/FETCH_SUCCESS`:
return {
...state,
loading: false,
items: action.payload
};
case `${name}/FETCH_ERROR`:
return {
...state,
loading: false,
error: action.payload
};
case `${name}/ADD`:
return {
...state,
items: [...state.items, action.payload]
};
case `${name}/REMOVE`:
return {
...state,
items: state.items.filter(item => item.id !== action.payload)
};
default:
return state;
}
};
}
// 使用
const usersReducer = createListReducer('users');
const productsReducer = createListReducer('products');
// 2. 高阶Reducer
function withUndo(reducer) {
const initialState = {
past: [],
present: reducer(undefined, {}),
future: []
};
return (state = initialState, action) => {
const { past, present, future } = state;
switch (action.type) {
case 'UNDO':
if (past.length === 0) return state;
return {
past: past.slice(0, -1),
present: past[past.length - 1],
future: [present, ...future]
};
case 'REDO':
if (future.length === 0) return state;
return {
past: [...past, present],
present: future[0],
future: future.slice(1)
};
default:
const newPresent = reducer(present, action);
if (present === newPresent) {
return state;
}
return {
past: [...past, present],
present: newPresent,
future: []
};
}
};
}
// 使用
const todosReducer = (state = [], action) => {
// ... todo reducer logic
};
const undoableTodosReducer = withUndo(todosReducer);
// 3. 动态Reducer注入
class DynamicReducerManager {
constructor(initialReducers) {
this.reducers = { ...initialReducers };
this.combinedReducer = combineReducers(this.reducers);
}
getReducerMap() {
return { ...this.reducers };
}
reduce(state, action) {
return this.combinedReducer(state, action);
}
add(key, reducer) {
if (!key || this.reducers[key]) {
return;
}
this.reducers[key] = reducer;
this.combinedReducer = combineReducers(this.reducers);
}
remove(key) {
if (!key || !this.reducers[key]) {
return;
}
delete this.reducers[key];
this.combinedReducer = combineReducers(this.reducers);
}
}
// 使用
const reducerManager = new DynamicReducerManager({
app: appReducer,
user: userReducer
});
const store = createStore(
(state, action) => reducerManager.reduce(state, action)
);
// 动态添加reducer
reducerManager.add('newFeature', newFeatureReducer);
store.dispatch({ type: '@@redux/REPLACE' });
// 4. 智能合并Reducer
function createSmartReducer(handlers) {
return (state = {}, action) => {
// 检查是否有对应的handler
if (handlers[action.type]) {
return handlers[action.type](state, action);
}
// 检查通配符handler
for (const pattern in handlers) {
if (pattern.includes('*')) {
const regex = new RegExp(pattern.replace('*', '.*'));
if (regex.test(action.type)) {
return handlers[pattern](state, action);
}
}
}
return state;
};
}
// 使用
const userReducer = createSmartReducer({
'user/SET_*': (state, action) => ({
...state,
[action.meta.field]: action.payload
}),
'user/FETCH_*_SUCCESS': (state, action) => ({
...state,
data: action.payload,
loading: false
}),
'user/FETCH_*_ERROR': (state, action) => ({
...state,
error: action.payload,
loading: false
})
});4.4 性能优化技巧
javascript
// 1. Selector记忆化
import { createSelector } from 'reselect';
// 基础选择器
const selectTodos = state => state.todos;
const selectFilter = state => state.filter;
// 记忆化派生数据
const selectVisibleTodos = createSelector(
[selectTodos, selectFilter],
(todos, filter) => {
console.log('Computing visible todos'); // 只在输入变化时执行
switch (filter) {
case 'SHOW_ALL':
return todos;
case 'SHOW_COMPLETED':
return todos.filter(todo => todo.completed);
case 'SHOW_ACTIVE':
return todos.filter(todo => !todo.completed);
default:
return todos;
}
}
);
// 参数化选择器
const makeSelectTodoById = () => createSelector(
[selectTodos, (state, id) => id],
(todos, id) => todos.find(todo => todo.id === id)
);
// 使用
function TodoItem({ id }) {
const selectTodoById = useMemo(makeSelectTodoById, []);
const todo = useSelector(state => selectTodoById(state, id));
return <div>{todo.text}</div>;
}
// 2. 批量更新优化
import { batch } from 'react-redux';
function performBatchUpdate() {
batch(() => {
store.dispatch({ type: 'ACTION_1' });
store.dispatch({ type: 'ACTION_2' });
store.dispatch({ type: 'ACTION_3' });
// 只会触发一次组件更新
});
}
// 3. 浅比较优化
import { shallowEqual } from 'react-redux';
function MyComponent() {
const user = useSelector(
state => ({
name: state.user.name,
email: state.user.email
}),
shallowEqual // 使用浅比较
);
return <div>{user.name}</div>;
}
// 4. State规范化
import { normalize, schema } from 'normalizr';
// 定义schema
const userSchema = new schema.Entity('users');
const commentSchema = new schema.Entity('comments', {
author: userSchema
});
const postSchema = new schema.Entity('posts', {
author: userSchema,
comments: [commentSchema]
});
// 规范化数据
const originalData = {
id: 1,
title: 'Post',
author: { id: 1, name: 'Alice' },
comments: [
{ id: 1, text: 'Comment', author: { id: 2, name: 'Bob' } }
]
};
const normalizedData = normalize(originalData, postSchema);
// 规范化的reducer
function entitiesReducer(state = { users: {}, posts: {}, comments: {} }, action) {
switch (action.type) {
case 'FETCH_POST_SUCCESS':
return {
users: { ...state.users, ...action.payload.entities.users },
posts: { ...state.posts, ...action.payload.entities.posts },
comments: { ...state.comments, ...action.payload.entities.comments }
};
default:
return state;
}
}
// 5. 使用Immer简化不可变更新
import produce from 'immer';
const todosReducer = produce((draft, action) => {
switch (action.type) {
case 'ADD_TODO':
draft.push(action.payload); // 可以直接修改
break;
case 'TOGGLE_TODO':
const todo = draft.find(t => t.id === action.payload);
if (todo) {
todo.completed = !todo.completed;
}
break;
case 'REMOVE_TODO':
const index = draft.findIndex(t => t.id === action.payload);
if (index !== -1) {
draft.splice(index, 1);
}
break;
}
}, []);4.5 调试与监控
javascript
// 1. Action追踪
const actionTracker = store => {
const actionHistory = [];
const MAX_HISTORY = 100;
return next => action => {
const timestamp = Date.now();
const prevState = store.getState();
const result = next(action);
const nextState = store.getState();
actionHistory.push({
action,
timestamp,
prevState,
nextState,
diff: getStateDiff(prevState, nextState)
});
if (actionHistory.length > MAX_HISTORY) {
actionHistory.shift();
}
// 暴露全局访问
window.__ACTION_HISTORY__ = actionHistory;
return result;
};
};
function getStateDiff(prev, next) {
const diff = {};
Object.keys(next).forEach(key => {
if (prev[key] !== next[key]) {
diff[key] = {
prev: prev[key],
next: next[key]
};
}
});
return diff;
}
// 2. 性能监控
const performanceMonitor = store => next => action => {
const start = performance.now();
const result = next(action);
const duration = performance.now() - start;
if (duration > 16) { // 超过一帧的时间
console.warn(`Slow action: ${action.type} took ${duration.toFixed(2)}ms`);
}
// 发送到分析服务
if (window.analytics) {
window.analytics.track('redux_action', {
type: action.type,
duration
});
}
return result;
};
// 3. State快照
class StateSnapshotManager {
constructor() {
this.snapshots = [];
}
take(store, label) {
this.snapshots.push({
label,
timestamp: Date.now(),
state: JSON.parse(JSON.stringify(store.getState()))
});
}
restore(store, index) {
if (index >= 0 && index < this.snapshots.length) {
const snapshot = this.snapshots[index];
store.dispatch({
type: '@@RESTORE_SNAPSHOT',
payload: snapshot.state
});
}
}
list() {
return this.snapshots.map((s, i) => ({
index: i,
label: s.label,
timestamp: new Date(s.timestamp).toISOString()
}));
}
clear() {
this.snapshots = [];
}
}
// 使用
const snapshotManager = new StateSnapshotManager();
// 在关键点拍摄快照
snapshotManager.take(store, 'After login');
snapshotManager.take(store, 'After data load');
// 恢复到之前的状态
snapshotManager.restore(store, 0);
// 4. Redux DevTools增强
const devToolsEnhancer = window.__REDUX_DEVTOOLS_EXTENSION__
? window.__REDUX_DEVTOOLS_EXTENSION__({
trace: true,
traceLimit: 25,
features: {
pause: true,
lock: true,
persist: true,
export: true,
import: 'custom',
jump: true,
skip: true,
reorder: true,
dispatch: true,
test: true
}
})
: f => f;
const store = createStore(
rootReducer,
composeWithDevTools(
applyMiddleware(thunk, logger),
devToolsEnhancer
)
);总结升级
Redux核心概念深入理解:
核心架构
Store(状态容器)
├── State(应用状态)
├── Dispatch(派发机制)
└── Subscribe(订阅系统)
Data Flow(数据流)
View → Action → Middleware → Reducer → Store → View
Middleware Chain(中间件链)
Action → MW1 → MW2 → MW3 → Reducer高级模式总结
1. 源码级理解
✅ createStore实现
✅ combineReducers逻辑
✅ applyMiddleware机制
✅ compose函数原理
2. 中间件模式
✅ 错误处理
✅ 异步队列
✅ 防抖节流
✅ 条件派发
✅ 批量更新
3. Reducer模式
✅ 工厂函数
✅ 高阶Reducer
✅ 动态注入
✅ 智能合并
4. 性能优化
✅ Selector记忆化
✅ 批量更新
✅ 浅比较
✅ State规范化
✅ Immer集成
5. 调试监控
✅ Action追踪
✅ 性能监控
✅ State快照
✅ DevTools增强Redux的核心在于可预测性和可维护性,深入理解这些概念将为掌握现代状态管理奠定坚实基础。