事情是这样的
上周技术评审会上,leader问:"这个模块用什么做状态管理?"
我说:"easy-model。"
然后全场安静了5秒。
"这是什么?""没听过。""靠谱吗?""有Redux好吗?"
我深吸一口气,准备了一波反向输出。
我为什么"叛变"了
1. Redux:爱过,但真的太累了
// Redux版本
// 1. 定义action types
const INCREMENT = "counter/INCREMENT";
const DECREMENT = "counter/DECREMENT";
// 2. 定义actions
const increment = () => ({ type: INCREMENT });
const decrement = () => ({ type: DECREMENT });
// 3. 定义reducer
const counterReducer = (state = 0, action) => {
switch (action.type) {
case INCREMENT:
return state + 1;
case DECREMENT:
return state - 1;
default:
return state;
}
};
// 4. 配置store
const store = createStore(counterReducer);
// 5. 在组件中使用
const Counter = () => {
const count = useSelector((state) => state);
const dispatch = useDispatch();
return (
<div>
<div>{count}</div>
<button onClick={() => dispatch(increment())}>+</button>
</div>
);
};
一个计数器,整整60行代码。
而且随着项目变大:
- action文件爆炸
- reducer文件爆炸
- selector文件爆炸
- 中间件配置爆炸
- 最后没人看得懂
2. MobX:类型推来推去,心累
// MobX版本
class CounterStore {
@observable count = 0;
@action
increment() {
this.count += 1;
}
}
const store = new CounterStore();
// 组件
const Counter = observer(() => {
return <div>{store.count}</div>;
});
看起来简单?但实际项目中:
// 稍微复杂点的场景
class OrderStore {
@observable orders: Order[] = [];
@observable loading = false;
@computed
get totalAmount() {
return this.orders.reduce((sum, o) => sum + o.amount, 0);
}
@action
async fetchOrders() {
this.loading = true;
try {
const res = await api.getOrders();
runInAction(() => {
this.orders = res.data;
});
} finally {
runInAction(() => {
this.loading = false;
});
}
}
}
然后TypeScript开始报错:
- "Type 'Order[]' is not assignable to type 'Order'"
- "Cannot read property 'reduce' of undefined"
光修类型问题就能耗费一下午。
easy-model:真香
// easy-model版本
class CounterModel {
count = 0;
increment() {
this.count += 1;
}
decrement() {
this.count -= 1;
}
}
function Counter() {
const counter = useModel(CounterModel, []);
return (
<div>
<div>{counter.count}</div>
<button onClick={() => counter.increment()}>+</button>
</div>
);
}
不到30行,包含类型定义。
同事问我:它有什么亮点?
我掰着指头数:
1. 类模型 + IoC
// 依赖注入,spring既视感
const apiSchema = object({ baseUrl: string() });
class UserApi {
@inject(apiSchema)
config?: { baseUrl: string };
async getUser(id: string) {
return fetch(`${this.config?.baseUrl}/users/${id}`);
}
}
2. 深度监听
// 嵌套对象变化也能监听
watch(order, (keys, prev, next) => {
// keys: ['items', 0, 'price']
});
3. History内置
const history = useModelHistory(model);
history.back(); // 撤销
history.forward(); // 重做
history.reset(); // 重置
4. Loader加载状态
class UserModel {
@loader.load()
async fetchUser() {
// ...
}
}
function UserPage() {
const { isLoading } = useLoader();
// isLoading(userModel.fetchUser) 直接获取单个方法状态
}
性能实测
| 方案 | 10万元素批量更新 |
|---|---|
| Zustand | 0.6ms |
| easy-model | 3.1ms |
| MobX | 16.9ms |
| Redux | 51.5ms |
有IoC能力的状态管理方案里,easy-model没有对手。
现在
评审结束了,leader说:
"这个库确实有点东西,下个项目可以试试。"
Github: github.com/ZYF93/easy-…
不是Redux用不起,而是easy-model更有性价比。
点个⭐️,支持下这个"小众"但真好用的库 🙏