JSCommon系列 - 前端常用的状态管理

前端常用的状态管理

JSCommon 介绍

JavaScript/TypeScript 的简单工具集合,为前端应用提供你所需要的全部工具函数

开始使用:

bash 复制代码
npm install @wolforest/jscommon

项目地址: github.com/wolforest/j...

前端状态管理库

前端开发中,状态管理一直是个绕不开的话题。随着应用复杂度的增加,我们需要更优雅的方案来管理应用状态。今天我们来聊聊两个备受关注的状态管理库:Jotai 和 Zustand。

一、状态管理的痛点与选择

传统状态管理的问题

在React开发中,我们经常遇到这些问题:

  • 状态提升导致不必要的重渲染
  • 复杂的状态逻辑难以维护
  • 跨组件状态共享困难
  • 异步状态处理复杂

现代状态管理的两种思路

目前主流的状态管理库主要分为两种设计思路:

  1. 原子化管理:以Jotai为代表,自下而上的状态管理
  2. 集中式管理:以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性能优化

  1. 原子分割:将大对象拆分为多个小原子
  2. 选择性订阅:只订阅需要的状态
  3. 原子家族:处理动态数据集合
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性能优化

  1. 选择性订阅:只订阅需要的状态片段
  2. 浅比较:使用shallow进行浅比较
  3. 状态分片:将不相关的状态分离
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 实现计划

  1. 第一阶段:实现基础的React状态管理封装
  2. 第二阶段:添加Vue和Taro支持
  3. 第三阶段:提供更多高级功能(中间件、开发工具等)
  4. 第四阶段:性能优化和生态集成

通过这样的封装,开发者可以:

  • 快速上手状态管理,无需学习多个库的API
  • 在不同框架间保持一致的开发体验
  • 享受jscommon的Tree-shaking和类型安全特性
  • 根据项目需求灵活选择底层实现

总结

状态管理库的选择没有标准答案,关键是要根据项目需求和团队情况来决定。Jotai适合需要精细控制的场景,Zustand适合追求简洁的项目。

随着前端技术的发展,状态管理也在不断演进。期待 @wolforest/jscommon 在状态管理方面的封装,为开发者提供更便捷的解决方案。

选择适合的工具,写出更好的代码。


项目地址: github.com/wolforest/j...

感谢阅读到最后,期待你的 github 🌟 鼓励!

相关推荐
brzhang7 分钟前
我们复盘了100个失败的AI Agent项目,总结出这3个“必踩的坑”
前端·后端·架构
万能的小裴同学15 分钟前
让没有小窗播放的视频网站的视频小窗播放
前端·javascript
今禾44 分钟前
# 深入理解JavaScript闭包与柯里化:函数式编程的核心利器
javascript
小小琪_Bmob后端云1 小时前
引流之评论区截流实验
前端·后端·产品
我科绝伦(Huanhuan Zhou)1 小时前
MOP数据库备份脚本生成工具
前端·css·数据库
liang_jy1 小时前
Android 窗口显示(一)—— Activity、Window 和 View 之间的联系
android·面试
海的诗篇_1 小时前
前端开发面试题总结-vue2框架篇(三)
前端·javascript·css·面试·vue·html
Danny_FD1 小时前
在 React 函数组件中实现 `<textarea>` 平滑自动滚动到底部
前端
掘金一周1 小时前
数据脱敏的这6种方案,真香!| 掘金一周 5.29
前端·人工智能·后端
小小愿望1 小时前
彻底禁用移动端H5页面默认下拉刷新功能:原理与实战
前端