欢迎您开端 @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 故意弱化了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 !点:
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 根底概念速览