说说zustand的缺点——对几个用过的状态管理工具的总结

主要是与mobX的一些对比,redux和recoil很久没用了

一家之言,我是彩笔

redux

记忆里redux的不可变树特性会导致一些意料外的刷新(应改是有办法解决的才对),并且心智负担有点重,需要自己构建action等,虽然又toolkit的加入据说会好很多,但是最后还是被我放弃了,太累了

mobX

我认为他是最简单的能够对应上OOP的一种状态管理工具了,装饰器语法很棒,我没有用得很深并且有一阵子没用了,说些自己的见解

  1. 最新版还能用装饰器语法吗?如果支持也只是 怕别用用别怕,我永远忘不了它,makeObservable 一个一个加还是麻烦,而且 construct 跟实际的代码分离,最后还不如 makeAutoObservable
  2. makeAutoObservable 或许并不是最好的选择:取决于你是否将 mobx object 单纯地作为一个 store 处理

mobx 中属性是否是 state/action 应该是有选择地去处理而不是交给 auto,整个class可以根据业务封装成独立的 data manager model,根据实际的使用情况,内挂很多private属性/纯函数等与state无关,但是与业务相关的东西,和 hook 的构建类似,应该是基于业务的OOP

zustand

最近在用,并非本人选型,但是用着还行,追求极致的打包大小时这是个好选择,项目规模大时建议不要考虑

优点
  • 不像mobx还需要observer包裹组件
  • 体积小,但是其实没差多少,不是极致追求体积的场景我觉得差别不大
说说缺点
ts 复制代码
const list = useListStore(state=>state.list);
// ...
list.map
  • jsdoc 注释以及引用关系会在这里丢失,list 只会指向 const list =,需要继续查找state.list的定义
  • 他是 hook 的用法,use 开头一个 store 很怪,如果命名不以use开头, userStore(...)这样作为方法调用一个 store 也很怪,始终没有想到一个简单明了的语义设计
ts 复制代码
// 构建方式1
interface StoreType {
  /** xxx 用途的列表 */
  list: { id: string }[];
}

const useListStore = create<StoreType>((set, get) => ({
  /** 这里注释并没有什么用处 */
  list: [],
}));
  • 类型和数据分离,跟上面一样,如果要查找初始值,查找定义时,会跳到类型,然后根据类型查找,要在一堆引用里找到 store 的初始定义
  • 当属性很多(虽然不应该让他很多),类型很长时,需要将 StoreType 分文件管理,更难受了
ts 复制代码
// 构建方式2
// 如果需要自动推断类型,需要不传入 set get
const useListStore = create(() => ({
  /** xxx 用途的列表 */
  list: [] as { id: string }[],
}));
  • 构建方式2可以避免上面的问题,但是每个初始值都需要去断言,依然会存在jsdoc丢失的问题,并且无法在 store 内直接使用 set/get 而需要 useListStore.setState 这与库的原始设计相悖,这样比起来基于 class 的 mobx 在使用上要舒服很多,可以 list:Type = []
  • 官方示例中 将 action 写在 store object 内真的是很迷的一步,为了聚合一个 state 相关的逻辑,会在 store object 内写很大的一块,我在实践中更多的是基于文件块进行管理,尽可能降低 store object 本身的行数(主要还是减少全局 state 的使用),让逻辑更加清晰
ts 复制代码
import { create } from 'zustand';

export interface BaseStore {
  /** 广告 - 打开推荐创作者 */
  excellentShown: boolean;
  /** 打开编辑精选 - 当前展示期数
   * - `''` 为默认期数
   * - `undefined` 为关闭编辑精选
   */
  editPickPeriod?: number | string;
  /** 排行榜(如果分成两个状态处理会导致 effect hook 执行两次)
   * - `undefined` 为关闭排行榜
   */
  rankingSource?: {
    type: 'fc_day' | 'fc_week' | 'active_day' | 'active_week';
    /** 排行榜期数 */
    period?: string | number;
  };
  /** 是否显示提现页 */
  payoutPageShown: boolean;
  /** feed 流 socket 触发的数量变化未加载的数量 */
  newFeedCount: number;
  /** null 表示显式的未获取到,undefined 表示 prefetch 可以再等等 */
  customLink?: string | null;
}

const baseStore = create<BaseStore>((set, get) => ({
  excellentShown: false,
  editPickPeriod: undefined,
  rankingSource: undefined,
  payoutPageShown: false,
  newFeedCount: 0,
}));

export default baseStore;

const get = baseStore.getState;
const set = baseStore.setState;

export const baseStoreUtils = {
  /** getter - 右下角固定按钮显隐 */
  getFixedToolsHidden: () => {
    const { payoutPageShown } = get();
    return !!payoutPageShown;
  },
  /** 重置设置会导致右下角固定按钮显隐变化的状态 */
  resetFixedToolsHiddenState: () => set({ payoutPageShown: false }),
  setPayoutPageShown: (shown: boolean) => set({ payoutPageShown: shown }),
};
  • 基于AOP(面向切片)的概念,无论是 action 还是 getter 都是很有必要的,前几天查一个数据变更行为导致的bug时,面对💩山里遍地的 xxxStore.setState({xxx:xxx}) 我是十万分的崩溃,zustand 在设计上非常的灵活,这在编码上是很方便,但在项目管理上真的很让人头疼,无法制造 readonly 的 getter 导致一堆对项目没有敬畏的代码出现,这不是好事

结论

无论选型什么状态管理工具,其实差别都没有那么大,至少在人与人的差距面前是这样的,遇到堆💩人,用什么都救不了,面对项目管理的工作,新的不一定是好的,coooooool 的代码需要 cool 的队友

相关推荐
迷曳43 分钟前
28、鸿蒙Harmony Next开发:不依赖UI组件的全局气泡提示 (openPopup)和不依赖UI组件的全局菜单 (openMenu)、Toast
前端·ui·harmonyos·鸿蒙
爱分享的程序员1 小时前
前端面试专栏-工程化:29.微前端架构设计与实践
前端·javascript·面试
上单带刀不带妹1 小时前
Vue3递归组件详解:构建动态树形结构的终极方案
前端·javascript·vue.js·前端框架
-半.1 小时前
Collection接口的详细介绍以及底层原理——包括数据结构红黑树、二叉树等,从0到彻底掌握Collection只需这篇文章
前端·html
没有bug.的程序员1 小时前
JAVA面试宝典 -《 架构演进:从单体到 Service Mesh》
java·面试·架构
90后的晨仔1 小时前
📦 Vue CLI 项目结构超详细注释版解析
前端·vue.js
@大迁世界1 小时前
用CSS轻松调整图片大小,避免拉伸和变形
前端·css
一颗不甘坠落的流星1 小时前
【JS】获取元素宽高(例如div)
前端·javascript·react.js
白开水都有人用1 小时前
VUE目录结构详解
前端·javascript·vue.js
if时光重来2 小时前
axios统一封装规范管理
前端·vue.js