Backbone Design Patterns - Frontend
Comparison for frontend backbone design patterns: Flux, Redux, Vuex. Since Redux is commonly know as a replacement for Flux(declared event in its official website) and the development of Vuex has taken a lot of reference from Redux, so in the overall usecases, they are very similar.
In this article, I went through all the integration documents of the three and made a short summary on each of the frameworks core concepts.
Flux
老牌的 State 状态管理方案,核心模型有:
- Store: 状态储存对象.
- Dispatcher: action 全局路由对象,store 需要把自己注册到Dispatcher 方便监听全局事件, store 以及store 之间可以通过 wait 等等方式设定依赖关系.
var flightDispatcher = new Dispatcher();
// Keeps track of which country is selected
var CountryStore = {country: null};
// Keeps track of which city is selected
var CityStore = {city: null};
// Keeps track of the base flight price of the selected city
var FlightPriceStore = {price: null};
CountryStore.dispatchToken = flightDispatcher.register(function(payload) {
if (payload.actionType === 'country-update') {
CountryStore.country = payload.selectedCountry;
}
});
CityStore.dispatchToken = flightDispatcher.register(function(payload) {
if (payload.actionType === 'country-update') {
// `CountryStore.country` may not be updated.
flightDispatcher.waitFor([CountryStore.dispatchToken]);
// `CountryStore.country` is now guaranteed to be updated.
// Select the default city for the new country
CityStore.city = getDefaultCityForCountry(CountryStore.country);
}
});
flightDispatcher.dispatch({
actionType: 'city-update',
selectedCity: 'paris'
});
- Container 属于用来绑定Store -> Component 内部状态的封装器:
import { Component } from 'react';
import { Container } from 'flux/utils';
class CounterContainer extends Component {
static getStores() {
return [CounterStore];
}
static calculateState(prevState) {
return {
counter: CounterStore.getState(),
};
}
render() {
return <CounterUI counter={this.state.counter} />;
}
}
const container = Container.create(CounterContainer);
因为大部分Flux 中的使用场景随着 Hook 中的useContext 以及useReducer 能够被直接替代,因此目前Flux已经推出历史舞台了.
Redux
核心元素:
- Store
- dispatch action
- subscribe to store’s state update
- Reducer
- 处理action, 支持一个store 下注册多个reducer.
- 可以通过createSlice 生成对应的 reducer 切面,然后挂载到store 下面,对特定状态,特定操作进行处理.
使用方法:
- Component 通过Hook 进行获取:
- useDispatcher.
- useSelector 进行state 状态获取.
- Connect()
- 通过一些关键参数比如说:mapStateProps, mapDispatchProps component 进行封装.
具体API 接口, 参考 redux-toolkit
Vuex
下面一幅图看循环的几个step,Components 指代Vue视觉元素, action 表示抽象的各种可能触发数据更新的场景 对mutation 做出了明确的封装, Mutations 指代Store 中注册的mutation 字段中的相关可以对property 进行修改的函数.
- State
- 每个app 只有统一的一个单状态树.
- 对于多个store state 到property 转换,可以使用mapState 函数.
- components 可以有local state(property)与 global state 并存.
const Counter = {
template: `<div>8</div>`,
computed: {
count () {
return this.$store.state.count
}
}
}
computed: {
localComputed () { /* ... */ },
// mix this into the outer object with the object spread operator
...mapState({
// ...
})
}
-
Mutations
store 中的mutation 集中注册了可以修改状态的方法,需要通过commit 调用.
const store = createStore({
state: {
count: 1
},
mutations: {
increment (state) {
// mutate state
state.count++
}
}
})
store.commit('increment')
// 也可以传入 mutation 的参数,
// store.commit('increment', 10)
// increment by 10
- Actions
对mutation 进行的封装, 内部支持sync/async 操作.
const store = createStore({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment (context) {
context.commit('increment')
}
}
})
store.dispatch('increment')
global store 可以拆分为小的module,module 可以含有 state,mutation,actions,getters 甚至是nested modules 对象. modules中的各类方法,比如说 getters , actions 等等 都跟随module 的名称自动添加namespace:
modules: {
foo: {
namespaced: true,
getters: {
// `getters` is localized to this module's getters
// you can use rootGetters via 4th argument of getters
someGetter (state, getters, rootState, rootGetters) {
getters.someOtherGetter // -> 'foo/someOtherGetter'
rootGetters.someOtherGetter // -> 'someOtherGetter'
rootGetters['bar/someOtherGetter'] // -> 'bar/someOtherGetter'
},
someOtherGetter: state => { ... }
},
actions: {
// dispatch and commit are also localized for this module
// they will accept `root` option for the root dispatch/commit
someAction ({ dispatch, commit, getters, rootGetters }) {
getters.someGetter // -> 'foo/someGetter'
rootGetters.someGetter // -> 'someGetter'
rootGetters['bar/someGetter'] // -> 'bar/someGetter'
dispatch('someOtherAction') // -> 'foo/someOtherAction'
dispatch('someOtherAction', null, { root: true }) // -> 'someOtherAction'
commit('someMutation') // -> 'foo/someMutation'
commit('someMutation', null, { root: true }) // -> 'someMutation'
},
someOtherAction (ctx, payload) { ... }
}
}
}