Redux 的概念
Redux 是 JavaScript应用的状态容器,提供可预测化的状态管理;
- Javascript应用:是指任何Javascript构建的项目,而不是仅仅是React框架构建的项目,当然他们搭配用起来更加顺手;
- 状态容器:react项目中的state抽离出来集中在对象树状结构中store(状态容器),一个项目只能有一个store(状态容器);
- 可预测性:对于状态的更新变化都是通过纯函数进行操作的,纯函数的核心就是不管传入什么参数,都不会影响改变你传入参数的值;也就是每个新的state都会在旧的state进行产生,而且还不改变旧的state,这就是可预测性;
Redux和Vuex的区别和联系
- Redux,本身就是一个单纯的状态管理者,我们不追溯它的历史,从使用角度来说:它提供一个全局的对象store,store中包含state对象用以包含所有应用数据,并且store提供了一些reducer方法。这些方法可以自定义,使用调用者得以改变state的值。state的值仅为只读,如果需要更改则必须只能通过reducer。
- React-Redux,简单来说,它提供了一些接口,用于Redux的状态和React的组件展示结合起来,以用于实现状态与视图的一一对应。
- Vuex,吸收了Redux的思想,并且针对web应用的开发模式和Vue框架做了优化。所以它在实现了全量Redux的思想以外,为了与Vue框架结合,它也具备了类似React-Redux中的与框架结合的功能(尽管具体使用方式可能有差异),此外还一些更好用的特性
store和state是最基本的概念,Vuex没有做出改变。其实Vuex对整个框架思想并没有任何改变,只是某些内容变化了名称或者叫法,通过改名,以图在一些细节概念上有所区分。
- Vuex弱化了dispatch的存在感。Vuex认为状态变更的触发是一次“提交”而已,而调用方式则是框架提供一个提交的commit API接口。
- Vuex取消了Redux中Action的概念。不同于Redux认为状态变更必须是由一次”行为”触发,Vuex仅仅认为在任何时候触发状态变化只需要进行mutation即可。Redux的Action必须是一个对象,而Vuex认为只要传递必要的参数即可,形式不做要求。
- Vuex也弱化了Redux中的reducer的概念。reducer在计算机领域语义应该是”规约”,在这里意思应该是根据旧的state和Action的传入参数,”规约”出新的state。在Vuex中,对应的是mutation,即”转变”,只是根据入参对旧state进行”转变”而已。
- 总的来说,Vuex通过弱化概念,在任何东西都没做实质性削减的基础上,使得整套框架更易于理解了。
- 另外Vuex支持getter,运行中是带缓存的,算是对提升性能方面做了些优化工作,言外之意也是鼓励大家多使用getter。
什么用Redux?
先谈谈react,react是由facebook公司开源的前端框架;主要用于构建前端界面,将项目中的前端界面进行组件化处理,也就是说react是解决DOM层面上的抽象层;
对于一个完整的前端项目来说,这样是仅仅不够的,它并没有很好的解决组件之间的通信,只通过形式props实现了简单结构的组件通信,对于复杂结构的组件,如果进行采用props形式实现通信,会使整个项目变得难以维护,项目结构混乱(props嵌套传递层次太深);因此诞生出了另一套解决方案:基于Flux架构实现的Redux状态管理框架;
redux运作机制
redux中state属于状态容器,用来存放相应的数据状态,然后在自定义Action规则,它是把数据从应用传到 store 的有效载荷。属于 store 数据的唯一来源。你可以通过 store.dispatch() 将 action 传到 store。执行reducer纯函数进行数据状态更改;其中store是将它们连接起来的概念化的标识;redux具体运行机制如下图所示:
Redux 名词解释
createStore
创建 store 对象,包含 getState, dispatch, subscribe, replaceReducer
reducer
reducer 是一个计划函数,接收旧的 state 和 action,生成新的 state
action
action 是一个对象,必须包含 type 字段
dispatch
dispatch( action )
触发 action,生成新的 statesubscribe
实现订阅功能,每次触发 dispatch 的时候,会执行订阅函数
combineReducers
多 reducer 合并成一个 reducer
replaceReducer
替换 reducer 函数
middleware
扩展 dispatch 函数!
你再看 redux 流程图,是不是大彻大悟了?
Redux使用(后面使用react-redux会比较简单易懂)
首先使用 命令行 安装 redux
cnpm install redux --save
然后在 src 目录下 创建一个 store文件夹,创建index.js 文件 作为入口,创建reducer.js 文件(数据管理员)用于存放并暴露 state数据
其中 crateStore ()函数是用于创建 store对象的,将引入的 reducer 作为参数创建一个 store对象并暴露出去即可。
获取store的值
在页面中引入store即可通过store.getState()函数获取state的值
改变 store 的值
像Vue改变数据需要通过提交mutation一样,React改变Store的值需要使用action进行提交,通过action对象中的type作为判断操作类型,然后使用dispatch()函数进行action数据和操作类型的提交即可改变 store的值
给input输入框绑定修改的响应函数
<Input placeholder={this.state.inputValue} onChange={this.changeInput.bind(this)}></Input>
函数体里面通过创建一个action对象,规定使用一个type字段作为描述此次操作的类型,再绑定具体的数据,通过store.dispatch(action)提交修改
// 输入框改变值的响应函数 changeInput(e) { console.log(e.target.value); // 定义 action 对象,type表示此次操作的类型, const action = { type: "changeInput", value: e.target.value }; store.dispatch(action);//提交数据操作 console.log(store.getState()); // 打印新的数据 }
然后在reducer.js 文件中添加对这类型操作数据的方法
查看页面,store的值确实已经改变了
更新store后页面的state也要更新
由上面可以看到,与Vue不同的是,你store的数据即使已经改变,但是页面并没有更新数据,所以此时需要使用订阅函数给你的组件绑定更新,不订阅不会重新设置组件的state。
使用store.subscribe()传入更新函数进行订阅
其中更新函数要做的操作就是将新的store的值获取回来后设置给组件的state
完成store更新、组件state更新和页面更新
push数据
绑定一个新增按钮将输入框的值push到 store的数据列表字段中(记得函数要绑定this)
然后在reducer.js 文件中 新增判断类型分支来进行数据操作即可(可用switch简化操作)
所以 一次 对 store数据的操作分为以下几个步骤
- 安装redux(--save)
- src下创建store文件夹(index.js 文件 作为入口 暴露 store,reducer.js 文件(数据管理员)用于存放并暴露 state数据)
- 在页面中引入store即可通过store.getState()函数获取state的值
- 绑定响应事件
- 在事件里编写Redux的Action
- 将Action通过 store.dispatch()函数进行提交
- 绑定订阅更新函数(每个组件只需要一次)
- reducer业务逻辑的实现
文件结构优化
- actionTypes.js
为了防止 action 拼写错误(没做处理会没有任何反应,难以排查)以及代码冗余(查询操作可能很多地方都要用到)
在 store 文件夹下 创建 actionTypes.js 文件,将用到的 action的类型名称封装成常量,在需要使用的组件还有reducer.js文件中引入这些类型常量即可避免 代码冗余 和异常错误
- actionCreators.js文件
将 action 里的 提交数据操作封装到专门的 actionCreator.js文件作为函数的形式抛出(组件内保留store.dispatch提交数据),在有需要的地方引入函数即可。
注意点
store
必须是唯一的,多个store
是坚决不允许,只能有一个store
空间(只能在store下的index.js文件中调用一次createStore()函数)- 只有
store
能改变自己的内容,Reducer
不能改变(store直接操作数据,reducer只是返回一个操作后的state数据)Reducer
必须是纯函数(不能调用Date类或者发请求数据接口的操作),异步请求的数据可以作为参数传递给Reducer处理函数进行初始化componentDidMount(){ axios.get('https://www.easy-mock.com/mock/5cfcce489dc7c36bd6da2c99/xiaojiejie/getList').then((res)=>{ const data = res.data // 将异步请求的数据作为参数传递到reducer.js文件中进行初始化 const action = getListAction(data) store.dispatch(action) }) }
react-redux的使用
React-Redux
是一个React生态中常用组件,它可以简化Redux
流程。首先使用reac-redux 需要先安装 redux
cnpm install --save redux react-redux
同样的,需要在新的项目的src目录下创建store文件夹,以及里面的index.js文件和reducer.js文件
react-redux 访问store的数据
Provider 提供 访问 store的操作
从react-redux中引入Provider,然后引入store,在Provider标签中绑定store字段为引入的store,其子标签里的值都能访问该store
connect 连接 提供的store
在组件中引入connect连接父标签Provider提供的store,创建映射对象即可通过this.props.属性 访问store的数据
使用react-redux 修改 store数据
之前的redux修改数据需要在函数中创建action或者通过actionCreators的函数进行action数据的创建,然后store.dispatch(action)进行提交,现在react-redux的做法如下。
效果
使用react-redux push数据到store
效果
Redux DevTools工具使用
第一步 科学上网下载Redux DevTools插件在浏览器上
第二步 按照示例位置输入window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__())
import {createStore} from 'redux' import reducer from './reducer' // 创建store 并将reducer作为参数传入 const store = createStore(reducer, // 使用redux—tool工具 window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()) export default store
总结
react-redux仅有2个API,Provider和connect
1. Provider是顶层组件的作用,将store作为上下文提供给全局共享。Provider是一个提供器,只要使用了这个组件,组件里边的其它所有组件都可以使用store了,这也是React-redux的核心组件了。Provider 使组件层级中的 connect() 方法都能够获得 Redux store。正常情况下,你的根组件应该嵌套在 <Provider> 中才能使用 connect() 方法
ReactDOM.render( <Provider store={store}> <App /> </Provider>, document.getElementById('root') )
2. connect方法做的事情是将state和dispatch绑定到Connect组件的参数上,Connect组件是局部组件,将某个react组件包装起来,传递指定的state和props给该组件访问,把UI组件(无状态组件)和业务逻辑代码的分开,然后通过connect再链接到一起,让代码更加清晰和易于维护。