@frank-shaw
2017-02-13T08:17:18.000000Z
字数 4972
阅读 1988
javaScript
Redux本质上是一个state Manager,专门负责管理应用中的state。一个应用中有很多组件需要记录状态,redux希望以state tree的方式来记录一个应用同一时刻的所有组件状态。要形成state tree,必须有合并(combine)的概念,下面的reducer会讲到。
个人认为它的time travel功能是非常强大的,可以回溯每一个状态。这个功能在调试中应该格外有用。当然,有如此功能也是基于它的工作原理的,让我们来看看其基本概念。
action表征的是每一个state包含的信息源,本质上只有action中携带的数据才是重要的。action在形式上是一个简单的Object,如:
{type:'LIKE_ACTICLE', articleId:24}{type:'ADD_TODE', text:'Read the Redux docs.'}
action Object有一个要求:必须包含一个type属性,同时type属性的值建议大写(成为常量,同时在大型应用中,建议将这些常量单独放在一个文件中)。
action creator表示的是一类返回action的函数。值得注意的是,在同步情况下,action creator只允许返回一个action Object;在异步情况下,action creator除了返回action Object之外,还允许返回一个function。
actions仅仅描述了每一个state包含的信息,但是并没有没描述state之间是怎样变化的。这个工作就交给了reducers来做。它的基本格式是这样的:
(previousState, action) => newState
reducers负责的就是state的变化的过程,由前一种state,添加了某一个action之后,转变为了一个新的state。它的这一作用就要求reducer函数必须是一个纯函数:无论何时,相同的输入将会得到相同的输出。同时,在书代码的时候,我们也必须切记这一点:reducer函数必须是纯函数。
同时,针对reducers有一个建议是:将data state 与 UI state尽量区分开,不要混杂在一起。一个简单的例子是这样的:
//将data state 单独写成一函数function todos(state = [], action) {switch (action.type) {case ADD_TODO://返回的是全新的state,通过ES6新形式return [...state,{text: action.text,completed: false}]case TOGGLE_TODO:return state.map((todo, index) => {if (index === action.index) {//返回的是全新的state,通过assign方法return Object.assign({}, todo, {completed: !todo.completed})}return todo})default:return state}}function todoApp(state = initialState, action) {switch (action.type) {case SET_VISIBILITY_FILTER:return Object.assign({}, state, {visibilityFilter: action.filter})case ADD_TODO:case TOGGLE_TODO:return Object.assign({}, state, {todos: todos(state.todos, action)})default:return state}}
多个reducers可以合并为一个reducer,通过函数combineReducers()来实现。如果合并的先后次序不同以及实行多次合并,那么就可以有了reducers tree的概念,进而可以发展为state tree的概念。将多个reducers转化为一个reducer,也是符合store的概念的,因为Redux要求只能有一个store。
reducer的使用原则:
上面讲到了reducer,讲述了其如何依据action的不同来改变state的。那么store就是讲所有的reducer合并在一起的工具。你可以将其看成一个将军,率领的是众多的reducer。
我们知道通过combineReducer()可以将多个reducer合并为单个reducer,那么直接使用createStore(combinedReducer),我们就可以获得包含了所有reducer的store。
store有如下功能:
那么我会问:store中的dispatch(action)与reducer(previousState, action) => newState所实现的功能是一样的,那么它们之间有什么区别呢?让我们来开始尝试模拟一下createStore的内部实现吧(实际的实现比这复杂):
const createStore = (reducer) => {let state;let listeners = [];const getState = () => state;const dispatch = (action) => {//dispatch内部实际上调用的就是reducer函数以作为更新statestate = reducer(state,action);//每一次状态更新,都会触发所有的listeners做出相应,listener()内部可以调用getState()获取最新statelisteners.forEach(listener => listener());};const subscribe = (listener) =>{//添加listenerlisteners.push(listener);//返回的函数作用是解除监听return function unsubscribe(){const index = listeners.index(listener);listener.splice(index,1);}}return {getState, dispatch, subscribe};}
让我们来理一理redux的工作流程
我们已经明白action表示的含义。不管通过何种方式来调用(如:在界面上点击某个按钮,然后通过对应的函数调用),调用store.dispatch(action)之后,就有了接下来的第二步。
store会传送两个参数给reducer(可能是简单的一个reducer,也可能是组合过后的combinedReducer):现阶段的state tree、上一步的action。我们知道reducer返回的是一个新的state,这个state也有可能是在combinedReducer包含的多个reducers所返回的states所组成的state tree。
这是最后一步。我们得到了全新的state tree,但是依然需要将这些全新的state通过一定的处理之后才能够显示在界面上。这就是listener的作用。
异步操作在前端操作中格外常见,redux如何解决异步带来的问题呢?前面讲到的几个基本概念里面都是以同步作为大背景的。
关注事物之间的不同,可以更好地了解它。与同步相比,异步的不同之处在于:
下面来看一个例子:
//文件名称:action.jsimport fetch from 'isomorphic-fetch'export const REQUEST_POSTS = 'REQUEST_POSTS'function requestPosts(subreddit) {return {type: REQUEST_POSTS,subreddit}}export const RECEIVE_POSTS = 'RECEIVE_POSTS'function receivePosts(subreddit, json) {return {type: RECEIVE_POSTS,subreddit,posts: json.data.children.map(child => child.data),receivedAt: Date.now()}}// Meet our first thunk action creator!// Though its insides are different, you would use it just like any other action creator:// store.dispatch(fetchPosts('reactjs'))function fetchPosts(subreddit) {// Thunk middleware knows how to handle functions.// It passes the dispatch method as an argument to the function,// thus making it able to dispatch actions itself.return function (dispatch) {// First dispatch: the app state is updated to inform// that the API call is starting.dispatch(requestPosts(subreddit))// The function called by the thunk middleware can return a value,// that is passed on as the return value of the dispatch method.// In this case, we return a promise to wait for.// This is not required by thunk middleware, but it is convenient for us.return fetch(`https://www.reddit.com/r/${subreddit}.json`).then(response => response.json()).then(json =>// We can dispatch many times!// Here, we update the app state with the results of the API call.dispatch(receivePosts(subreddit, json)))// In a real world app, you also want to// catch any error in the network call.}}
这些只是基本的概念,需要在设计项目中不断学习,才能够更加深刻了解其内涵。
参考文档:
1.http://redux.js.org/docs/basics/
2.https://egghead.io/courses/getting-started-with-redux