欢迎您开始 @medux 之旅,建议您依次阅读如下 4 篇文章,这将耗费您大约 30 分钟。javascript
-- Github 地址 ---html
一般一个前端工程包含以下职能:前端
其中 UI 框架与宿主平台密切相关,比较独立且复杂,一般有多种不一样风格的解决方案可供选择。而除此以外其它职能相对简单,基本上均可以抽象为通用跨平台的 JS 运行时。vue
因此简单来讲,@medux
想建立一个能够对接不一样 第三方UI框架
的通用前端框架,它包含统一的状态管理、路由管理、模块化管理、结构化管理等职能,能够普遍运行于支持 JS 运行时的平台上,这正是时下热门的跨平台跨端
前端工程解决方案。java
也许你还在犹豫是否是须要独立的状态管理层,由于把状态管理写在 UI 渲染层里彷佛也挺顺手。可是在@medux 看来,你不只须要把它们从 UI 中分离出来,并且还要尽量的剥离多一点,由于:react
喜欢 vue 或 mobx 的朋友可能会问,medux 是要求可变数据仍是不可变数据?git
虽然 medux 是基于 redux 的,但本着实用至上的原则,并不要求严格遵循 redux 模型,它是另外一个 flux 框架。github
medux 框架内部会使用 ImmutableData 来自动生成并管理 state 及其 1 级节点,对于这个内置数据结构一般你也无需干预。而对于次级的 moduleState 你能够将它定义为 一个 MutableData,而后直接在 reducer 中修改 state 并返回它,尽管这有违 reducer 的本意,但这是对接 MutableData 最简单灵活的方案。web
在复杂的长业务流程中,跨模块调用与协做是少不了的,不少框架都支持模块化及跨模块 dispatch action,可是它们每每只支持主动调用,例如:typescript
login(){
...
if(response.success){
dispatch({type: 'moduleB/someType'});
dispatch({type: 'moduleC/someType'});
}
}
复制代码
medux 引入独特的 actionHandler 机制,让 action 能够具备 Event 特性,因而你能够在 moduleB、moduleC 中使用订阅监听模式:
{
@reducer
['moduleA.login'](){
//...doSomethings
}
}
复制代码
medux 号称一站式的前端框架,但它毫不是简单的轮子拼凑,也不想作个松散的大杂烩,因此从一开始就使用 Typescript 编写,而且将 UI 管理、状态管理、模块化管理使用各类类型推断紧密结合起来。
medux 刻意弱化了路由的概念,将路由视为另外一种 Store,它跟 Redux 的 Store 同样影响着 UI 的展现,在 component 中你不用刻意区分引发 UI 变化的是 ReduxStore 仍是 RouteStore,它们都是同样的,严格遵循 UI=Render(State)
因此一些咱们常见的路由组件@medux 并不推荐使用,例如
<Switch>
<Route exact path="/admin/home" component="{AdminHome}" />
<Route exact path="/admin/role/:listView" component="{AdminRole}" />
<Route path="/admin/member/:listView" component="{AdminMember}" />
</Switch>
复制代码
它们的主要问题以下:
那么在@medux 中你能够这样改写为普通组件:
<Switch>
{routeViews.adminHome?.Main && <AdminHome />}
{routeViews.adminRole?.List && <AdminRole />}
{routeViews.adminMember?.List && <AdminMember />}
</Switch>
复制代码
网上不少号称SSR同构
的解决方案(例如 nextjs),要么对 client 端有不少限制和要求,要么 client 端和 server 端差异仍是很大。而 Medux 重状态管理,轻UI
的策略对 SSR 同构有着自然的支持。参见 Demo
一个使用@medux 的典型工程结构:
src
├── assets // 存放公共静态资源
├── entity // 存放业务实体类型定义
├── common // 存放公共代码
├── components // 存放UI公共组件
├── modules
│ ├── app //一个名为app的module
│ │ ├── assets //存放该module私有的静态资源
│ │ ├── components //存放该module私有的UI组件
│ │ ├── views
│ │ │ ├── TopNav
│ │ │ ├── BottomNav
│ │ │ └── ...
│ │ ├── model.ts //定义本模块model
│ │ └── index.ts //导出本模块
│ ├── photos //另外一个名为photos的module
│ │ └── ...
│ └── index.ts //模块配置与管理
└──index.ts 启动入口
复制代码
其它网上经常使用的工程结构:
src
├── assets // 存放公共静态资源
├── common // 存放公共代码
├── components // 存放UI公共组件
├── routers // 配置路由加载器
├── layouts // 存放各类布局版型
│ ├── LayoutA
│ ├── LayoutB
│ └── ...
├── pages // 存放各类页面
│ ├── PageA
│ ├── user
│ │ ├── PageB
│ └── ...
├── views // 存放各类视图
│ ├── ViewA
│ ├── user
│ │ ├── ViewB
│ └── ...
├── store // 存放模块化的状态管理
│ ├── modules
│ │ ├── modelA
│ │ ├── modelB
│ │ └── ...
│ └── index.ts //store配置与管理
└──index.ts 启动入口
复制代码
对好比下:
模块能够同步加载,也能够异步按需加载。可是咱们在开发过程当中不能将模块的加载逻辑混入业务逻辑中,这样会让问题更复杂。medux 中的模块加载被视为一种策略能够随时更改,除了配置文件,无需更多代码变动。
本框架前身是我早些年写的另外一个框架 react-coat,它由于捆绑了 React UI 框架,变得再也不纯粹。
如今 @medux 被封装成了一系列 npm 包,它们从抽象到具体,你能够选配某些包并进行二次开发,也能够直接使用开箱即用的平台 UI 集成包
如下是正在开发,还没有完成的 Packages:
支持 IE8 及以上现代浏览器。IE11 及如下浏览器请自行加入polyfill
,并使用 src 目录的 TS 源码从新编译。
参见具体细节
如下是某个使用 @medux 的 model,能够先大概感觉一下它的风格:
// 仅需一个类,搞定 action、dispatch、reducer、effect、loading
export class ModelHandlers extends BaseModelHandlers<State, RootState> {
@reducer
protected putCurUser(curUser: CurUser): State {
return {...this.state, curUser};
}
@reducer
public putShowLoginPop(showLoginPop: boolean): State {
return {...this.state, showLoginPop};
}
@effect('login') // 将loading状态注入key为login的state中
public async login(payload: {username: string; password: string}) {
const loginResult = await sessionService.api.login(payload);
if (!loginResult.error) {
this.dispatch(this.actions.putCurUser({curUser: loginResult.data}));
Toast.success('欢迎您回来!');
} else {
Toast.fail(loginResult.error.message);
}
}
// model内错误会触发medux.ERROR的action,监听并发送给后台
@effect(null) // 设置为null表示不须要跟踪loading
protected async ['medux.ERROR'](error: CustomError) {
if (error.code === '401') {
this.dispatch(this.actions.putShowLoginPop(true));
} else if (error.code === '301' || error.code === '302') {
//路由跳转
historyActions.replace(error.detail);
} else {
Toast.fail(error.message);
await settingsService.api.reportError(error);
}
}
// 监听自已的INIT Action,作一些异步数据请求
@effect()
protected async ['this.INIT']() {
const [projectConfig, curUser] = await Promise.all([settingsService.api.getSettings(), sessionService.api.getCurUser()]);
this.dispatch(
this.actions.updateState({
projectConfig,
curUser,
})
);
}
}
复制代码
拥有丰富的 typescript 类型推断与反射是 medux 的一大特色:
medux-react-admin:基于@medux/react-web-router
和最新的ANTD 4.x
开发的通用后台管理系统,除了演示 medux 怎么使用,它还创造了很多独特的理念