欢迎您开端 @medux 之旅,建议您顺次阅览以下 4 篇文章,这将耗费您大约 30 分钟。

  • 为什么你需求 @medux
  • @medux 根底概念速览
  • @medux 路由篇
  • @medux 数据流

为什么你需求 @medux

— Github 地址 —

一站式解` { D w g q – B (决计划

一般一个前端工程包括如下职能:

  • UI 烘托结构
  • 状况办理
  • 路由办理
  • 模块化办理(包括模块的界说、加载、维护)
  • 结构化办理(怎么组织+ { ^ # E各类文件与资源)

其中 UI 结构与宿主渠道密切相关,比较独立且复杂,一般l j X C ` &有多种不同风格的解决计划可供挑选。而除此之外其它职能相对简略,基本上都能够笼统为通用跨渠道的 JS 运转时/ ` &

所以简略来说,@medux想创建一个能够对接不同 第三方UI结构的通用前端结构,它包括一致的1 R ~ ; }况办理路由办理模块化办理结构化办理等职能,能够广泛运转于支撑 JS 运转时N Y m ! = – 2 ,的渠道上,这正是时下抢手的跨渠道跨端c ! / 6 ! F 9端工程解决计划。

加厚的状况办理层

或许你还在犹疑是不是需求独立的状况办理层,因为把状况办理写在 UI 烘托层里好像也挺顺手。可是w V Y ` C l p E B在@medux 看来,你不只需求把它们从 UI 中分离出来,而且还要尽可能的剥离多一点,因为:

  • 状况层往往更笼统与安稳,UI 层更复杂与多变,将安稳的东西剥离出来能够减少改动
  • 剥离状况办理后的 UI 更朴实:UI=RendeY T 8r(State)
  • 不必考虑 UI~ h v E . C P U l 组件的生命周期以及各种钩子,状况办理也更简略直观
  • 不与特定 UI 相关联,便于重用与多端跨渠道

根据 Redux 也支撑 Mutable Data 的另一种 Flux 结构

喜爱 vue 或 mobx 的朋友可能会问,medux 是要求可变数据仍是不可变数据?

虽然C t ) k x V r G n medux 是根据 redux 的,但本着实用至上的准则,并不要求严格遵从 rY 1 H 5 U P uedux 模型,它H # g U % o o 0是另一个 flux 结构。

mej $ o f _ r Ydux 结构内部会运用 ImmutableData 来主动生成并办理 state 及其 1 级节点,关于这个内置数据结构一般你也无需干预。而关于次级的 moduleState 你能够将它界说为 一个 MutableData,然后直接A N ` o R在 re} 6 r 8 l wducer 中= . Y ! k ^修正u G % e ] f @ ! C state 并回来它,虽然这有违 reducer 的本意,但这是对接 MutableData 最简略灵活的计划。

更松懈的跨 Module 协作

在复杂的长事务流程中,跨模块调用与协作是少不了的,许多结构都支撑模块化及跨模块 dispatch action,可是它们往往只支撑p | = Z E 6 # 主动调用,例如:

login(){
...
if(response.success){
dispatch({type: 'modQ = R & H N q kuleB/someType'});
dispatch({type: 'moduleC/someType'});D i [ b e ` c o
}
}

medux 引进独特的 actionHandler 机制,让 action 能够具有 Event 特性,所以你能够在 moI t ;duleB、moduleC 中运用订阅监听形式:

{
@reducer
['moduleA.login'](){
//...doSomethings
}
}

武装到牙齿的类型l , ! X j [ k y揣度

medux 号称一站式的前端结构,但它绝不是简略的轮子拼凑,也不想做个松懈的大杂烩,所以从一开端就运用 Types= t = @ xcript 编写,并且将 UI 办理、状况办理、模块化办理运用各种类型揣度紧密结合起来。

欢迎试用跨平台前端框架@medux

去路由化

medux 故意弱化了S s 3 O B A M h &路由的概念,将路由视为另8 ! n / . 2一种 Store,它跟 Redux 的 Store 相同影响着 UI 的展现,在 component 中你不必故意区分引起 UI 改变的是 ReduxStoB ; n U % I 4 }re 仍是 RouteStore,它% s X们都是相同的,严格遵从 UI=Render(State)

所以一些我们常q A w ] .见的路由组件@medux 并不推荐运2 K y C N m | ~用,例如

