☰
Rematch 入门:以无样板代码的方式构建 Redux 框架的 Redux Store
2026/9/25 2:40:14 网站建设 项目流程
  • 前端

【免费下载链接】rematch

The Redux Framework

项目地址:https://gitcode.com/gh_mirrors/re/rematch
点击查看免费下载

本文基于 Rematch 仓库的介绍文档(docs/introduction.md)展开:Rematch 定位为“不带样板代码的 Redux 最佳实践”,即无需再手写 action types、action creators、switch 语句和 thunks。读完本文,你将理解 Rematch 的完整能力清单、从零初始化 store 的四步流程,并能对照 packages/core 的源码确认每一项特性背后的真实实现,从而在 React、React Native 等场景中以极小的依赖体积接入 Redux 状态管理。

一、Rematch 是什么

Redux 是一个强大的状态管理工具,拥有健康的中间件生态和出色的 devtools。Rematch 建立在 Redux 之上,通过减少样板代码并强制推行最佳实践来解决 Redux 的三大痛点:

  • 不再需要定义 action types 常量字符串;
  • 不再需要编写 action creators 函数;
  • 不再需要 reducer 中的switch分支,也不再需要为异步逻辑引入 thunks。

README 中的对比表概括了两者的关系(摘自 README.md):

能力ReduxRematch
简单搭建支持
更少的样板代码支持
可读性支持
可配置支持支持
redux devtools支持支持
自动生成的 action creators支持
异步处理thunksasync/await

需要说明的是,Rematch 并没有替代 Redux:从 packages/core/package.json 可见,@rematch/core声明了peerDependencies: { "redux": ">=4" },当前版本为 2.2.0,它构建的是一个真实 Redux store,只是把配置和调用方式包装得更为简洁。

二、官方特性清单与源码印证

介绍文档列出了 Rematch 的完整特性,下面逐项结合仓库源码给出实现层面的印证。

2.1 体积小于 2kb,且无需配置

介绍文档宣称核心小于 2kb(README 进一步标注为 less than 1.4 kilobytes)。源码规模也确实很小:packages/core/src/index.ts 整个入口只导出init、createModel与全部类型定义。

“无配置”体现为 packages/core/src/config.ts 中的createConfig:用户不传任何参数也能构建完整配置——models默认为{}、plugins默认为[],Redux 侧的reducers、rootReducers、enhancers、middlewares全部有默认值,devtoolOptions.name默认取 store 名称。

2.2 减少 Redux 样板代码

Model 把 state、reducers、effects 聚合在一处。从 packages/core/src/reduxStore.ts 的createModelReducer可以看到样板代码是如何被消解的:

  • 每个 model 的 reducer 键自动组合为modelName/reducerKey形式的 action name(如count/increment),完全取代手写 action type 常量;
  • 生成一个combinedReducer,按action.type分发到对应 reducer 并传入state、action.payload、action.meta,取代switch语句;
  • 若 reducer 键本身包含/(如监听其他 model 的 action),则通过isAlreadyActionName判定后直接作为 action name 使用。

2.3 内置副作用(effects)支持

effects 让异步逻辑用原生 async/await 表达。实现分两半:

  1. dispatcher 侧:packages/core/src/dispatcher.ts 的createEffectDispatcher会把effects: (dispatch) => ({...})形式的 effects 展开,将每个 effect 以modelDispatcher为this绑定后注册进bag.effects['modelName/effectName'],并在dispatch[modelName]上挂上带isEffect: true标记的 action 生成器;
  2. middleware 侧:packages/core/src/rematchStore.ts 中的createEffectsMiddleware拦截 action——当action.type in bag.effects时,先调用next(action)执行 reducer(如果存在同名的 reducer),再调用对应 effect 函数并传入action.payload、store.getState()、action.meta,返回其结果。

这正是“thunks 被 async/await 取代”的底层机制:effect 的返回值可以是一个 Promise,调用方可以await。

2.4 自动生成的 dispatch 与 React Devtools 支持

