flux
facebook推出的构建用户界面的应用架构
- 单向数据流,在view层不能直接修改数据,要修改数据只能通过提交action,然后dispatcher到stroe,使得整体数据的变化是可控的。
- dispatcher作为连接action和store的通道,一个dispatcher必须注册各个action type的回调函数,从而实现,在回调函数中调用stroe的各种函数,让store实现变化。
- 在view层监听store的变化,从而更改view内部的state
- 官方实现版本
个人理解
- dispatcher是否可以去掉,从flux/util提供的reduceStore来看,也是可以丢掉的。
- 太繁琐了,需要自己在view层监听store的变化,从而去改变state,应该设计一种根store传递到view,提交action时,修改store,从而直接导致view的变化。
flux/utils
flux应该也知道其官方实现的flux太繁琐了,所以进化出flux/utils,其中有几个重要的概念
container
- 控制view,收集信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34import AppView from '../views/AppView';
import {Container} from 'flux/utils';
import TodoActions from '../data/TodoActions';
import TodoDraftStore from '../data/TodoDraftStore';
import TodoEditStore from '../data/TodoEditStore';
import TodoStore from '../data/TodoStore';
function getStores() {
return [
TodoEditStore,
TodoDraftStore,
TodoStore,
];
}
function getState() {
return {
draft: TodoDraftStore.getState(),
editing: TodoEditStore.getState(),
todos: TodoStore.getState(),
onAdd: TodoActions.addTodo,
onDeleteCompletedTodos: TodoActions.deleteCompletedTodos,
onDeleteTodo: TodoActions.deleteTodo,
onEditTodo: TodoActions.editTodo,
onStartEditingTodo: TodoActions.startEditingTodo,
onStopEditingTodo: TodoActions.stopEditingTodo,
onToggleAllTodos: TodoActions.toggleAllTodos,
onToggleTodo: TodoActions.toggleTodo,
onUpdateDraft: TodoActions.updateDraft,
};
}
export default Container.createFunctional(AppView, getStores, getState);
- 控制view,收集信息
reduceStore
- 为了解决之前的store太繁琐。在container执行的时候,会传入action的所有handler,view层手动执行handler,从而触发dispatch。在store的reduce方法中,会针对各种action type返回全新的store
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17//view
<input
checked={areAllComplete ? 'checked' : ''}
id="toggle-all"
type="checkbox"
onChange={props.onToggleAllTodos}
/>
// action
toggleAllTodos() {
TodoDispatcher.dispatch({
type: TodoActionTypes.TOGGLE_ALL_TODOS,
});
}
// store
case TodoActionTypes.TOGGLE_ALL_TODOS:
const areAllComplete = state.every(todo => todo.complete);
return state.map(todo => todo.set('complete', !areAllComplete));
- 为了解决之前的store太繁琐。在container执行的时候,会传入action的所有handler,view层手动执行handler,从而触发dispatch。在store的reduce方法中,会针对各种action type返回全新的store
解决了最初版本中,需要在view层监听数据变化,从而触发view更新。
redux
- 和flux加上flux/util很相似。就三个概念,action描述数据变化,reducer根据action来产生新的state。store就是联系action和reducer的。他会负责dispatch action。dispatch内部会执行reducer方法,从而更新state
- store有一个subscribe函数,用来注册数据变化的监听函数,dispatch时,会在执行完reducer后,执行subscribe注册的函数。
- 总的来讲就是比flux简洁许多,整个结构清晰明了。
- 使用中间件的方式来处理异步数据流,比较有名的有redux-thunk和redux-saga和redux-promise
- 示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22const reducer = (state = {count: 0}, action) => {
switch (action.type){
case 'INCREASE': return {count: state.count + 1};
case 'DECREASE': return {count: state.count - 1};
default: return state;
}
}
const actions = {
increase: () => ({type: 'INCREASE'}),
decrease: () => ({type: 'DECREASE'})
}
const store = createStore(reducer);
store.subscribe(() =>
console.log(store.getState())
);
store.dispatch(actions.increase()) // {count: 1}
store.dispatch(actions.increase()) // {count: 2}
store.dispatch(actions.increase()) // {count: 3}
react-redux
- 通过provider 在getChildContext 把store挂在context上,子组件就能通过context获取store,解决了用props层层传递的问题
- 容器组件都是connect过的,connect通过context获取store,然后再map到纯UI组件。
- 为了避免每个组件都从context上获取数据,又衍生了容器组件和purComponent。并且提供connect方法,把container包装一下,connect去context上获取数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14export default class Provider extends Component {
getChildContext() {
return { store: this.store }
}
constructor(props, context) {
super(props, context)
this.store = props.store
}
render() {
return Children.only(this.props.children)
}
}
vuex
- 使用单一状态树,一个对象就包含了全部的应用层级状态。
- 几个概念,state(状态)、mutations(同步更改)、actions(异步更改)、getters(store的计算属性)、Module(模块化分割单一状态树)。
- 为了解决在不同组件中重复声明内部状态和store的联系,增加了mapState、mapMutations、mapGetters、mapActions几个辅助函数。
mutation必须是同步的,异步数据用dispatch action来触发异步操作。action中获取要异步数据后使用commit mutation来更新state。利用promise来解决异步数据的管理
1
2
3
4
5
6
7
8
9
10
11
12
13actions: {
actionA ({ commit }) {
return new Promise((resolve, reject) => {
setTimeout(() => {
commit('someMutation')
resolve()
}, 1000)
})
}
}
store.dispatch('actionA').then(() => {
// ...
})例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state,payload) {
state.count++
}
},
getters: {
doneTodos: state => {
return state.todos.filter(todo => todo.done)
}
},
actions: {
increment (context) {
context.commit('increment')
}
}
})
store.commit('increment', 10)
const moduleA = {
state: { ... },
mutations: { ... },
actions: { ... },
getters: { ... }
}
const moduleB = {
state: { ... },
mutations: { ... },
actions: { ... }
}
const store = new Vuex.Store({
modules: {
a: moduleA,
b: moduleB
}
})
Mobx
- 响应式、依赖收集
- 和react结合的例子,对视图用@observer装饰器,声明为依赖。数据变化的时候会自动执行数据对应的依赖
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24import {observer} from 'mobx-react';
@observer
class TimerView extends React.Component {
render() {
return (<button onClick={this.onReset.bind(this)}>
Seconds passed: {this.props.appState.timer}
</button>);
}
onReset () {
this.props.appState.resetTimer();
}
};
ReactDOM.render(<TimerView appState={appState} />, document.body);
appState.resetTimer = action(function reset() {
appState.timer = 0;
});
setInterval(action(function tick() {
appState.timer += 1;
}), 1000);
rxjs
- 结合了观察者模式、迭代器模式 和 使用集合的函数式编程,以满足以一种理想方式来管理事件序列所需要的一切。事件驱动、流管理。
- 结合react的例子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29import messages from './someObservable';
class MyComponent extends ObservableComponent {
constructor(props) {
super(props);
this.state = {messages: []};
}
componentDidMount() {
this.messages = messages
// 在数组中累积我们的消息
.scan((messages, message) => [message].concat(messages), [])
// 当得到一条新消息时进行渲染
.subscribe(messages => this.setState({messages: messages}));
}
componentWillUnmount() {
this.messages.unsubscribe();
}
render() {
return (
<div>
<ul>
{this.state.messages.map(message => <li>{message.text}</li>)}
</ul>
</div>
);
}
}
export default MyComponent;