我用 Zustand 三年了,直到遇见 easy-model...

不是说Zustand不好,而是有些场景它真的hold不住。

故事是这样的

我们公司有个中后台项目,状态管理一直用Zustand。讲真,Zustand确实香------API简洁、性能好、类型推断也还行。

直到有一天,产品经理提了一个需求:

"做一个操作日志中心,用户每做一个操作就记录下来,支持撤销重做。而且要能在列表页看到嵌套对象的变化轨迹。"

我自信满满地开始写,然后就被打脸了。

Zustand的痛点

1. 状态一多就成了"函数大杂烩"

tsx 复制代码
// store.ts
const useStore = create((set, get) => ({
  user: null,
  orders: [],
  filters: {},
  pagination: { page: 1, size: 10 },
  loading: false,

  setUser: (user) => set({ user }),
  setOrders: (orders) => set({ orders }),
  setFilters: (filters) => set({ filters }),
  setPagination: (pagination) => set({ pagination }),
  setLoading: (loading) => set({ loading }),

  fetchOrders: async () => {
    const { filters, pagination } = get();
    set({ loading: true });
    const res = await api.getOrders(filters, pagination);
    set({ orders: res.data, loading: false });
  },

  // ... 200行后
}));

一个文件写了500行,到后面自己都不想看了。

2. 撤销重做?自己实现吧

Zustand没有内置history支持。网上倒是有zundo这种中间件,但:

  • 配置繁琐
  • 类型推断经常出问题
  • 和业务代码集成麻烦

3. 监听嵌套对象?不好意思,做不到

tsx 复制代码
const orders = useStore((s) => s.orders);
// 改变了 orders[0].items[0].price
// 组件不会更新!因为引用没变

你得用subscribe或者自己写selector,关键是一旦嵌套深了,selector写得怀疑人生。

然后我发现了easy-model

tsx 复制代码
// 用类来组织,一个领域一个类
class OrderModel {
  orders: Order[] = [];
  filters: FilterParams = {};
  pagination = { page: 1, size: 10 };
  loading = false;

  async fetchOrders() {
    this.loading = true;
    const res = await api.getOrders(this.filters, this.pagination);
    this.orders = res.data;
    this.loading = false;
  }

  setFilter(key: string, value: any) {
    this.filters[key] = value;
  }
}

// 内置history支持
const order = useModel(OrderModel, []);
const history = useModelHistory(order);

// 撤销重做,一行搞定
history.back();
history.forward();
history.reset();

这才是面向对象!

深度监听,真香

tsx 复制代码
class ComplexModel {
  user = {
    profile: {
      address: { city: "北京" },
    },
  };
  orders = [];
}

// 监听嵌套对象变化
watch(user, (keys, prev, next) => {
  // keys: ['profile', 'address', 'city']
  console.log("变化了", keys, prev, next);
});

user.profile.address.city = "上海";
// 自动触发监听,拿到完整的变化路径

还有IoC?

tsx 复制代码
// 定义依赖
const apiSchema = object({
  baseUrl: string(),
}).describe("API配置");

// 注入
class OrderApi {
  @inject(apiSchema)
  config?: { baseUrl: string };

  async getOrders() {
    return fetch(`${this.config?.baseUrl}/orders`);
  }
}

// 配置
config(
  <Container>
    <CInjection
      schema={apiSchema}
      ctor={OrderApi}
      params={["https://api.example.com"]}
    />
  </Container>,
);

这不妥妥的企业级架构?

性能对比

官方benchmark(10万个元素,5轮批量更新):

方案 耗时
Zustand 0.6ms
easy-model 3.1ms
MobX 16.9ms
Redux 51.5ms

easy-model比Zustand慢3倍,但换来了:

  • 类模型组织方式
  • 内置IoC能力
  • 深度监听
  • History支持

这波不亏!

怎么选?

  • 小项目、简单状态 → Zustand依旧真香
  • 中大型、需要领域模型、需要IoC、需要history → easy-model真香

Github: github.com/ZYF93/easy-...

觉得有帮助的点个⭐️支持下 🙏

相关推荐
meilindehuzi_a2 小时前
从零理解并实现 RAG:用 LangChain.js 构建语义检索问答系统
开发语言·javascript·langchain
世界哪有真情3 小时前
拿人类意识卡 AI?等于用 bug 验收正式产品
前端·人工智能·后端
鱼樱前端3 小时前
别再自己拼凑了!这款 Vue 3.5+ 严肃产品级组件库,把 AI 对话、工作流和复杂数据全包了!
javascript·vue.js·github
Listen·Rain4 小时前
Vue3:setup详解
前端·javascript·vue.js
Patrick_Wilson4 小时前
修好 bug 只是开始:一次由监控需求反向重构日志结构的复盘
前端·监控·数据可视化
kyriewen5 小时前
面试官让我优化一个卡死的表格,我打开 Claude 五秒重写,他放下咖啡问我要不要来当组长
前端·javascript·面试
小粉粉hhh5 小时前
React(二)——dom、组件间通信、useEffect
前端·javascript·react.js
Csvn5 小时前
🤖 AI 生成前端代码的 5 个典型翻车现场及修复指南
前端
IT_陈寒6 小时前
SpringBoot自动装配坑了我一天,原来问题出在这
前端·人工智能·后端
索西引擎7 小时前
【React】key 属性:协调算法中的元素标识机制与最佳实践
前端·javascript·react.js