前端开发的历史,某种意义上就是状态管理方案的演进史。从jQuery的全局状态混乱,到现代原子化状态的精细管控,我们走过了怎样的一条认知升级之路?

一、历史的十字路口:Flux架构的诞生与Redux的统治

1.1 Flux的核心思想:单向数据流的革命

Flux不是具体的库,而是一种架构模式。它的诞生源于Facebook在开发过程中遇到的回调地狱和状态同步问题。

javascript

复制

下载

// Flux的核心Dispatcher
class Dispatcher {
  constructor() {
    this.callbacks = {};
    this.isPending = {};
    this.isHandled = {};
  }

  register(callback) {
    const id = generateId();
    this.callbacks[id] = callback;
    return id;
  }

  dispatch(payload) {
    // 确保按注册顺序执行
    Object.keys(this.callbacks).forEach(id => {
      if (!this.isPending[id]) {
        this.isPending[id] = true;
        this.callbacks[id](payload);
        this.isHandled[id] = true;
      }
    });
  }
}

1.2 Redux:Flux思想的极致简化

Redux将Flux简化为三个核心原则:

  • 单一数据源

  • 状态只读

  • 使用纯函数进行更改

javascript

复制

下载

// 经典的Redux计数器实现
const initialState = { count: 0 };

// Reducer必须是纯函数
function counterReducer(state = initialState, action) {
  switch (action.type) {
    case 'INCREMENT':
      return { ...state, count: state.count + 1 };
    case 'DECREMENT':
      return { ...state, count: state.count - 1 };
    case 'SET_COUNT':
      return { ...state, count: action.payload };
    default:
      return state;
  }
}

// Action Creator
const increment = () => ({ type: 'INCREMENT' });
const setCount = (value) => ({ 
  type: 'SET_COUNT', 
  payload: value 
});

// Store
const store = Redux.createStore(counterReducer);

1.3 Redux的痛点与中间件演进

随着应用复杂度增长,Redux暴露了诸多问题:

javascript

复制

下载

// ❌ 传统的Redux异步处理:冗长的样板代码
function fetchUserData(userId) {
  return (dispatch) => {
    dispatch({ type: 'FETCH_USER_REQUEST' });
    
    return fetch(`/api/users/${userId}`)
      .then(response => response.json())
      .then(user => {
        dispatch({ 
          type: 'FETCH_USER_SUCCESS', 
          payload: user 
        });
      })
      .catch(error => {
        dispatch({ 
          type: 'FETCH_USER_FAILURE', 
          payload: error 
        });
      });
  };
}

// ✅ 现代Redux Toolkit解决方案
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';

const fetchUserById = createAsyncThunk(
  'users/fetchById',
  async (userId, thunkAPI) => {
    const response = await fetch(`/api/users/${userId}`);
    return await response.json();
  }
);

const usersSlice = createSlice({
  name: 'users',
  initialState: {
    entities: {},
    loading: 'idle',
  },
  reducers: {},
  extraReducers: (builder) => {
    builder
      .addCase(fetchUserById.pending, (state) => {
        state.loading = 'pending';
      })
      .addCase(fetchUserById.fulfilled, (state, action) => {
        state.loading = 'idle';
        state.entities[action.payload.id] = action.payload;
      });
  }
});
二、响应式革命:MobX的优雅与陷阱

2.1 观察者模式的现代实现

MobX采用了完全不同的哲学:如果状态是真理的来源,那么派生状态应该自动更新。

javascript

复制

下载

import { makeObservable, observable, computed, action, autorun } from 'mobx';

class TodoStore {
  todos = [];
  filter = 'ALL';

  constructor() {
    makeObservable(this, {
      todos: observable,
      filter: observable,
      filteredTodos: computed,
      addTodo: action,
      setFilter: action,
    });
  }

  get filteredTodos() {
    switch (this.filter) {
      case 'COMPLETED':
        return this.todos.filter(todo => todo.completed);
      case 'ACTIVE':
        return this.todos.filter(todo => !todo.completed);
      default:
        return this.todos;
    }
  }

  addTodo = (text) => {
    this.todos.push({
      id: Math.random(),
      text,
      completed: false,
    });
  }

  setFilter = (filter) => {
    this.filter = filter;
  }
}

// 自动响应式
const store = new TodoStore();
autorun(() => {
  console.log(`Todos count: ${store.filteredTodos.length}`);
});

2.2 MobX的优雅与隐式依赖问题

javascript

复制

下载

// ❌ 隐式依赖导致的难以调试问题
class ProblematicStore {
  @observable data = null;
  @observable derivedValue = null;

  constructor() {
    // 这个autorun依赖了什么?很难静态分析
    autorun(() => {
      if (this.data) {
        // 这里可能访问了多个observable,形成隐式依赖网
        this.derivedValue = this.data.items
          .filter(item => item.isValid)
          .map(item => item.value);
      }
    });
  }
}

