原文地址:https://acme.top/mobx-react-stores-docreact
mobx-react-stores 是爲了方便將 antd pro 改成使用 mobx 而開發的,裏面的思路和代碼大量的參考了 dva 和 umi 的實現ios
npm i mobx-react-stores
獲取 stores
實例對象shell
import {stores} from 'mobx-react-stores';
mobx-react-stores 能夠用做狀態管理,也是其最核心的目的,接口設計的比較簡單,主要就兩個:npm
stores
注入待管理的 model
對象model
若是爲對象則必須包含一個 string 類型的 namespace
屬性,用做其在 stores
中的惟一標識,也就是 stores
中的屬性名稱model
也能夠爲一個 Promise
對象,Promise
必須返回一個包含 string 類型的 namespace
屬性namespaceAlias
可選,用來爲 model
起別名,若是存在則 model
能夠不包含 namespace
屬性dva
的 dispatch
,用來經過 namespace/action
這種形式快速訪問 model
對象中的函數type
爲字符串類型,格式爲 namespace/action
payload
爲任意類型,可選,用於向所調用的函數中傳遞參數,namespace/action
函數接收到的參數就是 payload
dispatch
通常直接被注入到了上下文中,方便組件調用mobx-loading
能實現相似 dva-loading 的效果,方便監聽 model 及其 action 的執行狀態,減小開發人員對組件 show
hide
的操做axios
import {loadingStore} from 'mobx-loading'
mobx-react-stores 默認集成了 mobx-loading,做爲 stores 的內置屬性,namespace 爲 loading
,因此能夠直接從 stores 中獲取 loadingStore 對象數組
const {loading} = stores;
// 經過 actions 獲取指定 namespace/action 的當前加載狀態 loading.actions['goods/fetchList'] // 經過 models 獲取指定 namespace 的當前加載狀態 // namespace 下任何 action 加載狀態爲 true,則此 namespace 的加載狀態即爲 true loading.models.goods // 經過 global 獲取全局的加載狀態 // 只要任何一個 model 的狀態爲 true,則 global 的狀態即爲 true loading.global // 從 mobx-react-stores 和 mobx-loading 中均可以獲取 namespace 裝飾器 // 用做 class,方便向 class 中注入 namespace 屬性 import {namespace} from 'mobx-loading'; import {namespace} from 'mobx-react-stores';
// 獲取 namespace 和 loading 裝飾器 import {namespace, loading} from 'mobx-react-stores'; import {fetchRandomUser} from "@/services/demo"; // 默認會爲 class 添加 namespace 屬性,值爲將 class 類名轉換爲首字母小寫的字符串 // 此處 namespace 值爲 randomUser @namespace class RandomUser { user; message; // 經過 loading 裝飾器,向 loadingStore 中注入當前 action 的加載狀態 @loading fetchUser = async () => { this.user = null; this.message = null; const response = await fetchRandomUser().catch(this.onRejected); if (response.results) { const user = response.results[0]; this.change({ user: { name: `${user.name.first} ${user.name.last}`, email: user.email, picture: user.picture.large }, message: null }); } } change = ({user, message}) => { this.user = user; this.message = message; } onRejected = (e) => { return { status: 500, message: e.message } } } export default new RandomUser(); // -----------------------RandomUser.js-------------------------------- import React from 'react'; import {inject} from 'mobx-react' @inject(({stores: {dispatch, loading, randomUser}}) => { return { dispatch, randomUser, loading: loading.models.randomUser } }) class RandomUser extends React.PureComponent { onFetchUser = () => { const {dispatch} = this.props; dispatch({ type: 'randomUser/fetchUser' }) } render() { const {loading, randomUser} = this.props; if (loading) { // do someting }else{ const {user, message} = randomUser; // do someting } } } export default RandomUser;
@loading(names: string | array | null) - loading 裝飾器做用於 model 內的函數,能夠接收字符串和數組的參數antd
例如: @loading('randomUser/fetchList')、@namespace(['randomUser', 'fetchList']) 與上面的示例代碼結果同樣app
loading
裝飾器優先使用當前 class 的 namespace 屬性和 函數的名稱(首字母小寫)生成此 action 的惟一標識,無 namespace 屬性則使用 class 的名稱(首字母小寫)。上門示例中 loading 無參則其函數的加載狀態可由 loadingStore.actions['randomUser/fetchList']
取得less
上面示例中的 loading 是裝飾器,與 stores.loading 不一樣。stores.loading 是 loadingStore 注入 stores 後的屬性dom
爲了方便管理路由以及向 stores 中注入 model,mobx-react-stores 提供了相似 umi 管理路由的方法,不過沒有像 umi 那樣去生成臨時文件,而是更直接的方式
建議在
src
目錄下新建default.router.js
文件來存放路由,若是模塊多而複雜,能夠將各個模塊的路由分散開來而後聚合到這個文件中
路由示例
export default [ // user { path: '/user', // 佈局文件 component: () => import('./layouts/UserLayout'), LoadingComponent = require('./components/PageLoading').default, Routes: [require('./pages/Authorized').default], authority: ['admin', 'user'], routes: [ // 從 /user 重定向到 /user/login {path: '/user', redirect: '/user/login'}, {path: '/user/login', name: 'login', component: () => import('./pages/User/Login')}, { name: 'register', path: '/user/register', models: () => [import('./pages/User/models/register')], component: () => import('./pages/User/Register') }, { name: 'register.result', path: '/user/register-result', component: () => import('./pages/User/RegisterResult'), }, ], } ]
() => [import('xxx/model1'), import('xxx/model2')]
、() => import('xxx/model1')
、[() => import('xxx/model1'), () => import('xxx/model2')]
等形式。主要方便於子路由的 models 和上級路由 models 進行數組合並,一些公共的 models 能夠放在上級路由中,方便管理。()=>import('xxxx'))
使用路由
import {formatterRoutes, renderRoutes} from 'mobx-react-stores'; import router from './default.router.js'; // ... do someting // 格式化 const routes = formatterRoutes(this.router, this.stores); // 渲染路由 let children = renderRoutes(routes); return ( <Router history={history}> {children} </Router> ); // ... do someting
mobx-react-stores 推薦使用 react-intl 來管理國際化
注入 Locale
import {stores, Locale} from 'mobx-react-stores'; // ... do someting // 可參考 Antd Pro const translations = { 'en-US': { messages: { ...require('../locales/en-US.js').default, }, locale: 'en-US', antd: require('antd/lib/locale-provider/en_US'), data: require('react-intl/locale-data/en'), momentLocale: '', }, 'zh-CN': { messages: { ...require('../locales/zh-CN.js').default, }, locale: 'zh-CN', antd: require('antd/lib/locale-provider/zh_CN'), data: require('react-intl/locale-data/zh'), momentLocale: 'zh-cn', }, // ... 其餘語言包 }; // 注入 stores.add(new Locale('zh-CN', translations)); // ... do someting
切換語言
stores.locale.change('zh-CN'); // 或者 dispatch({ type: 'locale/change', payload: 'zh-CN' });
爲了簡化開發,方便使用 國際化、history 等,mobx-react-stores 提供了集成好的接口
使用示例
在 src
目錄下新建 index.js
文件做爲入口文件
// 兼容 IE九、IE10 ~ 若是須要的話請解開封印 // import 'react-app-polyfill/ie9'; // import 'react-app-polyfill/stable'; import React from 'react'; import {LocaleProvider} from 'antd'; import {app} from 'mobx-react-stores'; import get from 'lodash/get'; import './utils/axios'; // 初始化攔截器 import './global.less'; import * as serviceWorker from './serviceWorker'; app.stores = require('./models').default; app.router = require('./default.router').default; app.defaultLoadingComponent = require('./components/PageLoading').default; // 渲染時回調,方便一些額外的操做,好比注入 antd 的國際化 app.renderCallback = (children) => { const {locale} = app.stores; const antd = get(app.stores, 'locale.translation.antd', undefined); if (antd) { return ( <LocaleProvider locale={antd.default || antd}> {children} </LocaleProvider> ); } return children; }; app.render(document.getElementById('root')); // If you want your app to work offline and load faster, you can change // unregister() to register() below. Note this comes with some pitfalls. // Learn more about service workers: https://bit.ly/CRA-PWA serviceWorker.unregister();
集成後
intl
、routing
兩個對象,可用於國際化和操做路由intl.formatMessage()
等函數routing.push()
、routing.replace()
等路由操做