flux是什么?
flux是一种状态管理框架。严格版的MVC框架。基本原则:
- 单向数据流
- 保存状态只读
缺点:
- 1、Store之间依赖关系。
- 2、难以进行服务端渲染。因为每个Store都是全局唯一的对象
- 3、Store混杂了逻辑和状态。Store封装了数据和处理逻辑,不合适热加载
在flux体系中,如果两个Store之间有逻辑依赖关系,就必须用上Dispatcher.waitFor(dispatchToken)函数。dispatchToken是AppDispatcher.rgister返回的来用于识别Store的,这样产生了不同Store之间的显示依赖关系。如果状态数据分散在多个Store中,容易造成数据冗余。
Dispatcher对象的作用是把一个action对象分发给多个注册了的Store
redux
redux(reducer+flux)强调三个基本原则:
- 单向数据流,只存储在唯一的一个Store上
- 保存状态只读。不能直接修改状态,要修改Store的状态,必须要通过派发action对象完成
- 数据改变只能通过纯函数完成
这个纯函数就是
reducer(state, action),根据state和action产生新的对象然后返回。返回值必须完全由state和action决定,而且不产生任何副作用,也不能修改参数state和action对象。reducer只负责计算,不负责存储状态
flux
//ActionType.js
export const INCREMENT = 'increment';
export const DECREMENT = 'decrement';
//CounterActions.js
import * as ActionTypes from './ActionTypes.js';
import AppDispatcher from './AppDispatcher.js';
export const increment = (counterCaption) => {
AppDispatcher.dispatch({
type: ActionTypes.INCREMENT,
counterCaption: counterCaption
});
};
export const decrement = (counterCaption) => {
AppDispatcher.dispatch({
type: ActionTypes.DECREMENT,
counterCaption: counterCaption
});
};
//AppDispatcher.js
import {Dispatcher} from 'flux';
export default new Dispatcher();
//CounterStore.js
import AppDispatcher from '../AppDispatcher.js';
import * as ActionTypes from '../ActionTypes.js';
import {EventEmitter} from 'events';
const CHANGE_EVENT = 'changed';
const counterValues = {
'First': 0,
'Second': 10,
'Third': 30
};
const CounterStore = Object.assign({}, EventEmitter.prototype, {
getCounterValues: function() {
return counterValues;
},
emitChange: function() {
this.emit(CHANGE_EVENT);
},
addChangeListener: function(callback) {
this.on(CHANGE_EVENT, callback);
},
removeChangeListener: function(callback) {
this.removeListener(CHANGE_EVENT, callback);
}
});
CounterStore.dispatchToken = AppDispatcher.register((action) => {
if (action.type === ActionTypes.INCREMENT) {
counterValues[action.counterCaption]++;
CounterStore.emitChange();
} else if (action.type === ActionTypes.DECREMENT) {
counterValues[action.counterCaption]--;
CounterStore.emitChange();
}
});
export default CounterStore;
//Counter.js
import CounterStore from '../stores/CounterStore.js';
class Counter extends Component {
constructor(props) {
super(props);
this.onChange = this.onChange.bind(this);
this.onClickIncrementButton = this.onClickIncrementButton.bind(this);
this.onClickDecrementButton = this.onClickDecrementButton.bind(this);
this.state = {
count: CounterStore.getCounterValues()[props.caption]
}
}
shouldComponentUpdate(nextProps, nextState) {
return (nextProps.caption !== this.props.caption) ||
(nextState.count !== this.state.count);
}
componentDidMount() {
CounterStore.addChangeListener(this.onChange);
}
componentWillUnmount() {
CounterStore.removeChangeListener(this.onChange);
}
onChange() {
const newCount = CounterStore.getCounterValues()[this.props.caption];
this.setState({count: newCount});
}
onClickIncrementButton() {
Actions.increment(this.props.caption);
}
onClickDecrementButton() {
Actions.decrement(this.props.caption);
}
render() {
const {caption} = this.props;
return (
<div>
<button onClick={this.onClickIncrementButton}>+</button>
<button onClick={this.onClickDecrementButton}>-</button>
<span>{caption} count: {this.state.count}</span>
</div>
);
}
}
Counter.propTypes = {
caption: PropTypes.string.isRequired
};
export default Counter;
redux区别于flux:
- 1、action构造函数只负责创建一个对象并返回,通过store.dispatch派发action。而flux的action会dispatcher实现分发给store
- 2、只有一个store。而flux有多个store,更新数据,
- 3、新增reducer纯函数,负责计算,返回一个新的state
//CounterAction.js 返回一个action对象
import * as ActionTypes from './ActionTypes.js';
export const increment = (counterCaption) => {
return {
type: ActionTypes.INCREMENT,
counterCaption: counterCaption
};
};
export const decrement = (counterCaption) => {
return {
type: ActionTypes.DECREMENT,
counterCaption: counterCaption
};
};
//Store.js 只有一个store
import {createStore} from 'redux';
import CounterReducer from './CounterReducer.js';
const initValues = {
'First': 0,
'Second': 10,
'Third': 20
};
const store = createStore(CounterReducer, initValues);
export default store;
//CounterReducer.js 只负责计算,不负责存储状态
import * as ActionTypes from './ActionTypes.js';
export default (state, action) => {
const {counterCaption} = action;
switch (action.type) {
case ActionTypes.INCREMENT:
return {...state, [counterCaption]: state[counterCaption] + 1};
case ActionTypes.DECREMENT:
return {...state, [counterCaption]: state[counterCaption] - 1};
default:
return state
}
}
//Cunter.js
import store from '../Store.js';
import * as Actions from '../Actions.js';
class Counter extends Component {
constructor(props) {
super(props);
this.state = this.getOwnState();
}
getOwnState() {
return {
value: store.getState()[this.props.caption]
};
}
componentDidMount() {
store.subscribe(this.onChange);
}
componentWillUnmount() {
store.unsubscribe(this.onChange);
}
onChange() {
this.setState(this.getOwnState());
}
onIncrement() {
store.dispatch(Actions.increment(this.props.caption));
}
onDecrement() {
store.dispatch(Actions.decrement(this.props.caption));
}
}