Redux
-
createStore只有一个函数,返回4个闭包 -
dispatch只做了一件事,调用reducer然后调用subscribe的listener-
Redux并不知道state有没有发生变化,更不知道state具体哪里发生了变化
-
view层需要知道哪一部分需要更新,只能通过脏检查
-
-
react-redux-
在store.subscribe上挂回调
-
每次发生subscribe就调用
connect传进去mapStateToProps和mapDispatchToProps,然后脏检测props的每一项 -
如果有n个组件connect,每当dispatch一个action的时候,无论做了什么粒度的更新,都会发生O(n)时间复杂度的脏检测
-
每次reducer执行完Redux就直接调用listener了
-
在短时间内发生了多次修改(例如用户输入),不可变的开销,加上redux用字符串匹配action的开销,脏检测的开销,再加上view层的开销,时间复杂度就是O(n)
-
Mobx
- 本身独立,不与任何view层框架互相依赖:可以随意选择合适的view层框架
-
为每个组件创建一个Watcher,在数据的getter和setter上加钩子
-
当组件渲染的时候(例如,调用render方法)会触发getter
-
然后把这个组件对应的Watcher添加到getter相关的数据的依赖中(例如,一个Set)
-
当setter被触发时,就能知道数据发生了变化,然后同时对应的Watcher去重绘组件
-
当数据发生变化时,可以精确地知道哪些组件需要被重绘,数据变化时重绘的过程是O(1)的时间复杂度
-
Mobx:把数据声明为observable
选择Mobx的原因
-
学习成本少
- 不需要额外异步处理库
-
面向对象编程,也支持函数式编程
-
用
@observableand@observer,以面向对象编程方式使得JavaScript对象具有响应式能力 -
Redux最推荐遵循函数式编程
-
-
模版代码少:相对于Redux的各种模版代码
- 不需要编写 ctionCreater,reducer,saga/thunk 模板代码
不选择Mobx的可能原因
-
过于自由
- Mobx提供的约定及模版代码很少
-
大型项目需要特别注意可拓展,可维护性
架构
创建 store
-
Redux先配置
- 创建store,用
redux-thunk或redux-saga中间件以支持异步action,用Provider将store注入应用
// src/store.js import { applyMiddleware, createStore } from "redux"; import createSagaMiddleware from 'redux-saga' import React from 'react'; import { Provider } from 'react-redux'; import { BrowserRouter } from 'react-router-dom'; import { composeWithDevTools } from 'redux-devtools-extension'; import rootReducer from "./reducers"; import App from './containers/App/'; const sagaMiddleware = createSagaMiddleware() const middleware = composeWithDevTools(applyMiddleware(sagaMiddleware)); export default createStore(rootReducer, middleware); // src/index.js … ReactDOM.render( <BrowserRouter> <Provider store={store}> <App /> </Provider> </BrowserRouter>, document.getElementById('app') ); - 创建store,用
-
Mobx 直接将所有store注入应用
import React from 'react'; import { render } from 'react-dom'; import { Provider } from 'mobx-react'; import { BrowserRouter } from 'react-router-dom'; import { useStrict } from 'mobx'; import App from './containers/App/'; import * as stores from './flux/index'; // set strict mode for mobx // must change store through action useStrict(true); render( <Provider {...stores}> <BrowserRouter> <App /> </BrowserRouter> </Provider>, document.getElementById('app') );
注入Props
-
Redux
// src/containers/Company.js class CompanyContainer extends Component { componentDidMount () { this.props.loadData({}); } render () { return <Company infos={this.props.infos} loading={this.props.loading} /> } } // function for injecting state into props const mapStateToProps = (state) => { return { infos: state.companyStore.infos, loading: state.companyStore.loading } } const mapDispatchToProps = dispatch => { return bindActionCreators({ loadData: loadData }, dispatch); } // injecting both state and actions into props export default connect(mapStateToProps, { loadData })(CompanyContainer); -
Mobx
@inject('companyStore') @observer class CompanyContainer extends Component { componentDidMount () { this.props.companyStore.loadData({}); } render () { const { infos, loading } = this.props.companyStore; return <Company infos={infos} loading={loading} /> } }
定义Action/Reducer等
-
Redux
// src/flux/Company/action.js export function fetchContacts(){ return dispatch => { dispatch({ type: 'FREQUEST_COMPANY_INFO', payload: {} }) } } // src/flux/Company/reducer.js const initialState = {}; function reducer (state = initialState, action) { switch (action.type) { case 'FREQUEST_COMPANY_INFO': { return { ...state, contacts: action.payload.data.data || action.payload.data, loading: false } } default: return state; } } -
Mobx
// src/flux/Company/store.js import { observable, action } from 'mobx'; class CompanyStore { constructor () { @observable infos = observable.map(infosModel); } @action loadData = async(params) => { this.loading = true; this.errors = {}; return this.$fetchBasicInfo(params).then(action(({ data }) => { let basicInfo = data.data; const preCompanyInfo = this.infos.get('companyInfo'); this.infos.set('companyInfo', Object.assign(preCompanyInfo, basicInfo)); return basicInfo; })); } $fetchBasicInfo (params) { return fetch({ ...API.getBasicInfo, data: params }); } } export default new CompanyStore();
异步Action
-
Redux:另外添加
redux-thunk或redux-saga以支持异步action,Mobx并不需要额外配置-
redux-saga
// src/flux/Company/saga.js // Sagas // ------------------------------------ const $fetchBasicInfo = (params) => { return fetch({ ...API.getBasicInfo, data: params }); } export function *fetchCompanyInfoSaga (type, body) { while (true) { const { payload } = yield take(REQUEST_COMPANY_INFO) console.log('payload:', payload) const data = yield call($fetchBasicInfo, payload) yield put(receiveCompanyInfo(data)) } } export const sagas = [ fetchCompanyInfoSaga ];
-