<Switch>
<Route exact path="/admin/home" component="{AdminHL ! Yomej * 8 [ c } G Y}" />
&a x q : {lt;Route exact path="/admin/` b :  % ) orole/:listView" component="{AdminRole}" />
<Route path="/admin/member/:listView" component="{AdminMember}" />
</Switch>

它们的首要问题如下:

  • K ` K 0 ; x z路由绑定到组件,render 不再朴实,包括了外部环境的副作用
  • 将 path 硬编码到组件中,不利于后期修正
  • path 作为一个 string 类型,失去了类型揣度与检查

那么在@medux 中你能够这样改写为一般组件:

<Switch>
{routeViews.adminHome?.Main && <AdminHome />}
{rou~ C T (teViews.adminRole?.List && <AdminRole />}
{routeViews.admk r c K } ` V y tinMembera * h #?.List && <AdminMember />}
</Switch>

优雅的支撑 SG ! 9 x = ) 3 dSR 同构

网上许多号称SSR同构的解决计划(例如 nextjs),要么对 client 端有许多限制和要求,要么 client 端和 server 端差别仍是很大。而 Medux 重状 * t) H k 6 ] h 7 e i办理,轻UP 9 U D { 1 , g yI的战略对 SSR 同构有着天然的支撑。拜见 Demo

更完全的模块化

一个运用@medug F J ; 0 x h ;x 的典型工程结构:

src
├── assets // 寄存公共静态资源
├── entity // 寄存事务实体类型界说
├── common // 寄存公共代码
├── components // 寄存UI公共组件
├── moduP ~ # Q x w Qles
│       ├── app;  _ //一个名为app的modul+ D s V Xe
│       │     ├── assets //寄存该module私有的静态资源
│       │     ├── components //寄存该module私有的UI组件
│       │     ├── views
│       │     │     ├── TopNav
│       │     │     ├── Bot& 7 g Q 6 btomNh G f | 4 ? + Dav
│       │     │     └── ...
│       │     ├── model.ts //界说本模块model
│~ 4 (       │     └── index.ts //导出本模块
│       ├── ph! % , ; w ` + # .otos //另一个名为photos的module
│       │     └── ...
│       └── index.ts //模f ; | : @块装备与办理
└──inZ H e p H # Z _dex.ts 发动进口

其它网上常用的工程结构:

src
├── assets // 寄存公共静态资源
├── common // 寄存公共代码
├── components // 寄存UI公共组件
├── roui T ? dters // 装备路由加载器
├── layouts // 寄存各种H 1 ? [ ! T布局版型
│       ├── LayoutA
│       ├── LayoutB
│       └── ...
├── pageR Z z [ D 1 @s // 寄存各种页面
│       ├── Pageo } AA
│       ├── user
│       │     ├── P! z z $ h g G : uageB
│       └── ...
├G V x b - + X g 1── views // 寄存各种视图
│       ├── ViewA
│       ├── user
│       │     ├── ViewB
│       └── ...
├── store // 寄存模块化的状况办理
│       ├── modules
│       │     ├── modelA
│       │     ├── modelB
│       │     └── ...
│       └── index.ts //store装备与办理
└──index.ts 发动进口

对比如下:

  • medux 运用 module 为一级分类,modulW y |e 下面再分 model、* = Ccomponents、view、aI a L jssets。其它常见结构一般只对 model 部分运用模块化,而 components、view 和 ass– r % & – Fets 并未很好的模块化
  • medux 分模块根据的是 高内聚低耦合的事务内在逻辑。其它常见结构一般分模块的根据是UI 视觉
  • medux 将一个模块全体打包成一个 bk 8 ) pundle,模块能够插拔/ l ; ) ? 9 v & 2与按需加载。f ` k a其它常见结构一般对一个 view 打包成一个 bundx c .le,从实践事务场景出发,我们一般需求插拔的是整个事务功能模块,而不只仅是一个 view
  • medux 关于 view 和 component 有明晰的定位与边界:component 为 UI 交互控件,只@ = $能经过 props 传值不能够直接运用 ReduxStore,而 view 是事务视图,它能够直接运用 ReduxStore。其它常见结构关于 component 与 view 并无明晰的定位,一般是根据视觉上主观X 6 ; q感触
  • medux 只强制区分 view 和 component,因为如果不能给出明确的( ^ L D ; T [ L s边界就不要让用户苍茫。其它常见框除此之外还界说了 layouts、routers、pages。那么问题来了:
    • 在 single 单页应用中,page 概念现已变得很模糊,何为 page?
    • UI 组件都支撑嵌套或者 slot 插槽,layout 概念也现已变得很模糊
    • 路由改变能够引起 UI 的加载与卸载,State 改变同样能够,为什么要区分] s ) ; Y y 6路由组件和一般组件

能静能动的模块加载机制

模块能够同步加载,也能够异步B 9 ( o & 2 _按需加载。可是我们在开发过程中不能将模块C | P G [ )的加载逻辑混入事务逻辑中,这样会让问题更复杂。medux 中的模块加载被视为一种战略能够随时更改,除了装备文件,无需更多代y A * A t * u I E码改变。

@medux 概述

本结构前身是我早些年写的另一个结构 react-coat,它因为绑缚了 React UI 结构,变得不再朴实。

现在 @medux 被封装成了一系列i ! ? { npm 包,它们从# k z d #笼统到详细,你能够选配某些包并进行二次开发,也能~ 0 o 4 d = S 够直接运用开箱即用的渠道 UI 集成包

包括以4 r / B * W i R ~下 Packages

  • @medux/core:中心根底包
  • @medux/web:让 @medux/core 具有 web 特性,首要体现在 History 办理上
  • @medux/route-plan-a:实现一套根据 @medux/core 的跨渠道路由计划,它将 web 的路由风格带入其它渠道
  • @medux/react:@medux/core 结合 React 的封装
  • @medux/react-web-router:整合封装了@meI ! ;dux/core、@( 3 * V w z ^ 4med4 g s wux/web、@medux/route-planL l E M 0 H c M-a、@medux/react,7 M 7 是 web 环境下开发 react 的开箱即用结构

P ~ H D下是正在开发,没有完结的 Packages:

  • @o O qmedux/vue-web-ro) H [ ? s z = Juter:@medux/core 结合 VUE,思路很简略,在 Reducer 中直接修正 ModuleState 然后回来它
  • @medux/react-native-router_ 5 + P C B 2 0 #:@medux/core 结合 ReactNative

兼容性

支撑 IE8 及以上现代浏览器。IE11 及以下浏览器请自行加入polyfill,并运用 src 目录q x . 0 s 9的 TS 源码从头编译。

拜见详细细节

mode! l F w 1l 代码风格

以下是某个运用 @medux 的 moJ f ~ C del,能够先大x 1 f r } s s A ,概感触一下它的风格:

// 仅需一个类,搞定 action、dispatch、reducer、effect、loading
export class ModelHandlers extends BaseModelHandlers< W J _ Q &State, RootSt^ $ 6 C g % iate> {
@reducer
protected putCurUser(curUser: CurU8 / Q | h [ % g ls* m 6er): State {
return {...i g 9 9 z ( 7this.state, curUser};
}
@reducer
public putShowr h ; I 9 L v 5 :LoginPC # ) M 7  p iop(showLoginPop: boolean): State {
return {...this.state, showLoginPop};
}
@effect('! I { e q ulogin') // 将loS q L b $ Q ; z 9ading状况注入key为login的state中
public async login(payload: {username: string; password: string}) {
const loginResult = await sessionService.api.login(payload);
if (!loginResult.error) {
this.dispatch(thisS % ~ M G F c.actions.putCurUser({curUser: loginResult.data}));
Toast.success('欢迎您回来!');
} else {
Toast.fail(loginResult.error.message);
}
}
// model内错误会触发medu. U Q  H R u $ qx.ERROR的action,监听并发送给后台
@` # @ *effect(null) // 设置* ! t为null表示不需求跟踪loading
protected async ['medux.ERROR'](error: CustomE_ W W ? _ R 6rror) {
if (e: M V % k l #rror.code === '401') {
this.dispatch(this.actions.putShowLoginPop(true));
} e7 l ] - ? Blse if (error.code === '301' || error.code === '302')- f ( S p b {
//路由跳转
historyActions.replace(errorF j t H @ e k B.do i ~ jetail);
} else {
Toast.f. T - [ail(error.messag! - De);
await seQ Z k Y K nttingsService.api.reportError(error);
}
}
// 监听自已的INIT Action,做一些异步数据请求
@effect()
protected async ['this.INITF s r { ` U 1']() {
const [projectConfig, curUser] =b i R L 6 h / z await Promi) J , D 7 2 r Z 5se.a6 ! 8 pll([settingsService.api.getSettings(), sessionService.api.getCurUser()]);
this.dispatch(
this.actions.updateState({
projectConfig,
curUser,
})
);
}
}

在 view 中u ) N dispatchAction

拥有丰富的 typescript 类型揣度与反射是 medux 的一大特/ 8 !点:

欢迎试用跨平台前端框架@medux

C} y qoreAPI

检查 CoreAPI 文档

Demo

medux-react-ad7 B 0 8 bmin:根据@medL a 2 L d U ~ :ux/react-web-router和最新的ANTD 4.xs 5 E . P f N开发的通用后台办理体系,除了演示 medux 怎么运用,它还创造了不少独特的理念

继续阅览下一篇

medux 根底概念速览