dispatch[model]action的便捷语法由createActionDispatcher生成:构造{ type: 'modelName/actionName', payload?, meta? }后转发给 Redux 的dispatch。

Devtools 支持在 packages/core/src/reduxStore.ts 的composeEnhancersWithDevtools中实现:只要未通过devtoolOptions.disabled关闭且window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__存在,就用扩展提供的 compose 包装增强器,devtoolOptions.name默认为 store 名称,因此多个 store 在 Devtools 中可区分。

2.5 TypeScript 支持

packages/core/src/types.ts 提供了完整类型体系:

  • Model/NamedModel/Models描述模型结构;
  • RematchRootState<TModels>从所有 model 的state推导根 state 类型;
  • RematchDispatch<TModels>在原生 ReduxDispatch 之上叠加dispatch[modelName]actionName的签名,并根据 reducer/effect 的参数推导payload、meta是否必填,effect dispatcher 还会携带isEffect标记与返回类型;
  • createModel<RootModel>()({ ... })辅助函数在 packages/core/src/index.ts 中定义,用于在models/index.ts中导出RootModel接口后获得完整的类型推断。

2.6 动态添加 reducers

RematchStore接口在ReduxStore基础上额外暴露addModel方法。其实现位于 packages/core/src/rematchStore.ts 的rematchStore对象字面量中:先validateModel校验、createModelReducer注册 reducer、prepareModel与enhanceModel生成 dispatchers,最后调用reduxStore.replaceReducer(createRootReducer(bag))并派发@@redux/REPLACE触发 Devtools 重算,实现运行时动态挂载新 model。

2.7 支持热重载与多 store

  • 热重载:model 以普通模块导出、store 通过init集中创建,配合shouldHotReload等 devtool 选项与replaceReducer机制(见 2.6),HMR 场景下 reducer 可被整体替换而不丢失状态;
  • 多 store:packages/core/src/config.ts 维护了一个模块级计数器,未指定name时自动命名为Rematch Store 0、Rematch Store 1……每次调用init都基于独立配置创建独立 Redux store,store.name会同时用于 Devtools 实例名,便于多 store 并存。

2.8 支持 React Native

@rematch/core对 Redux 的依赖是纯 JS 实现,Devtools 接入被typeof window === 'object'守卫保护(见 packages/core/src/reduxStore.ts),非浏览器环境自动回退到Redux.compose,因此可直接用于 React Native。

2.9 插件可扩展性与官方插件库

Rematch 及其内部全部构建在插件管道之上(见bag.forEachPlugin的调用点:createMiddleware、onModel、onStoreCreated、onReducer、onRootReducer)。官方插件一览(见 docs/plugins/index.md):

  • Immer 插件:用 immer 包装 reducers,允许以可变写法产生不可变状态;
  • Select 插件:为 models 提供 reselect 风格的 selectors;
  • Persist 插件:redux-persist 封装,持久化数据;
  • Loading 插件:为 effects 自动添加 loading 指示器;
  • Updated 插件:记录 model、effect、reducer 最近触发时间;
  • 另有 typed-state 插件 提供useTypedState等类型化状态访问。

官方插件源码均在仓库内可直接阅读,例如 packages/loading/src/index.ts 展示了完整插件形态:通过config.models注入一个loadingmodel,通过onModel钩子包裹每个 effect 的 dispatcher,在 Promise 的 then/catch 中派发show/hide更新 loading 状态,并支持whitelist/blacklist过滤与boolean/number/full三种状态形态。

三、从零开始的四步上手流程

以下流程继承自 docs/installation.md,是最小可用的完整路径。

Step 0:安装

npm install @rematch/core

注意redux是 peer 依赖(>=4),需要随项目一起安装;engines要求 Node>=10。

Step 1:定义 models

Model 回答三个问题:初始状态是什么(state)、如何同步改变状态(reducers)、如何处理异步(effects)。

