不能因为别人怀疑自己, 但是可以通过别人启发自己 !
昨天有人让我把他当小白讲讲 redux, 我表示理出来的逻辑并不是很明确, 他可能是在教我如何写文章吧, 我对自己写的东西, 并不是很负责, 目的一直停留在增强自己的短时间记忆, 我会安排时间将之前的文章做逻辑性梳理, 当然先从这篇开始.
我们知道 react 组件传递是单向的, 如果组件关系太远, 或者没有关系, 我们就会很麻烦, redux 就是解决这个问题, 他将数据存储到仓库, 通过 reducer 派发 action 动作, 来 substrict
redux 组成有 5 个
createStore 创建仓库, 接受 reducer 作为参数
bindActionCreator 绑定 store.dispatch 和 action 的关系
combineReducers 合并多个 reducers
applyMiddleware 洋葱模型的中间件, 介于 dispatch 和 action 之间, 重写 dispatch
compose 整合多个中间件
createStore
主要导出 getState,dispatch,subscribe 三个方法, 分别是获取仓库, 派发动作和订阅事件. 生成 store, 他包含所有数据, 拿数据通过 getState 获取, 一个 State 对应一个 View. 只要 State 相同, View 就相同.
其内部保存啦一颗状态树, 这个状态树是任意类型的, 当获取状态树的时候后会复制一份状态树吗, 并导出, 防止对状态树的恶意更改.
因此, 只能通过派发 dispatch 更改状态, dispatch 会调用 reducer 处理 action 动作, reducer 是个函数, 他有 state 和 action 两个参数, 作用是将老得 state 经过 action 处理后返回新的 state,action 是个有 type 属性的对象, 来标示他对哪个状态进行改变
于此同时, 当状态发生改变会触发 subscribe 订阅的监听事件
- export default function createStore(reducer,initState,enchancer) {
- if (enchancer) {
- return enchancer(createStore)(reducer,initState);
- }
- // 仓库内部保存了一颗状态树. 可以是任意类型
- let state;
- let listeners=[];
- function getState() {
- return JSON.parse(JSON.stringify(state));
- }
- // 组件可以派发动作给仓库
- function dispatch(action) {
- // 调用 reducer 进行处理, 获取老的 state, 计算出新的 state
- state=reducer(state,action);
- // 通知其他的组件
- listeners.forEach(l=>l());
- }
- // 如果说其他的组件需要订阅状态变化时间的话,
- function subscribe(listener) {
- listeners.push(listener);
- return function () {
- listeners = listeners.filter(item=>item!==listener);
- }
- }
- dispatch({type:'@@INIT'});
- return {
- getState,
- dispatch,
- subscribe
- }
- }
复制代码
bindActionCreator
虽然我们用 dispatch 派发事件, 派发动作类型大概是一个有 type 属性的对象, 我们将这个对象的方法封一个 actions 的文件夹, 在 dispach 的时候直接调用 action, 列入将 dispatch(()=>{type:add}), 此时我们需要建立 dispatch 和 actions 的关系
- <button onClick={()=>bindActions.add1(1)}>+</button>
- export default function (actions,dispatch) {
- //actions = {add(){return {type:ADD}},minus(){return {type:'MINUS'}}}
- return Object.keys(actions).reduce(
- (memo,key) => {
- memo[key]=(...args) => dispatch(actions[key](...args));
- return memo;
- },{});
- }
复制代码
combineReducers
返回一个函数, 这个函数代表合并后的 reducer, 那么他一定有两个参数, 最终返回新状态, 我们迭代 reducers 每一个属性
- // 因为 redux 应用只能有一个仓库, 只能有一个 reducer
- // 把多个 reducer 函数合并成一个
- export default function (reducers) {
- // 返回的这个函数就是合并后的 reducer
- return function (state={},action) {
- let newState={};
- for (let key in reducers) {
- // key=counter1
- //reducers[counter1]=counter 那个 reducer
- //state 合并后的状态树
- //if (key===action.name) {
- newState[key]=reducers[key](state[key],action);//state[key] 老状态
- //}
- }
- return newState;
- }
- }
复制代码
我们可以发现, 每个组件都需要获取仓库, 管理订阅, 向仓库派发事件, 这样很融于, 我们需要复用这样的逻辑, 可以用高阶组件, 或者将函数作为子组件, 我们此时用这个库
applyMiddleware
中间件大概是 redux 里面比较难理解的部分啦
他的原理如上图, state 和 dispatch 构成啦我们的仓库, 我们可以向仓库发送 action 通过 reducer 改变状态, 状态改变之后可以修改视图, 用户可以通过鼠标点击视图, 视图派发 action, 改变状态, 形成循环,, 有时候我们需要发布异步操作, 想在派发前, 派发后做一些额外动作, 此时我们就需要插入中间件, 我们的方法就是得到 dispatch 方法, 重写 dispacth, 这里用到啦我们说的源码解析
一个 redux 中间件大概就是如下, 之后可能会将 redux 和 express 和 koa 中间件做个对比给大家总结一下
- function logger(store){//getState , 新的 dispatch
- return function(next){//store.dispatch 旧的
- return function action(){
- console.log('old');
- next()
- console.log('new')
- }
- }
- }
- // 解析过程
- let logger = store => next =>action =>{
- }
- // 以下是处理过程
- function applyMiddleware(middleware){
- return function(creacteStore){
- return function(reducer){
- let store = creacteStore(reducer);
- let middleware2 = middleware(store)
- let dispatch = middleware2(store.dispatch)
- }
- }
- }
- let store = applyMiddleware(logger)(creacteStore)(reducer)
复制代码
- import compose from './compose';
- export default function (...middlewares) {
- return function (createStore) {
- return function (reducers) {
- let store=createStore(reducers);// 这就是原始的仓库 dispatch 就是原始的 dispatch
- let dispatch;//dispatch 方法指向新的 dispatch 方法
- let middlewares2 = middlewares.map(middleware=>middleware({
- getState: store.getState,
- dispatch:action=>dispatch(action)
- }));// 调用第一层去掉
- dispatch=compose(...middlewares2)(store.dispatch);// 再调用第二次把第二层去掉
- return {
- ...store,
- dispatch
- }
- }
- }
- }
复制代码
compose 整合中间件
- function add1(str) {
- return 1+str;
- }
- function add2(str) {
- return 2+str;
- }
- function sum(a,b) {
- return a+b;
- }
- //let ret=add1(add2(add3('zdl')));
- //console.log(ret);
- function compose1(...fns) {//[add1,add2,add3]
- return function (...args) {
- let last=fns.pop();
- return fns.reduceRight((val,fn)=>fn(val),last(...args));
- }
- }
- export default function(...fns) {
- return fns.reduce((a,b)=>(...args)=>a(b(...args)));
- }
- /**
- * 第一次的时候 a =add1 b=add3 let ret = add1(add2(...args))
- * 第二次的时候 (...args)=>add1(add2(sum(...args)))
- */
- let ret=compose(add1,add2,sum)('a','b');
- console.log(ret);//123zdl
复制代码
react-redux
有四个文件
connect 将 store 和 dispatch 分别映射成 props 属性对象, 返回组件
context 上下文 导出 Provider,, 和 consumer
Provider 一个接受 store 的组件, 通过 context api 传递给所有子组件, 优点类似于路由啦
- index
- index
- import Provider from './Provider';
- import connect from './connect';
- export {
- Provider,
- connect
- }
复制代码
- Provider
- /**
- * 是一个组件, 用来接受 store, 再经过它的手通过 context api 传递给所有的子组件
- */
- import React,{Component} from 'react'
- import {Provider as StoreProvider} from './context';
- import PropTypes from 'prop-types';
- export default class Provider extends Component{
- // 规定如果有人想使用这个组件, 必须提供一个 redux 仓库属性
- static propTypes={
- store:PropTypes.object.isRequired
- }
- render() {
- let value={store:this.props.store};
- return (
- <StoreProvider value={value}>
- {this.props.children}
- </StoreProvider>
- )
- }
- }
复制代码
- context
- import React from 'react'
- let {Provider,Consumer}=React.createContext();
- export {Provider,Consumer};
复制代码
connect
这是个高阶函数, 分别传入两个方法 mapStateToProps,mapDispatchToProps, 将 store 和 dispatch 分别映射成 props 属性对象, 这样在页面就不需要饮用仓库, 也不需要绑定 action 和 dispatch, 也不需要订阅状态, 返回组件
- import {Consumer} from './context';
- import React,{Component} from 'react';
- import {bindActionCreators} from '../redux';
- /**
- * connect 实现的是仓库和组件的连接
- * mapStateToProps 是一个函数 把状态映射为一个属性对象
- * mapDispatchToProps 也是一个函数 把 dispatch 方法映射为一个属性对象
- */
- export default function (mapStateToProps,mapDispatchToProps) {
- return function (Com) {
- // 在这个组件里实现仓库和组件的连接
- class Proxy extends Component{
- state=mapStateToProps(this.props.store.getState())
- componentDidMount() {
- this.unsubscribe = this.props.store.subscribe(() => {
- this.setState(mapStateToProps(this.props.store.getState()));
- });
- }
- componentWillUnmount = () => {
- this.unsubscribe();
- }
- render() {
- let actions={};
- // 如果说 mapDispatchToProps 是一个函数, 执行后得到属性对象
- if (typeof mapDispatchToProps === 'function') {
- actions = mapDispatchToProps(this.props.store.dispatch);
- // 如果说 mapDispatchToProps 是一个对象的话, 我们需要手工绑定
- } else {
- actions=bindActionCreators(mapDispatchToProps,this.props.store.dispatch);
- }
- return <Com {...this.state} {...actions}/>
- }
- }
- return () => (
- <Consumer>
- {
- value => <Proxy store={value.store}/>
- }
- </Consumer>
- );
- }
- }
复制代码
来源: https://juejin.im/post/5b9878cc6fb9a05d3447a3a5