Effector
一个响应式、类型安全、框架无关的 JavaScript 业务逻辑状态管理库。
Effector 是响应式、类型安全、框架无关的状态管理库,采用事件驱动和多存储架构。通过 Event、Store、Effect、Domain 四种基本单元构建业务逻辑,内置 Fork API 提供原生 SSR 支持,TypeScript 支持业界领先,被 Sber、Avito 等企业用于生产环境。
Effector
一个响应式、类型安全、框架无关的 JavaScript 业务逻辑状态管理库,以事件驱动架构和多存储设计为核心,提供约 10KB 的轻量体积与开箱即用的 TypeScript 支持,被 Sber、Avito、Aviasales、UNICEF、Semrush 等企业用于生产环境。
技术简介说明
Effector 是由 Anton Kosykh(GitHub: @zerobias)创建的响应式状态管理库,定位为"业务逻辑管理工具"而非传统意义上的"状态管理库"。其核心设计理念是将应用视为一组相互协作的"责任域"(responsibility scopes),通过事件(Event)、存储(Store)、效果(Effect)和域(Domain)四种基本单元来构建任意复杂度的业务逻辑。
与 Redux 的单一存储 + 不可变更新范式不同,Effector 采用多存储 + 事件驱动的架构:每个 Store 独立管理自己的数据片段,通过 Event 触发更新,Effect 封装副作用,sample 方法声明式地连接各单元形成响应式数据流。这种设计没有 Reducer、没有 Action Creator、没有 Middleware 链,也不使用 Proxy 或 class——只靠纯 JavaScript 函数实现所有能力。
Effector 在 TypeScript 支持方面处于业界领先水平,所有 API 均具备完整的类型推导能力。同时,它通过内置的 Fork API(fork、serialize、hydrate、scopeBind、allSettled)提供了原生 SSR 支持,配合 Babel/SWC 插件自动生成 Store ID(sid),确保服务端渲染时各请求间的状态隔离。
2025 年发布的 v23.4.0 "Atlas" 是最新的重要版本,带来了 HMR 支持、sample 命名增强、split 类型改进、scopeBind 多参数支持等关键特性。effector-react v23.3.0 已支持 React 19,effector-vue v23.1.0 新增了 Vue 3 Options API 支持。
尽管在 2025-2026 年间 Zustand、Jotai 等轻量级方案在新项目中快速增长,Effector 凭借其独特的事件驱动范式、强大的类型安全、优秀的 SSR 支持和精心设计的生态系统(patronum、farfetched、atomic-router 等),在复杂业务逻辑管理场景中保持着不可替代的地位。
基本信息
| 项目 | 内容 |
|---|---|
| 官网 | https://effector.dev/ |
| GitHub | https://github.com/effector/effector |
| License | MIT |
| 最新版本 | v23.4.4(npm,2025 年 9 月);GitHub 最新 release v23.4.1(2025 年 7 月 7 日) |
| 创建者 / 主要维护者 | Anton Kosykh(@zerobias) |
| 核心贡献者 | Anton Yurovskykh、Arsen-95 |
| GitHub Stars | ~4,800(2026 年 7 月) |
| npm 周下载量 | ~56K-62K(2026 年 7 月) |
| 框架支持 | 框架无关(React、Vue、Svelte、Solid、Node.js、Vanilla JS) |
| TypeScript | 原生支持,开箱即用的完整类型推导 |
| 首次发布 | 2019 年 |
| Bundle Size | ~10KB(gzip,支持 tree-shaking) |
| Changelog | https://changelog.effector.dev/ |
快速上手
安装
# npm
npm install effector
# pnpm
pnpm add effector
# yarn
yarn add effector如需框架绑定,需额外安装对应包:
# React 绑定
npm install effector effector-react
# Vue 绑定
npm install effector effector-vue
# Solid 绑定
npm install effector effector-solid
# Next.js 集成
npm install effector @effector/next注意:Effector 核心包是框架无关的,可以在 Node.js、浏览器、Web Worker 等任何 JavaScript 环境中运行。框架绑定包(
effector-react、effector-vue等)需根据使用的 UI 框架单独安装。
基础配置
React 项目配置
// babel.config.js — 配置 Babel 插件(SSR 必需)
module.exports = {
plugins: [
'effector/babel-plugin', // 自动生成 store sid,确保 SSR 状态隔离
],
}// app/model.ts — 业务逻辑层(与框架无关)
import { createStore, createEvent, createEffect, sample } from 'effector'
// 1. 定义 Event — "发生了什么"
export const todoAdded = createEvent<string>()
export const todoToggled = createEvent<number>()
export const todoFetchRequested = createEvent()
// 2. 定义 Effect — 处理副作用
export const fetchTodosFx = createEffect(async () => {
const response = await fetch('https://jsonplaceholder.typicode.com/todos?_limit=10')
if (!response.ok) throw new Error('网络请求失败')
return response.json() as Promise<Array<{ id: number; title: string; completed: boolean }>>
})
// 3. 定义 Store — 存储状态
export const $todos = createStore<Array<{ id: number; title: string; completed: boolean }>>([])
.on(todoAdded, (todos, title) => [
...todos,
{ id: Date.now(), title, completed: false },
])
.on(todoToggled, (todos, id) =>
todos.map(todo =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
)
)
.on(fetchTodosFx.done, (_, { result }) => result)
export const $isLoading = fetchTodosFx.pending
// 4. 使用 sample 连接逻辑
sample({
clock: todoFetchRequested,
target: fetchTodosFx,
})
// 派生 Store — 自动随源 Store 更新
export const $completedCount = $todos.map(todos =>
todos.filter(t => t.completed).length
)// main.tsx — 应用入口
import React from 'react'
import ReactDOM from 'react-dom/client'
import { Provider } from 'effector-react'
import { todoFetchRequested } from './app/model'
import { TodoList } from './components/TodoList'
// SSR 场景下使用 fork() 创建隔离 Scope
// const scope = fork()
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<TodoList />
</React.StrictMode>
)
// 触发初始数据加载
todoFetchRequested()最小示例
// counter.ts — 完整可用的计数器示例
import { createStore, createEvent, combine } from 'effector'
// Event:用户意图
const incremented = createEvent()
const decremented = createEvent()
const reset = createEvent()
// Store:应用状态
const $count = createStore(0)
.on(incremented, (state) => state + 1)
.on(decremented, (state) => state - 1)
.reset(reset)
// 派生 Store
const $doubled = $count.map((n) => n * 2)
const $isPositive = $count.map((n) => n > 0)
// 组合 Store
const $summary = combine(
{ count: $count, doubled: $doubled, isPositive: $isPositive },
)
// 订阅变化(适用于任何 JS 环境)
$summary.watch((state) => {
console.log('当前状态:', state)
// { count: 0, doubled: 0, isPositive: false }
})
// 触发事件
incremented() // count → 1
incremented() // count → 2
decremented() // count → 1
reset() // count → 0// Counter.tsx — React 组件绑定
import { useUnit } from 'effector-react'
import { $count, $doubled, incremented, decremented, reset } from './counter'
export function Counter() {
// useUnit 同时绑定 store 和 event
const count = useUnit($count)
const doubled = useUnit($doubled)
const [onIncrement, onDecrement, onReset] = useUnit([
incremented,
decremented,
reset,
])
return (
<div>
<h2>计数器: {count}</h2>
<p>双倍值: {doubled}</p>
<button onClick={onDecrement}>减少</button>
<button onClick={onIncrement}>增加</button>
<button onClick={onReset}>重置</button>
</div>
)
}核心概念与架构
核心架构设计
Effector 的架构基于响应式数据流和去中心化存储理念,通过四种基本单元(Event、Store、Effect、Domain)及其组合方法(sample、combine、forward 等)构建业务逻辑图:
┌──────────────────────────────────────────────────────────────┐
│ View 层(React / Vue / Svelte) │
│ useUnit() / useStore() / useList() — 读取 Store、触发 Event │
├──────────────────────────────────────────────────────────────┤
│ 框架绑定层 │
│ effector-react │ effector-vue │ effector-solid │ @effector/next│
├──────────────────────────────────────────────────────────────┤
│ Effector 核心层(框架无关) │
│ Event │ Store │ Effect │ Domain │ Scope │
│ sample │ combine │ forward │ guard │ split │ merge │ attach │
├──────────────────────────────────────────────────────────────┤
│ Fork API(SSR / 测试隔离) │
│ fork() │ serialize() │ hydrate() │ scopeBind() │ allSettled()│
├──────────────────────────────────────────────────────────────┤
│ 编译器插件层 │
│ @effector/babel-plugin │ @effector/swc-plugin │
│ (自动生成 sid、HMR 支持、forceScope 校验) │
└──────────────────────────────────────────────────────────────┘
核心概念
1. Event(事件)
Event 是 Effector 中最基础的单元,代表"某件事发生了"——一个可以携带载荷(payload)的信号。它是响应式数据流的入口点,任何状态变更都通过 Event 触发。
import { createEvent } from 'effector'
// 创建无载荷事件
const formSubmitted = createEvent()
// 创建带载荷事件(TypeScript 自动推导类型)
const userLoggedIn = createEvent<{ name: string; role: 'admin' | 'user' }>()
// 监听事件
formSubmitted.watch(() => console.log('表单已提交'))
userLoggedIn.watch((user) => console.log(`${user.name} 已登录`))
// 触发事件
formSubmitted()
userLoggedIn({ name: 'Alice', role: 'admin' })
// Event 变换
const userNameExtracted = userLoggedIn.map((user) => user.name)
// userNameExtracted 是一个新 Event,载荷为 string 类型
// Event 前置处理
const formSubmittedWithTimestamp = formSubmitted.prepend(
(data: { title: string }) => ({ ...data, timestamp: Date.now() })
)2. Store(存储)
Store 是响应式的数据容器,持有应用的当前状态值。每个 Store 独立管理自己的数据片段,通过 .on() 方法订阅 Event 来更新状态。Store 的值不可直接修改,必须通过 Event 驱动更新。
import { createStore, createEvent, combine } from 'effector'
const nameChanged = createEvent<string>()
const ageChanged = createEvent<number>()
// 创建 Store,必须提供默认值(不能为 undefined)
const $name = createStore('匿名')
.on(nameChanged, (_, newName) => newName)
const $age = createStore(0)
.on(ageChanged, (_, newAge) => newAge)
// 派生 Store — 自动随源 Store 变化而重新计算
const $greeting = $name.map((name) => `你好, ${name}!`)
// 组合多个 Store
const $profile = combine({
name: $name,
age: $age,
greeting: $greeting,
})
// 读取 Store 当前值(在非响应式上下文中)
console.log($name.getState()) // '匿名'
// 订阅 Store 变化
$profile.watch((profile) => {
console.log('Profile updated:', profile)
})
// 触发更新
nameChanged('Alice') // $name → 'Alice', $greeting → '你好, Alice!'
ageChanged(25) // $age → 25, $profile 自动更新关键特性:
- Store 忽略重复值更新(相同值不会触发订阅者)
.on()中的 reducer 必须是纯函数- 派生 Store 通过
.map()创建,自动追踪依赖
3. Effect(效果)
Effect 是处理副作用(尤其是异步操作)的容器。它内置了 pending、done、fail、finally 等状态 Event,使异步流程的管理变得声明式且类型安全。
import { createEffect, createStore, sample } from 'effector'
// 创建异步 Effect
const fetchUserFx = createEffect(async (userId: string) => {
const response = await fetch(`/api/users/${userId}`)
if (!response.ok) throw new Error('用户不存在')
return response.json() as Promise<{ id: string; name: string; email: string }>
})
// Effect 内置的状态 Store 和 Event
const $isLoading = fetchUserFx.pending // Store<boolean> — 是否正在执行
const $error = createStore<string | null>(null)
.on(fetchUserFx.fail, (_, { error }) => error.message)
.on(fetchUserFx.done, () => null)
fetchUserFx.done.watch(({ params, result }) => {
console.log(`用户 ${params} 获取成功:`, result)
})
fetchUserFx.fail.watch(({ params, error }) => {
console.error(`用户 ${params} 获取失败:`, error)
})
fetchUserFx.finally.watch(({ status }) => {
console.log(`执行结束, 状态: ${status}`) // 'done' | 'fail'
})
// Effect 可设置并发限制
const limitedFx = createEffect({
handler: async (id: string) => { /* ... */ },
limit: 3, // 最多同时执行 3 个
})4. Domain(域)
Domain 是逻辑单元的命名空间,用于将相关的 Event、Store、Effect 组织在一起。它在 SSR 和测试场景中特别有用——可以通过 Domain 统一重置所有内部 Store。
import { createDomain } from 'effector'
// 创建域
const authDomain = createDomain()
// 在域内创建单元
const $user = authDomain.createStore<{ name: string } | null>(null)
const login = authDomain.createEvent<{ email: string; password: string }>()
const logout = authDomain.createEvent()
const loginFx = authDomain.createEffect(
async (credentials: { email: string; password: string }) => {
const res = await fetch('/api/auth/login', {
method: 'POST',
body: JSON.stringify(credentials),
})
return res.json()
}
)
// 域内逻辑连接
$user.on(loginFx.done, (_, { result }) => result)
.on(logout, () => null)
// 域可以嵌套
const appDomain = createDomain()
const uiDomain = appDomain.createDomain()
// 域级别统一监听
authDomain.onCreateStore((store) => {
// 对所有域内 Store 应用统一的日志/重置逻辑
store.watch((value) => console.log('[auth] store updated:', value))
})5. Scope(作用域)— SSR 隔离
Scope 是 Effector 为 SSR 提供的隔离执行上下文。每个服务端请求拥有独立的 Scope,确保请求间不会相互污染状态。
import { fork, serialize, allSettled, scopeBind } from 'effector'
// 服务端:为每个请求创建独立 Scope
async function handleRequest(req: Request) {
const scope = fork({
values: {
// 可设置 Store 的初始值(如从 cookie 读取用户信息)
},
handlers: {
// 可覆盖 Effect 的处理器(用于测试或 mock)
},
})
// 在 Scope 中执行所有操作
await allSettled(fetchUserFx, { scope, params: 'user-123' })
// 序列化 Scope 状态,发送到客户端
const state = serialize(scope)
return {
html: renderApp(),
initialState: state, // 注入到 HTML 中供客户端 hydrate
}
}
// 客户端:hydrate 服务端状态
import { hydrate } from 'effector'
hydrate({
values: window.__INITIAL_STATE__,
})6. sample — 声明式逻辑连接
sample 是 Effector 中将各单元连接成完整数据流的核心方法。它从 clock(时钟/触发器)接收信号,可选地从 source(源 Store)读取数据,经过 fn 纯函数变换后,发送给 target(目标单元)。
import { sample, createStore, createEvent, createEffect } from 'effector'
const $cart = createStore<Array<{ id: number; qty: number }>>([])
const $userId = createStore<string>('')
const checkoutClicked = createEvent()
const submitOrderFx = createEffect(
async (data: { items: Array<{ id: number; qty: number }>; userId: string }) => {
// 提交订单...
}
)
// 从 clock 触发,从 source 读取数据,组合后发送给 target
sample({
clock: checkoutClicked, // 触发信号
source: { cart: $cart, userId: $userId }, // 读取多个 Store
fn: ({ cart, userId }) => ({ // 纯函数变换
items: cart,
userId,
}),
target: submitOrderFx, // 目标 Effect
})
// 条件触发(guard 模式)
const $isAdmin = createStore(true)
sample({
clock: checkoutClicked,
source: $cart,
filter: $isAdmin, // 仅当 $isAdmin 为 true 时才触发
target: submitOrderFx,
})核心特性
1. 一流的 TypeScript 支持
Effector 的所有 API 均具备完整的类型推导能力,无需额外类型注解即可确保端到端类型安全。sample 的 fn 参数类型、createEffect 的 params/result 类型、Store 的泛型等均自动推导。
import { createStore, createEvent, sample, createEffect } from 'effector'
// Event 载荷类型自动推导
const searchQueryChanged = createEvent<string>()
// Store 类型自动推导
const $query = createStore('')
.on(searchQueryChanged, (_, q) => q) // $query: Store<string>
// Effect 的 params 和 result 类型自动推导
const searchFx = createEffect(async (query: string) => {
const res = await fetch(`/api/search?q=${query}`)
return res.json() as Promise<Array<{ id: number; title: string }>>
})
// searchFx.done 类型为 { params: string; result: Array<...> }
// searchFx.fail 类型为 { params: string; error: Error }
// sample 的 fn 返回类型自动校验与推导
sample({
clock: searchQueryChanged,
target: searchFx, // 类型校验:clock 的载荷必须与 Effect 的 params 兼容
})2. 框架无关设计
Effector 核心完全独立于任何 UI 框架。业务逻辑可以在 React、Vue、Svelte、Solid、Node.js 甚至 Web Worker 中共享同一套代码,只需更换框架绑定层。
// 框架无关的业务逻辑
import { createStore, createEvent } from 'effector'
export const $count = createStore(0)
export const increment = createEvent()
$count.on(increment, (n) => n + 1)
// React 中使用
import { useUnit } from 'effector-react'
function ReactCounter() {
const [count, onIncrement] = useUnit([$count, increment])
return <button onClick={onIncrement}>{count}</button>
}
// Vue 中使用
import { useUnit } from 'effector-vue'
// Vue 组件中同样可以使用 $count 和 increment3. 内置 SSR 支持(Fork API)
通过 fork、serialize、hydrate、scopeBind、allSettled 五个 API,Effector 提供了完整的 SSR 状态隔离方案,每个服务端请求拥有独立的执行上下文,无需全局状态清理。
4. 自动依赖追踪与响应式更新
派生 Store 通过 .map() 和 combine() 创建时,Effector 自动建立依赖关系图。当源 Store 更新时,所有下游派生 Store 自动重新计算,无需手动订阅或记忆化配置。
const $firstName = createStore('John')
const $lastName = createStore('Doe')
// 自动追踪 $firstName 和 $lastName 的变化
const $fullName = combine($firstName, $lastName, (first, last) => `${first} ${last}`)
// $firstName 或 $lastName 变化时,$fullName 自动更新5. 编译器插件(Babel / SWC)
官方提供 Babel 和 SWC 插件,自动生成 Store 的唯一标识符(sid),这是 SSR 状态下水(hydration)正确工作的前提。同时支持 HMR(热模块替换)、forceScope 强制 Scope 校验等高级能力。
6. 轻量级与 Tree-shaking
Effector 核心 gzip 后约 10KB,支持完整的 tree-shaking。未使用的 API 不会被打入最终 bundle。相比 Redux + Immer + Reselect 的组合(~14KB),Effector 以更小的体积提供了同等甚至更强的能力。
7. 声明式副作用管理
Effect 作为一等公民,将副作用(API 调用、文件操作等)封装为可测试、可组合的单元。内置的 pending、done、fail 状态消除了手写 loading/error 状态的需求。
8. 丰富的数据流组合方法
Effector 提供了一系列声明式的逻辑连接方法:
| 方法 | 用途 |
|---|---|
sample | 核心连接方法,从 clock 触发、source 读取、fn 变换后发往 target |
combine | 将多个 Store 合并为一个派生 Store |
forward | 直接将一个 Event 的触发的转发给另一个 Event/Store |
guard | 条件过滤,仅当条件满足时才传递数据 |
split | 按条件将 Event 分流到不同的目标 |
merge | 合并多个 Event 为一个 |
attach | 将 Effect 绑定到 Store 状态 |
9. 可测试性
通过 fork() 创建隔离的测试环境,可以 mock Effect 的处理器、设置 Store 初始值,然后使用 allSettled 驱动测试场景,最后用 scope.getState() 验证最终状态。
import { fork, allSettled } from 'effector'
import { $user, loginFx, loginButtonClicked } from './model'
test('用户登录成功后更新用户信息', async () => {
const scope = fork({
handlers: {
// Mock Effect,不发起真实请求
[loginFx]: async (credentials) => ({
id: '1',
name: credentials.email.split('@')[0],
}),
},
})
await allSettled(loginButtonClicked, {
scope,
params: { email: '[email protected]', password: '123' },
})
expect(scope.getState($user)).toEqual({ id: '1', name: 'alice' })
})10. 无 Proxy、无 Class
Effector 不使用 Proxy(无隐式魔法)、不使用 class(无 this 绑定问题),所有逻辑通过纯函数实现。这使得代码行为可预测,调试更简单,也避免了 MobX 等库中常见的"魔法"陷阱。
生态图
Effector 生态
├── 核心包
│ ├── effector —— 核心库(框架无关)
│ ├── effector-react —— React 绑定(useUnit、useList、useStoreMap)
│ ├── effector-vue —— Vue 绑定(Composition API + Options API)
│ ├── effector-solid —— Solid.js 绑定
│ └── @effector/next —— Next.js 集成(SSR/SSG 支持)
│
├── 官方工具
│ ├── patronum —— 官方工具库(delay、debounce、throttle、conditional 等)
│ ├── @effector/babel-plugin —— Babel 编译器插件(sid 生成、HMR、forceScope)
│ ├── @effector/swc-plugin —— SWC 编译器插件
│ ├── @effector/reflect —— React HOC redesigned 连接组件与 Effector
│ ├── effector-logger —— Store/Event/Effect 日志工具
│ ├── @effector/redux-devtools-adapter —— Redux DevTools 适配器
│ └── eslint-plugin-effector —— ESLint 规则(强制最佳实践)
│
├── 数据获取
│ └── farfetched —— 高级数据获取库(缓存、乐观更新、重试)
│
├── 路由
│ └── atomic-router —— 框架无关的声明式路由
│
├── 存储持久化
│ ├── effector-storage —— 同步 Store 与各种存储后端(localStorage、IndexedDB、Cookie 等)
│ └── effector-localstorage —— localStorage 专用同步模块
│
├── 表单管理
│ ├── effector-forms —— 声明式表单配置
│ ├── effector-react-form —— React 表单绑定
│ ├── efform —— 高质量 DX 的表单管理
│ ├── effector-reform —— 组合式表单
│ ├── filledout —— 集成 yup 验证的表单管理
│ └── effector-final-form —— Final Form 绑定
│
├── 国际化
│ └── @withease/i18next —— i18next 绑定
│
├── 工具集
│ ├── @withease/web-api —— Web API 绑定(网络状态、标签可见性等)
│ ├── @withease/factories —— 工厂模式辅助工具
│ ├── @withease/redux —— Redux → Effector 迁移辅助
│ ├── effector-hotkey —— 快捷键管理
│ ├── effector-undo —— 撤销/重做功能
│ └── effector-history —— Store 历史记录追踪
│
├── 渲染引擎
│ └── forest —— 响应式 UI 渲染引擎(实验性)
│
└── 开发工具
├── effector-inspector —— Store 检查器(类似 DevTools)
└── @effector/swc-plugin —— SWC 编译支持
适用场景
1. 复杂业务逻辑的企业级应用
Effector 的事件驱动架构和多存储设计天然适合处理复杂的业务规则、多模块联动的状态逻辑。Domain 提供的命名空间机制使大型项目的代码组织更加清晰。被 Sber(俄罗斯联邦储蓄银行)、Raiffeisen Bank 等金融机构采用。
2. 需要严格类型安全的项目
Effector 的 TypeScript 支持是所有状态管理库中最优秀的之一。所有 API 的输入输出均具备完整类型推导,sample 的 fn 类型、Effect 的 params/result 类型在编译时即可捕获不匹配错误。适合对类型安全有高要求的 TypeScript 项目。
3. 服务端渲染(SSR)应用
Effector 的 Fork API 提供了原生的 SSR 状态隔离方案,每个请求拥有独立的 Scope,无需全局状态清理或手动序列化。配合 @effector/next 可在 Next.js 中实现开箱即用的 SSR 状态管理。
4. 多框架 / 框架迁移场景
核心库完全框架无关,业务逻辑可在 React、Vue、Svelte 等框架间共享。对于使用多框架的 Monorepo 项目或计划迁移框架的团队,Effector 提供了最低迁移成本的状态管理方案。
5. 需要精细化状态测试的项目
通过 fork() 创建隔离测试环境、allSettled() 驱动测试场景、scope.getState() 验证最终状态,Effector 的测试方案比 Redux 的 reducer 测试更贴近真实使用场景,且支持异步 Effect 的完整生命周期测试。
6. 对包体积敏感的应用
Effector gzip 后约 10KB,支持完整的 tree-shaking,且不需要额外的 Immer(Redux Toolkit 依赖)或 Proxy polyfill(MobX 依赖)。在移动端 H5、嵌入式组件等对 bundle size 敏感的场景有明显优势。
7. 事件驱动 / 实时系统
Effector 的事件驱动范式天然适合处理 WebSocket 消息流、实时通知、多用户协作等场景。Event 可以作为外部事件的统一入口,通过 sample 路由到对应的处理逻辑。
开发与工程化
TypeScript 支持
Effector 的 TypeScript 支持是其核心竞争力之一,所有 API 均提供完整的类型推导:
import { createStore, createEvent, createEffect, sample, combine } from 'effector'
// Event 类型推导
const userCreated = createEvent<{ id: string; name: string }>()
// Store 类型推导
const $users = createStore<Array<{ id: string; name: string }>>([])
.on(userCreated, (users, user) => [...users, user])
// Effect 类型推导 — params 和 result 自动推导
const fetchUserFx = createEffect(
async (params: { id: string; includeDetails?: boolean }) => {
const res = await fetch(`/api/users/${params.id}`)
return res.json() as Promise<{ id: string; name: string; email: string }>
}
)
// fetchUserFx.done: Event<{ params: { id: string; includeDetails?: boolean }; result: { id: string; name: string; email: string } }>
// fetchUserFx.fail: Event<{ params: ...; error: Error }>
// sample 类型推导 — fn 的返回类型必须与 target 的输入类型匹配
sample({
clock: userCreated,
fn: (user) => user.id, // fn 返回 string
target: fetchUserFx, // ❌ 类型错误!fetchUserFx 需要 { id: string; includeDetails?: boolean }
})
// 正确写法
sample({
clock: userCreated,
fn: (user) => ({ id: user.id }), // fn 返回 { id: string },匹配 Effect 的 params
target: fetchUserFx,
})
// combine 类型推导
const $userCount = $users.map((users) => users.length) // Store<number>
const $summary = combine({ users: $users, count: $userCount })
// $summary: Store<{ users: Array<...>; count: number }>ESLint 集成
pnpm add -D eslint-plugin-effector{
"plugins": ["effector"],
"rules": {
"effector/no-watch": "warn",
"effector/no-getState": "warn",
"effector/enforce-effect-handler-types": "error"
}
}测试策略
// model.test.ts — Effector 单元测试
import { fork, allSettled } from 'effector'
import {
$todos,
$isLoading,
todoAdded,
todoToggled,
fetchTodosFx,
todoFetchRequested,
} from './model'
describe('Todo Model', () => {
test('todoAdded 应添加新待办', async () => {
const scope = fork()
await allSettled(todoAdded, { scope, params: '学习 Effector' })
const todos = scope.getState($todos)
expect(todos).toHaveLength(1)
expect(todos[0].title).toBe('学习 Effector')
expect(todos[0].completed).toBe(false)
})
test('todoToggled 应切换完成状态', async () => {
const scope = fork({
values: [
[$todos, [{ id: 1, title: '测试', completed: false }]],
],
})
await allSettled(todoToggled, { scope, params: 1 })
expect(scope.getState($todos)[0].completed).toBe(true)
})
test('fetchTodosFx 成功后应更新列表', async () => {
const mockTodos = [{ id: 1, title: 'Mock Todo', completed: false }]
const scope = fork({
handlers: {
[fetchTodosFx]: async () => mockTodos,
},
})
await allSettled(todoFetchRequested, { scope })
expect(scope.getState($todos)).toEqual(mockTodos)
expect(scope.getState($isLoading)).toBe(false)
})
})编译器插件配置
// babel.config.js
module.exports = {
plugins: [
[
'effector/babel-plugin',
{
// 自动生成 sid(SSR 必需)
addSIDs: true,
// 开发环境启用 HMR 支持
hotReload: process.env.NODE_ENV === 'development',
// 强制在 Scope 内执行(防止 SSR 中的全局状态污染)
forceScope: process.env.NODE_ENV === 'production',
// 转换旧版 Domain 方法(迁移辅助)
transformLegacyDomainMethods: true,
},
],
],
}生产环境配置建议
// store.ts — 生产环境配置
import { createStore, createEvent, createEffect } from 'effector'
// 1. 使用 Babel/SWC 插件自动生成 sid
// 2. 生产环境启用 forceScope 防止全局状态泄露
// 3. 配合 effector-storage 做状态持久化
import { persist } from 'effector-storage/local'
const $theme = createStore<'light' | 'dark'>('light')
persist({ store: $theme, key: 'app-theme' })
// 4. 使用 patronum 提供的生产级工具
import { debounce, throttle } from 'patronum'
const searchInput = createEvent<string>()
const $searchQuery = createStore('')
.on(searchInput, (_, q) => q)
// 防抖处理搜索输入
debounce({ source: searchInput, timeout: 300, target: $searchQuery })性能与安全
性能优化
-
自动依赖追踪:派生 Store 通过
.map()和combine()创建时,Effector 自动建立精确的依赖关系图,只有当直接依赖变化时才触发重新计算,避免不必要的计算开销。 -
Store 去重更新:Store 自动忽略相同值的更新(通过
===比较),防止下游订阅者被无意义地触发。 -
Tree-shaking 支持:Effector 核心支持完整的 tree-shaking,未使用的 API(如
combine、split、guard)不会被打入最终 bundle。 -
轻量级订阅模型:Effector 内部使用高效的事件总线机制,Store 订阅和通知的开销极低,适合高频更新场景。
-
Effect 并发控制:内置
limit选项限制 Effect 的并发执行数量,防止大量异步请求同时执行导致的性能问题。
import { debounce, throttle } from 'patronum'
// 防抖:用户停止输入 300ms 后才触发搜索
debounce({
source: searchInput,
timeout: 300,
target: searchFx,
})
// 节流:滚动事件每 100ms 最多处理一次
throttle({
source: scrollEvent,
limit: 100,
target: updateScrollPosition,
})安全考量
-
无 Proxy 无 eval:Effector 不使用 Proxy 或
eval,避免了原型污染和代码注入风险。所有逻辑通过纯函数实现,行为完全可预测。 -
SSR 状态隔离:通过
fork()创建的 Scope 确保不同请求间的状态完全隔离,防止服务端数据泄露到错误的客户端。 -
forceScope 模式:配合 Babel 插件的
forceScope选项,可在开发阶段强制所有 Store 操作必须在 Scope 内执行,防止意外使用全局状态。 -
序列化安全检查:
serialize()只导出可序列化的 Store 值,自动过滤函数、Symbol 等不可序列化类型,防止持久化或传输过程中出现异常。 -
Effect 错误处理:Effect 的
fail和finally事件确保所有副作用的错误都被显式捕获,不会产生未处理的 Promise rejection。 -
XSS 防护:与所有状态管理库一样,渲染来自 Store 的用户输入数据时必须在视图层做 HTML 转义。Effector 本身不处理视图渲染,XSS 防护责任在 UI 框架层。
技术对比
Effector vs Redux vs MobX vs XState vs Zustand vs Jotai
| 特性 | Effector | Redux Toolkit | MobX | XState | Zustand | Jotai |
|---|---|---|---|---|---|---|
| 设计理念 | 事件驱动,多存储,响应式 | 单向数据流,单一存储,不可变 | 响应式,可变对象,OOP | 有限状态机,状态图 | 单一 store,Hook 化 | 原子化,自底向上 |
| Bundle Size | ~10KB(gzip) | ~14KB(gzip) | ~16KB(gzip) | ~13KB(gzip) | ~3KB(gzip) | ~4KB(gzip) |
| 学习曲线 | 中-高 | 中-高 | 中 | 高 | 低 | 低-中 |
| TypeScript 支持 | ⭐⭐⭐ 一流 | ⭐⭐⭐ 优秀 | ⭐⭐ 良好 | ⭐⭐ 良好 | ⭐⭐ 良好 | ⭐⭐ 良好 |
| Boilerplate | 少 | 中等(RTK 大幅减少) | 少 | 多(状态定义冗长) | 极少 | 少 |
| 框架绑定 | React/Vue/Svelte/Solid | React 为主 | React/Vue/框架无关 | React/Vue/Svelte | React/Vue/框架无关 | React 为主 |
| SSR 支持 | ⭐⭐⭐ 原生 Fork API | ⭐⭐ 需手动配置 | ⭐⭐ 需手动配置 | ⭐ 有限 | ⭐⭐ 需配合外部方案 | ⭐ 有限 |
| DevTools | ⭐⭐ Inspector + Redux 适配器 | ⭐⭐⭐ 最强(时间旅行) | ⭐⭐ MobX DevTools | ⭐⭐⭐ 可视化状态机 | ⭐⭐ 通过中间件 | ⭐ React DevTools |
| 数据获取 | farfetched(生态库) | ⭐⭐⭐ RTK Query 内置 | 需自行集成 | 非核心关注点 | 需配合 TanStack Query | 需配合 React Query |
| 不可变更新 | 纯函数 reducer | ⭐ Immer 自动处理 | 直接可变 | 不可变状态转换 | 手动(可选 Immer) | 不适用 |
| 异步处理 | ⭐⭐⭐ Effect 一等公民 | ⭐⭐ createAsyncThunk | action 内置 | ⭐⭐⭐ 核心能力 | 需配合外部库 | 需配合 Suspense |
| 社区规模(2026) | 小众但增长中 | ⭐⭐⭐ 最大 | ⭐⭐ 成熟 | ⭐⭐ 增长中 | ⭐⭐⭐ 新项目首选 | ⭐⭐ 增长中 |
| npm 周下载量 | ~60K | ~20.6M | ~1.5M | ~800K | ~5.8M | ~2.2M |
| GitHub Stars | ~4.8K | ~75K+(含 Redux) | ~28K | ~28K | ~40K+ | ~18K |
| 适用规模 | 中-大型 | 大-超大型 | 中-大型 | 复杂流程/状态机 | 小-中型 | 中-大型 |
| 核心差异 | 事件驱动 + 类型安全 | 单一存储 + DevTools | 可变响应式 + OOP | 状态机 + 可视化 | 极简 + 轻量 | 原子化 + 精细 |
选型建议
- 选 Effector:需要严格类型安全、复杂业务逻辑管理、多框架共享、原生 SSR 隔离、事件驱动架构的团队;对 TypeScript 体验有高要求;不介意较小的社区和较陡的学习曲线
- 选 Redux Toolkit:需要 DevTools 时间旅行调试、已有 Redux 基础、大型企业级项目、需要内置数据获取(RTK Query)
- 选 MobX:偏好 OOP 风格、复杂领域模型、自动依赖追踪、需要最小样板代码
- 选 XState:需要精确建模复杂状态转换(如支付流程、多步表单)、需要可视化状态图、状态机思维
- 选 Zustand:新项目首选、追求极简 API、小到中型项目、最快上手
- 选 Jotai:复杂派生状态、原子化精细控制、Suspense 深度集成
最佳实践
1. 按责任域组织代码
// 推荐:每个功能域独立目录,包含 model(逻辑)和 UI
// features/auth/
// ├── model/
// │ ├── index.ts — 导出所有单元
// │ ├── stores.ts — Store 定义
// │ ├── events.ts — Event 定义
// │ ├── effects.ts — Effect 定义
// │ └── connections.ts — sample/forward 连接逻辑
// ├── ui/
// │ ├── LoginForm.tsx
// │ └── UserMenu.tsx
// └── index.ts — 统一导出
// features/auth/model/events.ts
import { createEvent } from 'effector'
export const loginButtonClicked = createEvent<{ email: string; password: string }>()
export const logoutClicked = createEvent()
// features/auth/model/effects.ts
import { createEffect } from 'effector'
export const loginFx = createEffect(
async (credentials: { email: string; password: string }) => {
const res = await fetch('/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(credentials),
})
if (!res.ok) throw new Error('登录失败')
return res.json()
}
)
// features/auth/model/connections.ts
import { sample } from 'effector'
import { loginButtonClicked } from './events'
import { loginFx } from './effects'
import { $user, $error } from './stores'
// 连接:点击登录 → 调用 Effect → 更新 Store
sample({ clock: loginButtonClicked, target: loginFx })
$user.on(loginFx.done, (_, { result }) => result)
$error.on(loginFx.fail, (_, { error }) => error.message)
.on(loginFx.done, () => null)2. 使用 Domain 管理 Store 重置
import { createDomain } from 'effector'
const formDomain = createDomain()
// 域内所有 Store 可在测试前统一重置
const $email = formDomain.createStore('')
const $password = formDomain.createStore('')
const $error = formDomain.createStore<string | null>(null)
// 创建重置事件
const resetForm = createEvent()
// 通过 Domain 的 onCreateStore 统一注册重置逻辑
formDomain.onCreateStore((store) => {
store.reset(resetForm)
})3. 使用 patronum 提供的生产级工具
import {
delay,
debounce,
throttle,
previous,
condition,
spread,
pending,
inFlight,
time,
} from 'patronum'
// 防抖搜索
debounce({ source: searchInput, timeout: 300, target: searchFx })
// 节流滚动事件
throttle({ source: scrollEvent, limit: 100, target: updatePosition })
// 追踪 Store 的上一个值
const $previousCount = previous($count)
// 条件路由
condition({
source: $formStatus,
if: (status) => status === 'valid',
then: submitFx,
else: showValidationError,
})
// 跟踪 Effect 的并发执行数量
const $activeRequests = inFlight(fetchUserFx, fetchPostsFx)4. SSR 场景正确使用 Scope
// 服务端入口
import { fork, allSettled, serialize } from 'effector'
import { $user, fetchUserFx, userIdReceived } from './model'
export async function renderPage(req: Request) {
// 每个请求创建独立 Scope
const scope = fork()
// 在 Scope 内执行所有操作
await allSettled(userIdReceived, { scope, params: req.params.userId })
await allSettled(fetchUserFx, { scope })
// 序列化状态
const initialState = serialize(scope)
return { html: renderToString(<App />), initialState }
}
// ❌ 避免:在 SSR 中直接触发事件(会污染全局状态)
// userIdReceived('user-123') // 危险!所有请求共享同一状态
// ✅ 正确:始终通过 Scope 隔离
// await allSettled(userIdReceived, { scope, params: 'user-123' })5. 避免在 Store 中存储可序列化外的数据
// ❌ 避免:在 Store 中存储函数、class 实例、DOM 引用
const $handler = createStore<() => void>(() => {}) // 无法序列化,SSR 会出错
// ✅ 推荐:只存储可序列化的纯数据
const $config = createStore({ apiUrl: '/api', timeout: 5000 })技术局限与边界
1. 社区规模较小
Effector 的 GitHub Stars(~4.8K)和 npm 周下载量(~60K)远小于 Redux(75K+ Stars, 20M+ 下载)和 Zustand(40K+ Stars, 5.8M 下载)。这意味着:
- Stack Overflow 上的英文问答较少
- 第三方教程和博客文章有限
- 招聘市场上 Effector 技能需求极低
- 遇到问题时更依赖官方文档和社区(主要是俄语社区)
2. 学习曲线较陡
Effector 的事件驱动范式与主流状态管理库(Redux 的单向数据流、MobX 的可变响应式)差异较大。开发者需要理解 Event、Store、Effect、Domain、Scope、sample 等一套全新概念,sample 的多种配置模式(clock + source + fn + target、filter、guard 等)也有较高的认知成本。
3. 文档和英文资源不足
虽然官方文档质量较高,但相比 Redux 和 Zustand 的文档丰富度仍有差距。大量社区文章、教程和讨论集中在俄语社区(如 Habr、Telegram 群组),英文资源的匮乏增加了非俄语开发者的学习成本。
4. DevTools 生态不够成熟
Effector Inspector 功能相比 Redux DevTools 仍有明显差距,不支持时间旅行调试、action 回放、state diff 对比等高级功能。虽然有 @effector/redux-devtools-adapter 可以接入 Redux DevTools,但体验不如原生集成。
5. Babel/SWC 插件依赖
SSR 场景下必须配置 Babel 或 SWC 插件来自动生成 Store 的 sid,否则 serialize/hydrate 无法正确工作。这增加了项目配置的复杂度,且在与某些构建工具(如非标准的 bundler)集成时可能遇到问题。
6. 与 React Server Components 的适配
Effector 的状态管理模型基于客户端运行时(Store、Event 等),在 React Server Components(RSC)架构下只能在 Client Components 中使用。与 Next.js App Router 的深度集成需要 @effector/next 的额外适配层,且 RSC 场景下 Effector 的价值有所降低。
7. 生态系统维护者有限
Effector 生态中的许多包(patronum、farfetched、atomic-router 等)由核心团队的少数成员维护,而非广泛的社区贡献。如果核心维护者减少投入,整个生态的可持续发展面临风险。
8. 行业认知度低
在 2025 年 State of React 调查中,Effector 的知名度远低于 Zustand、Redux Toolkit、Jotai、TanStack Query 等方案。多数 2025-2026 年的"状态管理库推荐"文章要么不提 Effector,要么将其列为小众选项。这意味着团队选择 Effector 可能面临新成员入职时的额外培训成本。
学习资源
官方资源
- Effector 官方文档 — 最权威的参考,包含核心概念、API 文档和指南
- Effector Changelog — 详细的版本更新记录
- Effector's Beginner Guide — 官方入门指南
- Effector GitHub 讨论区 — 架构模式和最佳实践讨论
- Effector Awesome List — 社区精选资源列表
文章与教程
- Effector — State Manager You Should Give a Try — 创建者 Anton Kosykh 的深度介绍
- Modern State Management in React with Effector (2025) — 2025 年最新 React + Effector 实战
- Why do I choose Effector instead of Redux or MobX? — 与 Redux/MobX 的对比分析
- Effector: we need to go deeper — 深入理解 Effector 内部机制
- Create Your Effector-like State Manager — 从零实现类 Effector 状态管理器
视频教程
- TypeScript for React and Effector From Beginners to Masters — TypeScript + Effector 完整教程
- Effector — Business Logic with Ease (Conference Talk) — 官方演讲合集
社区
- Effector Telegram(英语) — 英语社区讨论
- Effector Telegram(俄语) — 俄语社区(最活跃)
- Effector Twitter/X — 官方社交媒体
- Effector Open Collective — 赞助与支持
中文资源
- Effector 官方文档(部分中文翻译) — 部分中文文档
- 搜索"Effector 状态管理"可在掘金、知乎等平台找到部分中文介绍文章
2026 年现状
版本状态
截至 2026 年 7 月:
| 版本 | 发布日期 | 关键特性 |
|---|---|---|
| v23.0.0 | 2024 年 | 编译器插件重构,性能优化 |
| v23.2.0 | 2025 年 4 月 | combine 禁止非法 key、Effect 类型改进 |
| v23.3.0 | 2025 年 2 月(核心)/ 12 月(React 绑定) | 错误信息增强、scopeBind 多参数、React 19 支持 |
| v23.4.0 "Atlas" | 2025 年 7 月 4 日 | HMR 支持、sample 命名增强、split 类型改进、tagged template 工厂 |
| v23.4.1 | 2025 年 7 月 7 日 | 修复 sample 无插件时的 name 支持 |
| v23.4.4 | 2025 年 9 月 | 当前 npm 最新版本(补丁修复) |
关键动态
-
Atlas 23.4 是重要里程碑:v23.4.0 代号"Atlas",引入了期待已久的 HMR(热模块替换)支持、Babel/SWC 插件的
transformLegacyDomainMethods选项、forceScope增强、以及sample的name属性改善调试体验。 -
React 19 完全支持:
effector-reactv23.3.0(2025 年 12 月)已添加 React 19 支持,确保与最新 React 版本兼容。 -
Vue 生态持续增强:
effector-vuev23.1.0 新增了 Vue 3 Options API 支持和useVModel绑定改进,扩大了在 Vue 社区的适用范围。 -
社区仍属小众但稳定:Effector 的 npm 下载量维持在 ~60K/周,没有爆发式增长,但也没有衰退。其核心用户群(主要集中在俄罗斯/独联体地区和一些对类型安全有高要求的团队)保持稳定。
-
竞争格局变化:2025-2026 年的 React 状态管理格局中,Zustand 已成为新项目首选,TanStack Query 主导服务端状态,Jotai 在原子化方案中领先。Effector 在这个格局中定位为"复杂业务逻辑专家方案",而非通用状态管理工具。
-
远-fetched 持续演进:作为 Effector 生态中的数据获取方案,farfetched 持续更新,提供缓存、乐观更新、重试等能力,弥补 Effector 核心不内置数据获取的空白。
-
暂无 v24 计划:截至 2026 年 7 月,Effector 团队尚未公布 v24 的路线图。项目处于 v23.x 的稳定维护阶段。
未来展望
- 编译器工具链增强:Babel/SWC 插件预计将继续增强,可能引入更多编译时优化和类型检查能力。
- SSR 框架集成深化:
@effector/next和 SSR 相关 API 可能进一步简化 Next.js App Router 的集成体验。 - 社区国际化:随着 Effector 在国际社区的曝光增加,英文文档和资源有望逐步丰富。
- Signals 互操作:如果 TC39 Signals 提案标准化,Effector 可能提供与 Signals 的互操作层。
- 可持续性挑战:Effector 的未来发展高度依赖核心维护者 Anton Kosykh 的投入,社区治理和贡献者培养是其长期可持续性的关键挑战。
本文档基于 2026 年 7 月调研数据编写,信息可能随版本更新而变化。建议参考 官方文档 获取最新信息。