普通视图

发现新文章,点击刷新页面。
昨天 — 2026年2月24日首页

从 Recoil 的兴衰看前端状态管理的技术选型

作者 evle
2026年2月24日 16:13

从 Recoil 的兴衰看前端状态管理的技术选型

2023 年底,Meta 官方宣布 Recoil 进入“维护模式”,从它的兴衰历程中,我们看到了什么?

Recoil 的发展历程

2020 年:Recoil 横空出世

2020 年的一个下午,我正在 Twitter 上刷着动态,突然刷到一条让我想打开电脑蠢蠢欲动的动态 "Facebook 发布了 Recoil,一个实验性的状态管理库",状态管理在 React 生态里面一直是个工程负担,无论是团队角度从复杂工程的状态管理意识培养还是从开发者体验(DX)角度来看,React 状态管理一直像在等一个 “救世主”。

"Recoil 是一个实验性的状态管理库,为 React 提供了更好的状态管理体验。"

上面这是 Recoil 的宣传语,如果是2020年在写前端的同学肯定很熟悉当时的背景

当时的背景

  • Redux 虽然强大,但样板代码太多
  • Context API 性能问题明显,所有消费者都会重渲染
  • 社区渴望一个更简单、更现代的状态管理方案

Recoil 的核心优势

  1. 解决了 Context 的性能问题
    我只是想切换主题,为什么用户信息组件也要重渲染?应用越来越慢,每次状态变化都要重渲染几十个组件让我不得不拆分出非常多的 Context,有没有一种更简单的方式只订阅需要的状态。

  2. 比 Redux 更简单,减少了样板代码
    我只是想写一个计数器,为什么要写三个文件?action、reducer、dispatch...这些概念为什么这么抽象?这将层层传递(漏传) Props 的痛苦转变为一种新的痛苦。 有没有简单的状态管理方案?

  3. 原子化的状态管理理念
    工作台应用的复杂度完全取决于状态管理的复杂度,实际的业务逻辑并没有什么复杂的,反而我们在“技术”上花费大量时间建立开发者信心,这种 ROI 是经不起推敲的。 比如更新一个用户信息,我们 dispatch 一个 UPDATE_USER 类型的参数, 这时候心智负担是 A组件会重新渲染吗? B组件不应该渲染,但是渲染了。 C组件我也不知道是否会重新渲染。

  4. 官方背书
    既然是官方出的,那开发者生态自然会觉得这个方案非常可靠, 并且 TypeScript 支持优秀,我们下意识里面觉得是时候改变我们项目了。

2021-2022 年:快速成长期

Recoil 被广泛采用,成为 React 状态管理的热门选择之一。当时的成功案例和教程大量的涌现在互联网上 “Meta 内部项目开始使用”,"某知名开源项目也集成了 Recoil",生态的发展可是如火如荼,出现了 Recoil DevTools,与 React Router、React Query 等库集成良好。

在当时我们团队内部,我也开始推广 AtomSelector API 的使用,并对负责的项目进行状态管理改造。当时我比喻 Atom 就是一个保险柜:

  • 任何需要用钱的人都可以打开它
  • 任何人都可以往里面放钱或取钱
  • 保险箱里的钱变了,所有知道这个保险箱的人都会收到通知

假设我存了 100块到保险柜里

import { atom } from 'recoil';

// 创建一个 Atom,就像创建一个保险箱
const moneyState = atom({
  key: 'moneyState',      // 保险箱的名字(必须唯一)
  default: 100,           // 保险箱里的初始钱数
});

那么其他组件想交互我这个保险柜的状态像喝水一样简单

import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil';

// 在组件中使用
function Wallet() {
  // 1. 读取和写入(既能看钱,也能存钱取钱)
  const [money, setMoney] = useRecoilState(moneyState);
  
  // 2. 只读取(只能看钱,不能改)
  const money = useRecoilValue(moneyState);
  
  // 3. 只写入(只能改钱,不能看)
  const setMoney = useSetRecoilState(moneyState);
  
  return (
    <div>
      <p>我的钱:{money}元</p>
      <button onClick={() => setMoney(money + 10)}>存10元</button>
      <button onClick={() => setMoney(money - 10)}>取10元</button>
    </div>
  );
}

当然我们也可以使用与保险柜配套的智能计算器 selector 来与保险柜交互,他会自动管理依赖关系,举个例子,今天大A跌了,我们取出来的钱要 x0.8, 我们如果使用普通函数实现的话,组件渲染每次都会新建这个函数,每次都会重新计算,这会损耗性能。如果使用原生useCallback API 的话引入了手动管理依赖的心智负担。

import { selector } from 'recoil';

const doubleMoneyState = selector({
  key: 'newMoneyState',  // 计算器的名字
  get: ({ get }) => {          // 计算逻辑
    const money = get(moneyState);  // 从保险箱里读取钱数
    return money * 0.8;               // 跌了!
  },
});

那重复计算场景,根据多个状态计算场景,过滤或者排序状态场景都可以轻松通过 selector 解决了。但在真实的实际使用过程中,发现 Recoil 的学习曲线并不是那么平滑,虽然 atom 和 selector 可以让新人2天内上手,但是 atomFamily、selectorFamily 的使用负担,selector越写越复杂导致的性能问题,以及没有彻底改造异步状态为 waitForAll,而是通过 Promise.all 的 JS API 来组合管理异步状态, 都让我感觉这次改造不如“预期” 。

