Redux 是 JavaScript 状态容器, 提供可预测化的状态管理。css
那什么是能够预测化,个人理解就是根据一个固定的输入,必然会获得一个固定的结果。html
redux是专门为react开发的,但并非只能用于react,能够用于任何界面库。前端
随着单页面应用的普及,web app内部须要管理的状态愈来愈多,这些状态可能来自服务器端,用户输入的数据,用户交互数据,当前UI状态,本地的缓存数据等等。如何可以有条理的管理这些数据,成为前端开发中一个难题。react
使用redux的程序,全部的state都存储在一个单一的数据源store内部,相似一个巨大的对象树。web
state是只读的,能改变state的惟一方式是经过触发action来修改typescript
为了描述 action 如何改变 state tree , 你须要编写 reducers。express
reducers是一些纯函数,接口当前state和action。只须要根据action,返回对应的state。并且必需要有返回。npm
一个函数的返回结果只依赖于它的参数,而且在执行过程里面没有反作用,咱们就把这个函数叫作纯函数redux
顾名思义,action就是动做,也就是经过动做来修改state的值。也是修改store的惟一途径。segmentfault
action本质上就是一个普通js对象,咱们约定这个对象必须有一个字段type,来表示咱们的动做名称。通常咱们会使用一个常量来表示type对应的值。
此外,咱们还会把但愿state变成什么样子的对应的值经过action传进来,那么这里action可能会相似这样子的
{ type: 'TOGGLE_TODO', index: 5 }
Action 只是描述了有事情发生了这件事实,但并无说明要作哪些改变,这正是reducer须要作的事情。
Reducer做为纯函数,内部不建议使用任何有反作用的操做,好比操做外部的变量,任何致使相同输入但输出却不一致的操做。
若是咱们的reducer比较多,比较复杂,咱们不能把全部的逻辑都放到一个reducer里面去处理,这个时候咱们就须要拆分reducer。
幸亏,redux提供了一个api就是combineReducers Api。
store是redux应用的惟一数据源,咱们调用createStore Api建立store。
第一步搭建开发环境,这里不介绍了,参考上一篇文章 手把手教会使用react开发日历组件,搭建环境部分
搭建好环境切换到目录下面
npm install redux --save
把index.tsx修改成之下代码。
import { createStore, combineReducers, applyMiddleware } from 'redux' var simpleReducer = function(state = {}, action) { return { user: { name: 'redux' } } } var store = createStore(simpleReducer) console.log(store.getState())
咱们看到控制台打印出来的一个包含user信息的这么一个对象。
咱们使用到了几个api? createStore
建立store,store.getState()
获取store,也就是惟一数据源的根节点。
上文咱们也讲过,action的状况可能会比较多,redux也提供了combineReducers
Api。若是咱们有多个reducer,咱们就可使用起来了。
那咱们建立多个reducer测试一下,代码以下:
import { createStore, combineReducers, applyMiddleware } from 'redux' function user(state = {name: 'redux'}, action) { switch (action.type) { case 'CHANGE_NAME': return { ...state, name: action.name } } return state } function project(state = {name: 'min-react'}, action) { switch (action.type) { case 'CHANGE_NAME': return { ...state, name: action.name } } return state } var rootReducer = combineReducers({ user, project }) var store = createStore(rootReducer) console.log(store.getState())
如咱们所预料同样,咱们获得拥有两个字段的根store。
第一步咱们把html改形成这个样子,新增了一点标签
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <style type="text/css"> * { margin: 0; padding: 0; } </style> </head> <body> <div id="userName"></div> <input id="userNameInput"/><button id="userNameButton">更改userName</button> <script src="./dist/main.js"></script> </body> </html>
第二步,修改index.tsx,以下
import { createStore, combineReducers, applyMiddleware } from 'redux' import { func } from 'prop-types' function user(state = {name: 'redux'}, action) { switch (action.type) { case 'CHANGE_USER_NAME': return { ...state, name: action.name } } return state } function project(state = {name: 'min-react'}, action) { switch (action.type) { case 'CHANGE_PROJECT_NAME': return { ...state, name: action.name } } return state } var rootReducer = combineReducers({ user, project }) var store = createStore(rootReducer) function render(state = store.getState()) { var $userName = document.getElementById('userName') $userName.innerHTML = state.user.name } render() console.log(store.getState())
咱们看到页面正确的显示了咱们user的名称。下一步咱们须要作的就是经过用户的操做,改变store的值,进而触发view的更新。
因而咱们新增了这块代码:
store.subscribe(function() { render() }) // 绑定用户事件 var $userNameInput = document.getElementById('userNameInput') var userNameButton = document.getElementById('userNameButton') userNameButton.onclick = function() { var value = $userNameInput.value store.dispatch({ type: 'CHANGE_USER_NAME', name: value }) }
咱们看到保存以后,当咱们输入值以后,点击更改,页面的值随着改变。
可是控制台报了一个错误,TS2339: Property 'value' does not exist on type 'HTMLElement'.
,这是因为typescript强类型校验没经过致使的。只要加这段代码就行了
var $userNameInput = document.getElementById('userNameInput') as HTMLInputElement
看到了吧,redux就是这么简单。
其余全部上层应用,都是在此基础上开发的,因此开发一个redux应用的步骤就是
结合react,其余view类库,开发步骤莫不如此。
咱们也看到了,咱们的reducer只能作同步应用,若是咱们须要在reducer,作一些延迟操做,可怎么办
社区已经有成熟的类库作这件事件
npm install redux-thunk --save
redux自己已经提升了很好的扩展机制,就是中间件。这点很相似express的中间件。
//引入新的类库 import { createStore, combineReducers, applyMiddleware, compose } from 'redux' import thunk from 'redux-thunk' ... //store部分作以下修改 const finalCreateStore = compose(applyMiddleware(thunk))(createStore) const store = finalCreateStore(rootReducer, {})
redux-thunk的做用就是让dispatch方法不只仅只接收action对象,还能够包含一个方法。咱们能够在这个方法内部去调用异步代码
咱们把dom事件部分作了以下改造
userNameButton.onclick = function() { var value = $userNameInput.value store.dispatch<any>(function(dispatch, getState) { setTimeout(() => { dispatch({ type: 'CHANGE_USER_NAME', name: value }) }, 2000) }) }
能够看到页面元素确实在2s以后发生了变化,实际业务中啊,咱们这里能够作一些异步操做。
至于redux原理,以及源码和中间件的源码讲解能够参照个人另一篇文章 阅读redux源码