前端常用的状态管理

JSCommon 介绍
JavaScript/TypeScript 的简单工具集合,为前端应用提供你所需要的全部工具函数
开始使用:
bash
npm install @wolforest/jscommon
项目地址: github.com/wolforest/j...
前端状态管理库
前端开发中,状态管理一直是个绕不开的话题。随着应用复杂度的增加,我们需要更优雅的方案来管理应用状态。今天我们来聊聊两个备受关注的状态管理库:Jotai 和 Zustand。

一、状态管理的痛点与选择
传统状态管理的问题
在React开发中,我们经常遇到这些问题:
- 状态提升导致不必要的重渲染
- 复杂的状态逻辑难以维护
- 跨组件状态共享困难
- 异步状态处理复杂
现代状态管理的两种思路
目前主流的状态管理库主要分为两种设计思路:
- 原子化管理:以Jotai为代表,自下而上的状态管理
- 集中式管理:以Zustand为代表,轻量级的全局状态
二、Jotai:原子化状态管理的艺术
Jotai采用原子化的设计理念,每个状态都是一个独立的原子(atom),可以独立更新和订阅。
基础概念
typescript
import { atom, useAtom } from 'jotai'
// 创建一个简单的计数器原子
const countAtom = atom(0)
function Counter() {
const [count, setCount] = useAtom(countAtom)
return (
<div>
<p>当前计数:{count}</p>
<button onClick={() => setCount(count + 1)}>
增加
</button>
</div>
)
}
派生状态的威力
Jotai最强大的特性之一就是派生状态,可以基于其他原子创建新的状态:
typescript
const priceAtom = atom(100)
const quantityAtom = atom(2)
// 自动计算总价
const totalAtom = atom((get) =>
get(priceAtom) * get(quantityAtom)
)
function OrderSummary() {
const [price, setPrice] = useAtom(priceAtom)
const [quantity, setQuantity] = useAtom(quantityAtom)
const [total] = useAtom(totalAtom)
return (
<div>
<input
value={price}
onChange={(e) => setPrice(Number(e.target.value))}
placeholder="单价"
/>
<input
value={quantity}
onChange={(e) => setQuantity(Number(e.target.value))}
placeholder="数量"
/>
<p>总价:{total}元</p>
</div>
)
}
异步状态处理
处理异步数据在Jotai中非常直观:
typescript
const userAtom = atom(async (get) => {
const response = await fetch('/api/user')
return response.json()
})
function UserProfile() {
const [user] = useAtom(userAtom)
// Jotai会自动处理loading状态
return (
<div>
<h2>{user.name}</h2>
<p>{user.email}</p>
</div>
)
}
实际应用:表单状态管理
typescript
// 表单字段原子
const usernameAtom = atom('')
const passwordAtom = atom('')
// 表单验证原子
const formValidAtom = atom((get) => {
const username = get(usernameAtom)
const password = get(passwordAtom)
return username.length > 0 && password.length >= 6
})
function LoginForm() {
const [username, setUsername] = useAtom(usernameAtom)
const [password, setPassword] = useAtom(passwordAtom)
const [isValid] = useAtom(formValidAtom)
return (
<form>
<input
value={username}
onChange={(e) => setUsername(e.target.value)}
placeholder="用户名"
/>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="密码"
/>
<button disabled={!isValid}>
登录
</button>
</form>
)
}
三、Zustand:轻量级状态管理的典范
Zustand以其极简的API和小巧的体积(仅1.6KB)赢得了开发者的青睐。
基础使用
typescript
import { create } from 'zustand'
const useCounterStore = create((set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
decrement: () => set((state) => ({ count: state.count - 1 })),
reset: () => set({ count: 0 })
}))
function Counter() {
const { count, increment, decrement, reset } = useCounterStore()
return (
<div>
<p>计数:{count}</p>
<button onClick={increment}>+1</button>
<button onClick={decrement}>-1</button>
<button onClick={reset}>重置</button>
</div>
)
}
异步操作处理
typescript
const useTodoStore = create((set, get) => ({
todos: [],
loading: false,
fetchTodos: async () => {
set({ loading: true })
try {
const response = await fetch('/api/todos')
const todos = await response.json()
set({ todos, loading: false })
} catch (error) {
set({ loading: false })
console.error('获取待办事项失败:', error)
}
},
addTodo: async (text) => {
const response = await fetch('/api/todos', {
method: 'POST',
body: JSON.stringify({ text }),
headers: { 'Content-Type': 'application/json' }
})
const newTodo = await response.json()
set((state) => ({ todos: [...state.todos, newTodo] }))
}
}))
状态持久化
typescript
import { persist } from 'zustand/middleware'
const useSettingsStore = create(
persist(
(set) => ({
theme: 'light',
language: 'zh-CN',
toggleTheme: () => set((state) => ({
theme: state.theme === 'light' ? 'dark' : 'light'
})),
setLanguage: (language) => set({ language })
}),
{
name: 'app-settings'
}
)
)
实际应用:购物车管理
typescript
const useCartStore = create((set, get) => ({
items: [],
addItem: (product) => {
const { items } = get()
const existingItem = items.find(item => item.id === product.id)
if (existingItem) {
set({
items: items.map(item =>
item.id === product.id
? { ...item, quantity: item.quantity + 1 }
: item
)
})
} else {
set({ items: [...items, { ...product, quantity: 1 }] })
}
},
removeItem: (productId) => {
set((state) => ({
items: state.items.filter(item => item.id !== productId)
}))
},
getTotalPrice: () => {
const { items } = get()
return items.reduce((total, item) => total + item.price * item.quantity, 0)
}
}))
四、性能优化实践