Recoil 一直标记为 “试验性” ,刚开始我可能只是觉得 新项目嘛,API 可能随时变更,高速迭代,一直朝着最好的方向发展,但是渐渐的发现。企业级应用很难陪跑试验性的项目,没有人愿意为技术的升级买单。

2023 年:宣布进入维护模式

Recoil 团队宣布不再积极开发新功能,进入维护模式。

官方声明

"Recoil 已经进入维护模式,我们将继续修复关键 bug,但不会开发新功能。我们建议用户考虑其他状态管理方案。"

从官方公开资料与社区的状态来看,我觉得 Recoil 的衰落有3个原因:

  1. 资源有限

    • Meta 团队资源有限,无法同时维护多个状态管理方案
    • 优先级调整,资源分配到其他更重要项目(AI时代的趋势)
    • 维护一个"实验性"库的成本效益比不高
  2. 竞争激烈

    • Zustand、Jotai(Recoil原作者) 等新方案更轻量、更简单
    • 这些方案提供了类似的功能,但学习曲线更平缓
    • 社区开始转向更活跃的方案
  3. 需求变化

    • 前端技术栈快速演进,Recoil 的设计可能不再是最优解
    • 新的范式(如 Signals)开始兴起
    • 社区对状态管理的需求发生了变化

我从开始的震惊,转变为理解,也在社区开始讨论迁移方案,也在翻阅大量迁移指南和对比实践。

这次技术选型的经验

不要盲目追求"官方"方案

其实很多项目选择 Recoil 的一个重要原因是:它是 Meta 官方方案

  • "Recoil 是 Facebook 官方的,肯定可靠"
  • "Meta 的技术团队很厉害,他们的方案一定最好"
  • "官方方案 = 最佳方案"

但是回过头我们才发现

  • 官方 ≠ 最适合我们的项目
  • 官方方案也可能被放弃
  • 官方方案的学习曲线可能更陡峭
  • 官方方案的更新频率可能不如社区方案

Recoil 在后期面临的一个重要问题是:社区反馈响应不够及时。 提交了 Issue,但几周都没有回复,新功能的 Roadmap 始终没有看到,可持续性不够。

虽然切换到 Recoil 产生的技术收益不如预期,并且完全掉到另一个坑里(停止维护),但是团队通过这次迁移,强化和实践了 Recoil 可复制的理念。 比如每个状态都有自己的 R&R, 避免不必要的渲染。即使在后面迁移到 Zustand 我们的状态关系依然没有大的变化,状态之间的依赖关系清晰,并且可以独立测试每个状态。以及将一些老代码的 Context 也做了原子化拆分。

状态管理的技术趋势

3.1 简约主义:少即是多

当前最明显的趋势是追求简约。Zustand 的下载量超过了 Recoil 和 Jotai 的总和,大小却只有几KB,这正是与社区开发者共情 “这么简单的东西,我要写那么复杂吗?”,2018 年 统治者地位的 Redux 写个计数器demo 4个文件几十行代码,现如今。Zustand 3行代码,1个文件,开发1天内就能上手 API,更少的代码意味着更少的出错机会,团队反馈在 PeerReveiw 时的信心也增加了许多。Zustand 它让 80% 的场景变得简单,同时让 20% 的复杂场景仍然可行。

const useStore = create((set) => ({
  count: 0,
  increment: () => set((state) => ({ count: state.count + 1 })),
}));

function Counter() {
  const { count, increment } = useStore();
  return <button onClick={increment}>{count}</button>;
}

3.2 按需选择

社区不再追求"大一统"解决方案,就像工具箱里有锤子、螺丝刀、扳手,我们不会用锤子去拧螺丝。而是根据场景选择:

场景 推荐方案 理由
小型应用 React Context + Hooks 无需额外依赖
中型应用 Zustand 简单高效
大型企业应用 Redux Toolkit 生态成熟,工具完善
复杂状态依赖 Jotai 原子化设计
响应式需求 MobX / Valtio 自动追踪依赖

3.3 渐进式平滑迁移

在实际开发中,我听过很多“XXX技术好,我们要拥抱新技术!”,除去迁移风险,开发交付的核心是业务价值,重构过去稳定的模块在业务上也许完全没有收益, 就算以前写的很垃圾,经过这么多涂涂改改它也很稳定。 那我们的技术洁癖应该是渐进式迁移, 我们引入新方案与旧方案并存,新的业务享受着新技术的便利性,老的方案享受着无变更的稳定性。

那渐进式迁移的前提是什么? 迁移复杂度评估。目前主流的前端状态管理库似乎都意识到了这些工程难题:

  • 概念差异越大,迁移成本越高。
  • 代码结构差异越大,重构工作量越大。
  • 依赖的中间件、工具越多,迁移越复杂。

从而像充电器一样在做类似的“标准”。我们从 Recoil 迁移到 Zustand 的复杂度很低,因为两者的概念类似。

后话

技术会过时,但理念是:永恒的。

❌
❌