export const count = { state: 0, // 初始状态 reducers: { // 纯函数处理状态变更 increment(state, payload) { return state + payload; }, }, effects: (dispatch) => ({ // 非纯函数处理状态变更,异步用 async/await async incrementAsync(payload, rootState) { await new Promise((resolve) => setTimeout(resolve, 1000)); dispatch.count.increment(payload); }, }), };

TypeScript 版本使用createModel辅助函数,并在models/index.ts中声明RootModel以获得推断:

// ./models/count.ts import { createModel } from "@rematch/core"; import { RootModel } from "."; export const count = createModel<RootModel>()({ state: 0, reducers: { increment(state, payload: number) { return state + payload; }, }, effects: (dispatch) => ({ async incrementAsync(payload: number, state) { console.log("This is current root state", state); await new Promise((resolve) => setTimeout(resolve, 1000)); dispatch.count.increment(payload); }, }), });
// ./models/index.ts import { Models } from "@rematch/core"; import { count } from "./count"; export interface RootModel extends Models<RootModel> { count: typeof count; } export const models: RootModel = { count };

复杂 state 可用as断言给出完整类型,仓库中的完整可运行示例见 examples/count-react-ts/src/models/questions.ts 与 examples/all-plugins-react-ts/src/models,这些示例全部纳入仓库测试套件(examples/all-plugins-react-ts/src/index.test.tsx)。

Step 2:初始化 store

init是唯一必须调用的方法。最低限度只需提供models:

// store.js import { init } from "@rematch/core"; import * as models from "./models"; const store = init({ models }); export default store;
// store.ts import { init, RematchDispatch, RematchRootState } from "@rematch/core"; import { models, RootModel } from "./models"; export const store = init({ models }); export type Store = typeof store; export type Dispatch = RematchDispatch<RootModel>; export type RootState = RematchRootState<RootModel>;

Step 3:派发 action

dispatch既支持原生 Redux 的dispatch({ type, payload }),也支持dispatch[model]action简写,两者等价:

const { dispatch } = store; // state = { count: 0 } dispatch({ type: "count/increment", payload: 1 }); // state = { count: 1 } dispatch.count.increment(1); // state = { count: 2 } dispatch({ type: "count/incrementAsync", payload: 1 }); // 延迟后 state = { count: 3 } dispatch.count.incrementAsync(1); // 延迟后 state = { count: 4 }

Step 4:接入视图层

Rematch 可与 react-redux 等原生 Redux 集成方式无缝配合:

// App.js import React from "react"; import ReactDOM from "react-dom"; import { Provider, connect } from "react-redux"; import store from "./store"; const Count = (props) => ( <div> The count is {props.count} <button onClick={props.increment}>increment</button> <button onClick={props.incrementAsync}>incrementAsync</button> </div> ); const mapState = (state) => ({ count: state.count }); const mapDispatch = (dispatch) => ({ increment: () => dispatch.count.increment(1), incrementAsync: () => dispatch.count.incrementAsync(1), }); const CountContainer = connect(mapState, mapDispatch)(Count); ReactDOM.render( <Provider store={store}> <CountContainer /> </Provider>, document.getElementById("root") );

examples/count-react 是这套最小用法的完整可运行工程。

四、store 创建的内部调用链

理解init之后的内部流程,有助于在排查问题时定位环节。从 packages/core/src/index.ts 到 packages/core/src/rematchStore.ts,init的调用链为:

  1. createConfig(config.ts):补齐默认值、校验配置,并遍历config.plugins,将每个插件config.models、config.redux中的改动合并进主配置(模型合并、initialState/reducers 浅合并、enhancers/middlewares 追加、combineReducers/createStore可被插件覆盖);
  2. createRematchBag(bag.ts):把models映射转成带name与默认空reducers的命名模型数组,并对每个 model 执行validateModel;
  3. 组装 store(rematchStore.ts):
    • 先向bag.reduxConfig.middlewares压入 effects 中间件,再依次收集各插件的createMiddleware产物;
    • createReduxStore(reduxStore.ts)为每个 model 生成 combined reducer、合并 root reducer(支持rootReducers前置处理)、组合 middlewares 与 devtools 增强器后创建真正的 Redux store;
    • prepareModel先为每个 model 注入dispatch[modelName]并生成 reducer dispatchers,enhanceModel再生成 effect dispatchers 并触发插件的onModel钩子——两步分离是为了让循环引用模型(如示例 packages/core/test/v1_regressions/circurlarmodels.test.ts)在 effects 中解构时都能拿到彼此;
    • 最后执行插件的onStoreCreated钩子,允许插件替换或扩展最终 store。

相关行为均有测试覆盖,例如 packages/core/test/multiple.test.ts(多 store)、packages/core/test/plugins.test.ts(插件管道)、packages/core/test/effects.test.ts(effects 语义)。

五、插件 API:扩展点的完整清单

介绍文档强调 Rematch “Extendable with plugins”,其 API 详见 docs/api-reference/plugins.md。一个插件对象可包含:

  • config: { models, redux }:注入额外 model 或覆盖 Redux 配置(形状与init接受的配置一致);
  • exposed:向 store 挂载额外属性,供插件间通信;在onModel与onStoreCreated之前执行;
  • createMiddleware(bag):创建可访问 Rematch 内部 “bag” 的自定义中间件;
  • onReducer(reducer, modelName, bag):model 的 base reducer 创建时执行,可返回新 reducer 覆盖;
  • onRootReducer(reducer, bag):root reducer 创建时执行,可返回新 root reducer 覆盖;
  • onModel(namedModel, rematchStore):每个 model 的 reducers 与 dispatchers 就绪后执行,动态addModel时也会再次触发;
  • onStoreCreated(rematchStore, bag):store 就绪后的最后一个钩子,可返回新 store 覆盖。

完整形态示例(摘自官方插件 API 文档):

const plugin = { config: { redux: { combineReducers: customCombineReducers, }, models: { extra: extraModel, }, }, exposed: { select: {} }, createMiddleware: (rematchBag) => (store) => (next) => (action) => { // do something here return next(action); }, onReducer(reducer, modelName, bag) { // do something }, onRootReducer(reducer, bag) { // do something }, onModel(namedModel, rematchStore) { // do something }, onStoreCreated(rematchStore, bag) { // do something }, };

这些钩子在核心中的触发点均可在 packages/core/src/rematchStore.ts、packages/core/src/reduxStore.ts 与 packages/core/src/bag.ts 中逐一对应找到(forEachPlugin('onReducer' | 'onRootReducer' | 'onModel' | 'onStoreCreated' | 'createMiddleware'))。

六、进阶路径与资源索引

介绍文档为两类读者给出了分岔口:

  • 来自现有 Redux 代码库:迁移往往只涉及状态管理层的小改动,视图逻辑基本不动,详见 docs/migrating/from-redux.md;
  • 从零开始:先读安装指南 docs/installation.md,TypeScript 用户可直接跳转 docs/typescript.md 了解 utility types;v1 老用户可参考 docs/migrating/from-v1-to-v2.md。

仓库内值得深入的路径:

路径内容
packages/core/src核心库全部源码(bag、config、dispatcher、reduxStore、rematchStore、types、validate)
packages/core/test核心行为测试(effects、plugins、multiple、init、listener 等)
examples10 个可运行示例:count-react、count-react-ts、all-plugins-react-ts、multi-react(多 store)、nextjs-blog、gatsby-example 等
docs/api-referenceinit配置参数、models、plugins、redux 配置、store 的完整 API 参考
docs/recipesRedux DevTools、Redux 插件、测试等实战配方

七、许可与支持

  • 项目采用 MIT 许可(LICENSE);
  • 问题反馈、功能请求与疑问可提交 issue(对应 CONTRIBUTING.md 中的社区规范);
  • 版本适用前提:本文所述 API 对应仓库中@rematch/core2.2.0(packages/core/package.json),要求redux >= 4、Node>= 10,文档与示例均以当前仓库内容为准。
  • 前端

【免费下载链接】rematch

The Redux Framework

项目地址:https://gitcode.com/gh_mirrors/re/rematch
点击查看免费下载
上一篇:SQLAlchemy水平分片(Sharding)实战:基于多数据库的分片实现
下一篇:GraphScope项目常见问题深度解析与技术指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询