Jotai性能优化
- 原子分割:将大对象拆分为多个小原子
- 选择性订阅:只订阅需要的状态
- 原子家族:处理动态数据集合
typescript
// 不推荐:大对象原子
const userAtom = atom({
profile: { name: '', email: '' },
settings: { theme: 'light', notifications: true },
posts: []
})
// 推荐:原子分割
const userProfileAtom = atom({ name: '', email: '' })
const userSettingsAtom = atom({ theme: 'light', notifications: true })
const userPostsAtom = atom([])
Zustand性能优化
- 选择性订阅:只订阅需要的状态片段
- 浅比较:使用shallow进行浅比较
- 状态分片:将不相关的状态分离
typescript
// 不推荐:订阅整个store
const state = useStore()
// 推荐:选择性订阅
const count = useStore(state => state.count)
const increment = useStore(state => state.increment)
// 推荐:浅比较多个状态
import { shallow } from 'zustand/shallow'
const { count, increment } = useStore(
state => ({ count: state.count, increment: state.increment }),
shallow
)
五、如何选择适合的状态管理库
选择Jotai的场景
- 需要细粒度的状态控制
- 复杂的派生状态逻辑
- 状态间有复杂的依赖关系
- 追求最小化重渲染
选择Zustand的场景
- 喜欢简洁的API
- 需要全局状态管理
- 项目规模中等
- 需要状态持久化
对比总结
特性 | Jotai | Zustand |
---|---|---|
包体积 | 2.4KB | 1.6KB |
学习曲线 | 中等 | 简单 |
状态粒度 | 原子级 | 全局级 |
派生状态 | 强大 | 基础 |
异步支持 | 原生 | 需要手动处理 |
TypeScript | 优秀 | 优秀 |
六、@wolforest/jscommon 状态管理封装规划

目前 @wolforest/jscommon
已经集成了多个常用工具库,但在状态管理方面还是空白。基于项目的设计理念,我们可以考虑以下封装方案:
6.1 设计原则
- 框架无关:支持React、Vue、Taro等多框架
- 按需引入:支持Tree-shaking,用户可以选择性使用
- 统一API:提供一致的使用体验
- 类型安全:完整的TypeScript支持
6.2 模块结构规划
bash
packages/core/src/
├── state/
│ ├── index.ts # 统一导出
│ ├── react/ # React状态管理
│ │ ├── jotai.ts # Jotai封装
│ │ ├── zustand.ts # Zustand封装
│ │ └── hooks.ts # 通用hooks
│ ├── vue/ # Vue状态管理
│ │ ├── pinia.ts # Pinia封装
│ │ └── composables.ts # 通用组合式函数
│ ├── vanilla/ # 框架无关状态管理
│ │ ├── store.ts # 通用store
│ │ └── observable.ts # 观察者模式
│ └── types.ts # 类型定义
6.3 API设计示例
typescript
// 统一的状态管理接口
interface StateManager<T> {
getState(): T
setState(updater: Partial<T> | ((state: T) => Partial<T>)): void
subscribe(listener: (state: T) => void): () => void
destroy(): void
}
// React封装示例
export function createReactStore<T>(initialState: T) {
// 可以选择底层实现:Jotai或Zustand
return useZustand ? createZustandStore(initialState) : createJotaiStore(initialState)
}
// Vue封装示例
export function createVueStore<T>(initialState: T) {
return createPiniaStore(initialState)
}
// 通用工具函数
export function createPersistedStore<T>(key: string, initialState: T) {
// 自动处理持久化逻辑
}
export function createAsyncStore<T>(fetcher: () => Promise<T>) {
// 处理异步状态
}
6.4 使用示例
typescript
// 从jscommon导入状态管理工具
import { createReactStore, createPersistedStore } from '@wolforest/jscommon'
// 创建持久化的用户设置store
const useSettingsStore = createPersistedStore('settings', {
theme: 'light',
language: 'zh-CN'
})
// 在组件中使用
function Settings() {
const { theme, language, updateTheme } = useSettingsStore()
return (
<div>
<select value={theme} onChange={updateTheme}>
<option value="light">浅色</option>
<option value="dark">深色</option>
</select>
</div>
)
}
6.5 实现计划
- 第一阶段:实现基础的React状态管理封装
- 第二阶段:添加Vue和Taro支持
- 第三阶段:提供更多高级功能(中间件、开发工具等)
- 第四阶段:性能优化和生态集成
通过这样的封装,开发者可以:
- 快速上手状态管理,无需学习多个库的API
- 在不同框架间保持一致的开发体验
- 享受jscommon的Tree-shaking和类型安全特性
- 根据项目需求灵活选择底层实现
总结
状态管理库的选择没有标准答案,关键是要根据项目需求和团队情况来决定。Jotai适合需要精细控制的场景,Zustand适合追求简洁的项目。
随着前端技术的发展,状态管理也在不断演进。期待 @wolforest/jscommon
在状态管理方面的封装,为开发者提供更便捷的解决方案。
选择适合的工具,写出更好的代码。
项目地址: github.com/wolforest/j...
感谢阅读到最后,期待你的 github 🌟 鼓励!