Giter VIP home page Giter VIP logo

dura's Introduction

dura

Build Status Coverage Status NPM version NPM downloads TypeScript MIT Licence lerna

基于 redux 插件化、可拔插 的前端数据流管理框架 (灵感来源于 dvamirrorrematch ) 。 专为 typescript 而生


技术支持

kavLv9.jpg

为什么会有 dura ?

在业务开发当中,我尝试过很多流行的前端数据流的管理方案,例如:dvamirrorrematch ,但是实际的使用效果并不如意 。

dva

  • 重构火葬场,难以安心的删除 effect、reducer、state,因为担心未知的引用
  • 使用的时候需要频繁的在多个文件之间来回切换,以明确 action 的 type 和 state 的取值
  • redux-saga 高昂的上手成本已经远远大于它能带来的收益

mirror

  • 这哥们直接强绑了 react-router ,我们有 react-native 的项目
  • store 直接挂在了全局的 windows 下面 , 其实上很多时候我们可能需要多个 store 并存的

rematch

  • 对于 typescript 的支持还不错,但是不够精细
  • 很多官方插件 没有 typescript 的类型系统
  • 对于 reducer、effect 的参数提示完全没有
  • api 的设计并不是很喜欢,有些时候用起来觉得很奇怪

dura 拥有哪些能力?

  • 提示系统:强大到你怀疑人生的提示系统、你只需要不停的的鼠标点下去就好。
  • 🔥 逃离重构火葬场:项目重构时,代码想删就删,不用去担心未知的引用。
  • 🔌 插件机制:基本上能够兼容整个 redux 生态中的常用库。
  • 💰 � 上手成本:如果你掌握上面任何一个框架,那么对你来说 dura 的成本很低廉

为什么 不支持动态 model ?

因为动态 model 会破坏 dura 的提示系统,让提示不再精确, 或者说需要人为的去控制一些因素才能达到精确的提示,所以暂时不考虑支持动态 model,其实也可以通过包管理的方案来进行替代


文档

概念

runner

runner 我将它称之为 执行者 , 一般主要用来执行相关的 Action , 当然也有可能用来干其他的事情 , 比如执行 reselect 。所有的 runner 统一从 duraStore 中获取 , 如下:

//创建duraStore
export const duraStore = create({
  initialModel,
  plugins: [createAsyncPlugin(), createLoadingPlugin(initialModel), createImmerPlugin(), createSelectorsPlugin()]
}) as DuraStore<RootModel> & AsyncDuraStore<RootModel> & SelectorsDuraStore<RootModel>;

// 主要用来执行 reducer
const reducerRunner = duraStore.reducerRunner;

// 主要用来执行 effect
const effectRunner = duraStore.effectRunner;


// 主要用来执行 selector
const selectorRunner = duraStore.selectorRunner;

DuraStore 类型系统

默认情况下 @dura/core 包含有一个默认的类型系统

type DuraStore<RM extends RootModel = any, ExtensionState = any> = Store<ExtractRootState<RM> & ExtensionState

该类型系统会指明 DuraStore 的标准结构 , RootModel 指的是你传入的 initialModel 的具体类型 , 我们可以通过 typeof 直接进行获取 , 传入 initialModel 的具体类型 的目的是为了让你 在得到 store 之后使用 getState() 函数之后能够获得连续性的提示。

我们注意到 该类型系统中还有一个类型 叫做 ExtensionState , 这是作为补充使用, 早某些场景下(参照 @dura/async-loading 插件的实现), 我们实际的 rootState 不仅仅只包含我们的业务 model 中的 state ,在这种场景下,我们需要对 类型系统进行补充,这个时候我们则需要用到 ExtensionState , 例:

const initialModel = {
  /**
   * 用户模块
   */
  user: UserModel
};

export type RootModel = typeof initialModel;

const duraStore = create({
  initialModel,
  plugins: [createAsyncPlugin(), createLoadingPlugin(initialModel), createImmerPlugin(), createSelectorsPlugin()]
}) as DuraStore<RootModel> & AsyncDuraStore<RootModel> & SelectorsDuraStore<RootModel>;

// 由于上面as了 DuraStore 类型系统 , 所以此处有提示 , 可以持续的 点 下去
duraStore.getState().user.name;

//此处是loading插件提供的一个内置model , 并不在我们的业务model 中 ,
//是由于 AsyncDuraStore<RootModel> 类型系统所以依然能够给出一个比较良好的提示信息
duraStore.getState().loading.onAsyncChangeName;

ExtractRootState 类型系统

@dura/core 提供

type ExtractRootState<M extends RootModel>

该类型系统主要用来通过你的 rootModel 解构出 rootState 的类型路径, 使用案例:

type RootState = ExtractRootState<RootModel>;

function mapStateToProps(state: RootState) {
  return {
    name: state.user.name //此处会有比较良好的提示哦
  };
}

ExtractReducersRunner 类型系统

功能与 ExtractRootState 相似, 区别在于 ExtractReducersRunner 提取的是 reducer 的信息

Api

create( config:Config ) : DuraStore

创建 DuraStore 的核心方法 , 参数如下

  • 必传】initialModel 所有需要载入的 model
  • 可选】initialState 对应 redux 的 initialState
  • 可选】middlewares 额外的中间件 ,与 redux 用法相同
  • 可选】plugins 插件列表
const store = create({
  initialModel: {
    user: {
      state: {},
      reducers: {}
    }
  }
}) as DuraStore<RootModel>;

reducers 规范

