实例讲解Redux
示例代码地址在这里 ,你可以通过git clone
命令clone到本地,也可以直接下载。
然后通过执行npm install
安装所依赖的模块 。之后便可以通过node
执行示例代码。
什么是Redux
随着 JavaScript 单页应用开发日趋复杂,JavaScript 需要管理比任何时候都要多的 state (状态)。 这些 state 可能包括服务器响应、缓存数据、本地生成尚未持久化到服务器的数据,也包括 UI 状态,如激活的路由,被选中的标签,是否显示加载动效或者分页器等等。
简单来说Redux
就是javaScript
的状态管理器。同时,Redux
支持React
、Angular
、Ember
、jQuery
等。
到这里你可能并不知道该如何使用Redux
,别着急,在了解如何使用之前,需要先理解相关的基本概念。示例代码可查看demo1/app.js
State
在一个可以进行用户交互的界面上,当我们触发了交互事件的时候,界面也会跟着进行更新。例如在一个app中的下拉刷新,当我们进行下拉刷新这个动作时,会去请求服务器数据,当返回结果时app的页面会更新。界面的改变我们就可以看做是状态的变化。而状态的变化是由于我们进行了某个动作。在Redux中,尤其需要注意的是State是只读的,且状态的变化只能通过Reducer函数进行转换,
Action
Action就是你与界面进行交互的行为。它是一个对象且必须要包含一个type
属性,用来描述你要做的是什么事。
Store
Store
就是应用状态的容器。用来维护应用的state
、获取state
、更新state
、监听state
变化等。这里需要注意的是整个应用只有一个Store
Reducer
一个纯函数,接收当前的state
和一个action
参数,根据当前的行为,即action
的type
属性返回一个新的state
。状态的更新只能通过这个函数。
Talk is cheap, Show me the code!
可能看了上述内容你仍然不知道什么是Redux
,Redux
能干什么。下面结合实例demo1/app.js
中的内容理解上述概念。
1.引入相关模块(这里没有使用ES6
的方式是为了能使用node
命令直接查看结果,node
命令无法识别import
。当然你也可以通过配置babel
的方式使用import
语法)
var { createStore } = require('redux');
//import { createStore } from 'redux' //es6方式引入
2.定义Reducer
,来进行状态的更新
var defaultState = 'JavaScript'
function programLanguage(state = defaultState, action) {
switch (action.type) {
case 'IOS':
return action.language;
case 'WEB':
return action.language;
case 'SNACK':
return action.language;
default:
return state;
}
}
从上述代码可以看到,我们定义了一个programLanguage
方法,其实这就是一个Reducer
,它接受了当前state
和action
参数。用来更新state
。
3.创建store
。
let store = createStore(programLanguage);
再次强调整个应用中只能有一个store
,即createStore
函数只能被调用一次。
createStore
的定义如下:
function createStore(reducer, preloadedState, enhancer);
可以看到的是createStore接受三个参数
reducer:就是上述的reducer函数,这里不再赘述
preloadedState: 初始state
enhancer:组合store creator的高级函数,返回一个新的强化过的store creator。
4.更改应用的状态
store.dispatch({ type: 'default' });
store.dispatch({ language: 'Swift', type: 'IOS' });
store.dispatch({ language: 'Ruby', type: 'WEB' });
store.dispatch({ language: 'Python', type: 'SNACK' });
通过dispatch
方法将action
发送到reducer
函数中,进行状态的更新。
执行node demo1/app.js
的打印结果如下
JavaScript
JavaScript Swift
JavaScript Swift Ruby
JavaScript Swift Ruby Python
当执行第一句代码store.dispatch({ type: 'default' });
时 返回的是JavaScript。是由于dispatch方法内部会调用在createStore
中注册的Reducer
函数,即programLanguage
函数。由programLanguage
的内部逻辑return
的是一个defaultState
。此时整个应用的状态为JavaScript
当执行第二句代码store.dispatch({ language: 'Swift', type: 'IOS' });
时 返回了JavaScript Swift
。是由于type
为IOS
,programLanguage
返回的是state + ' ' +action.language;
因为上一句代码将应用的状态变为了JavaScript
,所以结果自然就是JavaScript Swift
。同时整个应用的状态也会变成JavaScript Swift
。
同理,第三句和第四句代码也是如此。
5.订阅状态的更新
store.subscribe(() =>
console.log(store.getState())
);
为了方便查看状态的变化。这里调用了subscribe
方法,每当状态发生变化时都会调用其回调函数。
store.getState()
获取应用的当前状态。
优化操作
Action creator
随着页面的增加,Action
必然也会越来越多,如果Action
内部所携带的消息也很多。此时如果需要状态的更新,就需要写复杂的Action
,造成代码可读性差,文件臃肿。所以我们可以使用Action creator
来构造Action
。当需要进行状态更新就传入相应的数据到Action creator
中生成Action
并返回。
在demo2
中
我们创建了一个HomeAction
的文件专门用来存放Action Creator
。在此文件中我们创建了三个Action creator
,分别是action_ios
、action_web
、action_snack
,并将创建Action
的任务交给这三个函数。
与此同时在我们demo2/app.js
中需要dispatch
一个action
的时候。只需要从Action creator
中取出Action
就行了。并不需要我们手动的去创建,如此一来便大大增加代码的可读性,可维护性。
分散Reducer
同样的随着业务量的增加,Reducer
必定也会越来越大。所以我们可以按模块的不同来拆分Reducer。将一个大的Reducer
拆分成几个小的reducer
。
在demo3
中reducer
文件夹中,我们按照模块的不同的拆分homeReducer
和profileReducer
。分别用来处理home
页面和profile
页面状态的更新。
那么问题来了,这么拆分如何使用createStore
去创建store
呢,毕竟createStore
只能传一个Reducer
参数。而且createStore
只能调用一次。
别着急,在redux
中提供了一个combineReducers
的方法,用来组合我们的Reducer
。
const mainReducer = combineReducers({
homeReducer,
profileReducer
})
let store = createStore(mainReducer);
最后
谢谢阅读,如果感觉本文对你有用,那将是我的荣幸。如果有写的错误的地方也请留言指正。