歡迎您開始 @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 怎麼使用,它還創造了很多獨特的理念