为了让 ts 能够有比较友好的提示信息,我们将 reducers 设计成两层函数的结构, 也就是高阶,如下所示:

const userModel = {
  state: {
    name: "默认"
  },
  reducers: {
    //第一层 , 这里的参数表示外部在调用你的时候需要传入的
    // 第一个参数一般为 action 的payload
    // 第二个参数一般为 action 的meta
    onChangeName(payload: { newName: string }) {
      //第二层 , 这里的参数是dura注入进来的,一般指的是当前model 的state
      return function(state: State): State {
        return { ...state, name: payload.newName };
      };
    }
  }
};

reducerRunner

当我们成功创建一个 duraStore 之后, 在 duraStore 中会自动挂载一个 reducerRunner 对象,我们可以将这个对象导出,在其他地方引用,该对象就是你需要针对 reducer 发起 Action 的时候用到的 , 具体用法这里就不做说明了, ts 的提示系统会告诉你怎么做

官方插件

例子

征集更过的基于 dura 的项目案例, 可直接提供在 issue 当中

一个简单的教程

为了让大家尽可能的简单一些上手,我们提供了一个整合了所有官方插件以及类型系统的库 @dura/plus(其实类型系统的配置对于新手来说还是具备一定的复杂度的),下面通过 jest 单元测试展示一个简单的 demo 教程

it("简单的测试", function(done) {
  const initialState = {
    name: undefined as string,
    sex: undefined as "男" | "女",
    age: undefined as number
  };

  type State = typeof initialState;

  const UserModel = {
    state: initialState,
    reducers: {
      onChangeName(payload: { name: string }) {
        return function(state: State) {
          state.name = payload.name;
          return state;
        };
      }
    },
    effects: {
      onAsyncChangeName(payload: { name: string }, meta?: LoadingMeta) {
        return async function(effectApi: EffectAPI<RootState>) {
          await effectApi.delay(1000);
          await effectRunner.address.onAsyncChangeCity({ city: "南京" });
          reducerRunner.user.onChangeName(payload);
        };
      }
    }
  };

  const initialAddressState = {
    detailName: undefined as string,
    city: undefined as string
  };

  type AddressState = typeof initialAddressState;

  const AddresModel = {
    state: initialAddressState,
    reducers: {
      onChangeCity(payload: { city: string }) {
        return function(state: AddressState) {
          state.city = payload.city;
          return state;
        };
      }
    },
    effects: {
      onAsyncChangeCity(payload: { city: string }) {
        return async function(effectApi: EffectAPI<RootState>) {
          await effectApi.delay(1000);
          reducerRunner.address.onChangeCity(payload);
        };
      }
    }
  };

  const initialModel = {
    user: UserModel,
    address: AddresModel
  };

  type RootState = PlusRootState<typeof initialModel>;

  const store = createDura(initialModel, {}) as PlusDuraStore<typeof initialModel>;

  const { reducerRunner, effectRunner } = store;

  effectRunner.user.onAsyncChangeName({ name: "张三" }, { loading: true });

  expect(store.getState().loading.user.onAsyncChangeName).toEqual(true);

  setTimeout(() => {
    expect(store.getState().loading.user.onAsyncChangeName).toEqual(false);
    expect(store.getState().user.name).toEqual("张三");
    expect(store.getState().address.city).toEqual("南京");
    done();
  }, 3000);
});

版本更新日志

  • 【2019.02.06】 1.0.0 主体功能上线

  • 【2019.02.06】 1.0.1 修复关于 State 类型系统不支持 boolean 的问题 511492c

  • 【2019.02.06】 1.0.2 修复关于 Selector 类型系统不正确 的问题 e41f5f2

  • 【2019.02.09】 1.0.3

    • @dura/plus 中移除了 selector 方案 f249a6a 考虑到其实集成 selector 方案并没有太大的意义,反而会增加新手对于 dura 的理解成本,所以决定将该方案从@dura/plus中进行移除,后续其实也不推荐使用 @dura/selectors 插件,并且该插件将不会提供版本迭代维护,建议直接使用 reselect
  • 【2019.02.11】 1.1.0 新功能上线 6202efb 1b0fb00

    • 解耦 compose
    • 支持外部传入 compose 用以覆盖 redux compose
    • 支持外部传入 createStore 用以覆盖 redux createStore
    • 为了尽可能的自由度,移除 @dura/plus 中默认装载的 @dura/immer
  • 【2019.02.11】 1.1.1 放宽 reducer 的类型校验7cb21af

  • 【2019.02.11】 1.1.2 发布卡住了更新内容同 1.1.1

  • 【2019.02.11】 1.1.3 发布卡住了更新内容同 1.1.1

  • 【2019.02.12】 1.2.0 更精准、友好的 reducer、effect 调用提示 7c9c54e

  • 【2019.02.18】 1.2.1 修复启用 loading 插件之后,effect 异常导致 loading 状态无法取消的问题 92e65df

小版本计划

  • other reducers支持普通的 reducer,例如: redux-form 等框架体系
  • 继承 model 的继承,虽然我觉得没有太大必要,不过还是会做一些业务背景调查,来看一下是否需要做支持

大版本计划

  • 沙盒环境 用于主动快速定位线上 bug
  • validation 机制(摸索阶段),虽然说我们鼓励扁平的数据格式,但是免不了总会存在数据嵌套,这里主要需要摸索在数据嵌套,尤其是列表的大背景下,如何使用 验证机制 更为友好
  • 脚手架工程

dura's People

Watchers

 avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.