// ✅ 更可控的响应式
class BetterStore {
  @observable data = null;
  
  @computed get derivedValue() {
    if (!this.data) return null;
    return this.data.items
      .filter(item => item.isValid)
      .map(item => item.value);
  }
}
三、状态机:确定性的艺术

3.1 为什么需要状态机?

传统状态管理的根本问题:状态可能处于任何组合,导致不可预测的行为。

javascript

复制

下载

// ❌ 传统的布尔状态组合 - 会产生非法状态
class ProblematicComponent {
  state = {
    isLoading: false,
    isError: false,
    data: null,
    error: null
  };

  async fetchData() {
    this.setState({ isLoading: true });
    try {
      const data = await api.getData();
      // 如果组件在请求期间被卸载,这里会设置状态到已卸载的组件
      this.setState({ 
        isLoading: false, 
        data,
        isError: false 
      });
    } catch (error) {
      this.setState({ 
        isLoading: false, 
        error,
        isError: true 
      });
    }
  }
}

3.2 XState:有限状态机的现代实现

javascript

复制

下载

import { createMachine, interpret } from 'xstate';

// 定义状态机
const dataMachine = createMachine({
  id: 'data',
  initial: 'idle',
  states: {
    idle: {
      on: { FETCH: 'loading' }
    },
    loading: {
      on: {
        RESOLVE: 'success',
        REJECT: 'failure',
        CANCEL: 'idle'
      }
    },
    success: {
      on: { REFETCH: 'loading' }
    },
    failure: {
      on: { RETRY: 'loading' }
    }
  }
});

// 使用状态机
const service = interpret(dataMachine)
  .onTransition((state) => {
    console.log('当前状态:', state.value);
  })
  .start();

service.send('FETCH');

3.3 带上下文的复杂状态机

javascript

复制

下载

const todoMachine = createMachine({
  id: 'todos',
  initial: 'loading',
  context: {
    todos: [],
    error: null,
    filter: 'all'
  },
  states: {
    loading: {
      invoke: {
        src: 'fetchTodos',
        onDone: {
          target: 'ready',
          actions: 'setTodos'
        },
        onError: {
          target: 'failure',
          actions: 'setError'
        }
      }
    },
    ready: {
      on: {
        ADD_TODO: {
          actions: 'addTodo'
        },
        TOGGLE_TODO: {
          actions: 'toggleTodo'
        },
        SET_FILTER: {
          actions: 'setFilter'
        }
      }
    },
    failure: {
      on: {
        RETRY: 'loading'
      }
    }
  }
}, {
  actions: {
    setTodos: assign({
      todos: (_, event) => event.data
    }),
    addTodo: assign({
      todos: (context, event) => [
        ...context.todos,
        {
          id: generateId(),
          text: event.text,
          completed: false
        }
      ]
    })
  },
  services: {
    fetchTodos: () => fetch('/api/todos').then(res => res.json())
  }
});
四、原子化设计:状态管理的终极进化

4.1 Jotai:基于原子状态的理念

原子化状态的核心思想:将状态分解为最小的原子单位,通过组合形成复杂状态。

javascript

复制

下载

import { atom, useAtom } from 'jotai';

// 基础原子
const countAtom = atom(0);
const textAtom = atom('hello');

// 派生原子 - 自动响应依赖变化
const doubledCountAtom = atom((get) => get(countAtom) * 2);
const characterCountAtom = atom((get) => get(textAtom).length);

// 异步原子
const userAtom = atom(async (get) => {
  const userId = get(currentUserIdAtom);
  const response = await fetch(`/api/users/${userId}`);
  return response.json();
});

// 写入派生
const incrementAtom = atom(
  null, // 读取函数 - 这里不需要读取
  (get, set) => set(countAtom, get(countAtom) + 1)
);

function Counter() {
  const [count, setCount] = useAtom(countAtom);
  const [doubledCount] = useAtom(doubledCountAtom);
  const [, increment] = useAtom(incrementAtom);

  return (
    <div>
      <p>Count: {count}</p>
      <p>Doubled: {doubledCount}</p>
      <button onClick={increment}>Increment</button>
    </div>
  );
}

4.2 Zustand:极简主义的胜利

javascript

复制

下载

import create from 'zustand';

// 创建store - 比Redux简洁得多
const useStore = create((set, get) => ({
  bears: 0,
  increasePopulation: () => set(state => ({ bears: state.bears + 1 })),
  removeAllBears: () => set({ bears: 0 }),
  asyncIncrease: async () => {
    // 异步操作天然支持
    const response = await fetch('/api/increase');
    const { amount } = await response.json();
    set(state => ({ bears: state.bears + amount }));
  }
}));

// 在组件中使用
function BearCounter() {
  const bears = useStore(state => state.bears);
  const increase = useStore(state => state.increasePopulation);
  
  return (
    <div>
      <h1>{bears} bears around here...</h1>
      <button onClick={increase}>Add bear</button>
    </div>
  );
}
五、现代状态管理的最佳实践

