单页面应用的痛点
对于复杂的单页面应用,状态(state)管理非常重要。state 可能包括:服务端的响应数据、本地对响应数据的缓存、本地创建的数据(比如,表单数据)以及一些 UI 的状态信息(比如,路由、选中的 tab、是否显示下拉列表、页码控制等等)。如果 state 变化不可预测,就会难于调试(state 不易重现,很难复现一些 bug)和不易于扩展(比如,优化更新渲染、服务端渲染、路由切换时获取数据等等)。
Redux 就是用来确保 state 变化的可预测性,主要的约束有:
state 以单一对象存储在 store 对象中
state 只读
使用纯函数 reducer 执行 state 更新
Actions、Reducers 和 Store
action 可以理解为应用向 store 传递的数据信息(一般为用户交互信息)。在实际应用中,传递的信息可以约定一个固定的数据格式,比如: Flux Standard Action。
为了便于测试和易于扩展,Redux 引入了 Action Creator:
function addTodo(text) {
return {
type: ADD_TODO,
text,
}
}
store.dispatch(addTodo(text))
reducer 实际上就是一个函数:(previousState, action) => newState
。用来执行根据指定 action 来更新 state 的逻辑。通过 combineReducers(reducers) 可以把多个 reducer 合并成一个 root reducer。
store 是一个单一对象:
管理应用的 state
通过
store.getState()
可以获取 state通过
store.dispatch(action)
来触发 state 更新通过
store.subscribe(listener)
来注册 state 变化监听器通过
createStore(reducer, [initialState])
创建
Middleware
middleware 其实就是高阶函数,作用于 dispatch 返回一个新的 dispatch(附加了该中间件功能)。可以形式化为:newDispatch = middleware1(middleware2(...(dispatch)...))
。
// thunk-middleware
export default function thunkMiddleware({ dispatch, getState }) {
return next => action =>
typeof action === 'function' ? action(dispatch, getState) : next(action)
}
通过 thunk-middleware 我们可以看出中间件的一般形式:中间件函数接受两个参数参数: dispatch 和 getState(也就是说中间件可以获取 state 以及 dispatch new action
)。中间件一般返回 next(action)
(thunk-middleware 比较特殊,它用于 dispatch 执行异步回调的 action)。store 的创建过程如下:
const reducer = combineReducers(reducers)
const finalCreateStore = applyMiddleware(promiseMiddleware, warningMiddleware,
loggerMiddleWare)(createStore)
const store = finalCreateStore(reducer)
异步 Actions
单页面应用中充斥着大量的异步请求(ajax)。dispatch(action) 是同步的,如果要处理异步 action,需要使用一些中间件。
redux-thunks 和 redux-promise 分别是使用异步回调和 Promise 来解决异步 action 问题的。
Redux 和传统 Flux 框架的比较
图来自 UNIDIRECTIONAL USER INTERFACE ARCHITECTURES
Redux 和 React
Redux 和 React 是没有必然关系的,Redux 用于管理 state,与具体的 View 框架无关。不过,Redux 特别适合那些 state => UI
的框架(比如:React, Deku)。
可以使用 react-redux 来绑定 React,react-redux
绑定的组件我们一般称之为 smart components
,Smart and Dumb Components 在 react-redux
中区分如下:
“Smart” Components | Top level, route handlers | Yes | Subscribe to Redux state | Dispatch Redux actions |
“Dumb” Components | Middle and leaf components | No | Read data from props | Invoke callbacks from props |