5.1 状态结构设计原则

javascript

复制

下载

// ❌ 糟糕的状态结构
const badState = {
  user: {
    profile: { /* ... */ },
    settings: { /* ... */ },
    // 将不同关注点混在一起
  }
};

// ✅ 好的状态结构 - 按领域拆分
const goodState = {
  auth: { /* 认证状态 */ },
  userProfile: { /* 用户资料 */ },
  userSettings: { /* 用户设置 */ },
  ui: { /* UI状态 */ }
};

5.2 状态性能优化模式

javascript

复制

下载

// 使用选择器避免不必要重渲染
const useUserSettings = (key) => {
  return useStore(
    useCallback(
      (state) => state.settings[key],
      [key]
    )
  );
};

// 批量更新优化
const useBatchUpdates = () => {
  const updateMultiple = useStore(state => state.updateMultiple);
  
  const handleComplexUpdate = useCallback(() => {
    // Zustand自动批处理,React状态需要手动批处理
    React.unstable_batchedUpdates(() => {
      updateMultiple(values);
    });
  }, [updateMultiple]);
};

5.3 测试策略

javascript

复制

下载

// 测试状态机
describe('todoMachine', () => {
  it('should transition to loading when FETCH is sent', () => {
    const expectedValue = 'loading';
    
    const actualState = todoMachine.transition('idle', { type: 'FETCH' });
    
    expect(actualState.value).toBe(expectedValue);
  });
});

// 测试Zustand store
const createTestStore = () => {
  return create(() => ({
    bears: 0,
    increase: () => set({ bears: get().bears + 1 })
  }));
};

test('increase bear population', () => {
  const store = createTestStore();
  store.getState().increase();
  expect(store.getState().bears).toBe(1);
});
六、如何选择:状态管理方案决策矩阵

建立基于项目需求的科学选择框架:

javascript

复制

下载

const stateManagementDecisionMatrix = {
  criteria: {
    projectSize: ['small', 'medium', 'large'],
    teamFamiliarity: ['low', 'medium', 'high'],
    stateComplexity: ['low', 'medium', 'high'],
    needForDevTools: [true, false],
    learningCurve: ['low', 'medium', 'high']
  },
  
  recommendations: {
    smallProject: {
      solution: 'Zustand or Jotai',
      reasoning: '极简API,学习成本低,满足大部分场景'
    },
    
    complexState: {
      solution: 'XState or Redux + Redux Toolkit',
      reasoning: '状态机提供确定性,Redux提供明确的数据流'
    },
    
    realTimeCollaboration: {
      solution: 'MobX or Valtio',
      reasoning: '响应式状态更适合实时同步场景'
    },
    
    largeTeam: {
      solution: 'Redux Toolkit or XState',
      reasoning: '强约束性和明确的模式有助于团队协作'
    }
  }
};
七、未来展望:状态管理的下一个前沿

7.1 服务端状态管理的兴起

React Query和SWR正在重新定义我们对状态的认知:

javascript

复制

下载

import { useQuery, useMutation, useQueryClient } from 'react-query';

function UserProfile() {
  const queryClient = useQueryClient();
  
  // 服务端状态
  const { data: user, isLoading } = useQuery('user', fetchUser);
  
  // 修改服务端状态
  const mutation = useMutation(updateUser, {
    onSuccess: () => {
      // 使缓存失效,触发重新获取
      queryClient.invalidateQueries('user');
    },
  });

  if (isLoading) return <div>Loading...</div>;
  
  return (
    <div>
      <h1>{user.name}</h1>
      <button onClick={() => mutation.mutate({ name: 'New Name' })}>
        Update Name
      </button>
    </div>
  );
}

7.2 编译时状态优化

新一代状态管理库开始利用编译时优化:

javascript

复制

下载

// 类似SolidJS的响应式原理
const [state, setState] = createSignal(0);

// 编译时就知道这个effect依赖state
createEffect(() => {
  console.log(`The count is ${state()}`);
});

// 没有虚拟DOM,直接更新DOM
setState(5); // 只更新依赖这个状态的DOM
结语:状态管理的本质回归

经历了从Flux到原子化状态的完整演进,我们终于认识到:

状态管理的根本目标不是选择最强大的库,而是在可预测性、开发体验和性能之间找到最佳平衡点。

  • 对于简单应用: Zustand/Jotai提供了完美的简洁性

  • 对于复杂业务逻辑: XState的状态机带来了前所未有的确定性

  • 对于大型团队项目: Redux Toolkit依然提供最可靠的约束

  • 对于服务端状态: React Query/SWR是现代应用的标准选择

真正的智慧不是盲目追求最新技术,而是深刻理解每种方案背后的设计哲学,为特定场景选择最合适的工具。

记住:最好的状态管理,是让开发者几乎感受不到它的存在。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