redux_redux-saga - CSDN
redux 订阅
Redux由Dan Abramov在2015年创建的科技术语。是受2014年Facebook的Flux架构以及函数式编程语言Elm启发。很快,Redux因其简单易学体积小在短时间内成为最热门的前端架构。 展开全文
Redux由Dan Abramov在2015年创建的科技术语。是受2014年Facebook的Flux架构以及函数式编程语言Elm启发。很快,Redux因其简单易学体积小在短时间内成为最热门的前端架构。
信息
创建时间
2015年
发布人
Dan Abramov
属    性
前端架构
外文名
Redux
redux简介
Redux对于JavaScript应用而言是一个可预测状态的容器。换言之,它是一个应用数据流框架,而不是传统的像underscore.js或者AngularJs那样的库或者框架。Redux最主要是用作应用状态的管理。简言之,Redux用一个单独的常量状态树(对象)保存这一整个应用的状态,这个对象不能直接被改变。当一些数据变化了,一个新的对象就会被创建(使用actions和reducers)。
收起全文
  • 本套课程让你轻松掌握redux,非常细致的讲解redux怎么应用,结合react巧妙使用,让你再也不用面对枯燥的文档,不知道如何解脱,一套教程让你彻底学会redux
  • 简单完全理解redux

    2019-08-16 10:08:57
    完全理解 redux 前言 记得开始接触 react 技术栈的时候,最难理解的地方就是 redux。全是新名词:reducer、store、dispatch、middleware 等等,我就理解 state 一个名词。 网上找的 redux 文章,要不有一本书的...

    完全理解 redux
    前言
    记得开始接触 react 技术栈的时候,最难理解的地方就是 redux。全是新名词:reducer、store、dispatch、middleware 等等,我就理解 state 一个名词。

    网上找的 redux 文章,要不有一本书的厚度,要不很玄乎,晦涩难懂,越看越觉得难,越看越怕,信心都没有了!

    花了很长时间熟悉 redux,慢慢的发现它其实真的很简单。本章不会把 redux 的各种概念,名词解释一遍,这样和其他教程没有任何区别,没有太大意义。我会带大家从零实现一个完整的 redux,让大家知其然,知其所以然。

    开始前,你必须知道一些事情:

    redux 和 react 没有关系,redux 可以用在任何框架中,忘掉 react。
    connect 不属于 redux,它其实属于 react-redux,请先忘掉它,下一章节,我们会介绍它。
    请一定先忘记 reducer、store、dispatch、middleware 等等这些名词。
    redux 是一个状态管理器。
    Let’s Go!

    状态管理器
    简单的状态管理器
    redux 是一个状态管理器,那什么是状态呢?状态就是数据,比如计数器中的 count。

     `let state = {   count: 1 }`
    

    我们来使用下状态

    console.log(state.count);
    

    我们来修改下状态

    state.count = 2;
    

    好了,现在我们实现了状态(计数)的修改和使用了。

    读者:你当我傻吗?你说的这个谁不知道?捶你?!

    笔者:哎哎哎,别打我!有话好好说!redux 核心就是这个呀!我们一步一步扩展开来嘛!

    当然上面的有一个很明显的问题:修改 count 之后,使用 count 的地方不能收到通知。我们可以使用发布-订阅模式来解决这个问题。

    /*------count 的发布订阅者实践------*/
    let state = {
      count: 1
    };
    let listeners = [];
    
    /*订阅*/
    function subscribe(listener) {
      listeners.push(listener);
    }
    
    function changeCount(count) {
      state.count = count;
      /*当 count 改变的时候,我们要去通知所有的订阅者*/
      for (let i = 0; i < listeners.length; i++) {
        const listener = listeners[i];
        listener();
      }
    }
    

    我们来尝试使用下这个简单的计数状态管理器。

    /来订阅一下,当 count 改变的时候,我要实时输出新的值/

    subscribe(() => {
      console.log(state.count);
    });
    

    /我们来修改下 state,当然我们不能直接去改 state 了,我们要通过 changeCount 来修改/

    changeCount(2);
    changeCount(3);
    changeCount(4);

    现在我们可以看到,我们修改 count 的时候,会输出相应的 count 值。

    现在有两个新的问题摆在我们面前

    这个状态管理器只能管理 count,不通用
    公共的代码要封装起来
    我们尝试来解决这个问题,把公共的代码封装起来

    const createStore = function (initState) {
      let state = initState;
      let listeners = [];
    
      /*订阅*/
      function subscribe(listener) {
        listeners.push(listener);
      }
    
      function changeState(newState) {
        state = newState;
        /*通知*/
        for (let i = 0; i < listeners.length; i++) {
          const listener = listeners[i];
          listener();
        }
      }
    
      function getState() {
        return state;
      }
    
      return {
        subscribe,
        changeState,
        getState
      }
    }
    

    我们来使用这个状态管理器管理多个状态 counter 和 info 试试

    let initState = {
      counter: {
        count: 0
      },
      info: {
        name: '',
        description: ''
      }
    }
    
    let store = createStore(initState);
    
    store.subscribe(() => {
      let state = store.getState();
      console.log(`${state.info.name}:${state.info.description}`);
    });
    store.subscribe(() => {
      let state = store.getState();
      console.log(state.counter.count);
    });
    
    store.changeState({
      ...store.getState(),
      info: {
        name: '前端九部',
        description: '我们都是前端爱好者!'
      }
    });
    
    store.changeState({
      ...store.getState(),
      counter: {
        count: 1
      }
    });
    

    到这里我们完成了一个简单的状态管理器。

    这里需要理解的是 createStore,提供了 changeState,getState,subscribe 三个能力。

    本小节完整源码见 demo-1

    有计划的状态管理器
    我们用上面的状态管理器来实现一个自增,自减的计数器。

    let initState = {
      count: 0
    }
    let store = createStore(initState);
    
    store.subscribe(() => {
      let state = store.getState();
      console.log(state.count);
    });
    /*自增*/
    store.changeState({
      count: store.getState().count + 1
    });
    /*自减*/
    store.changeState({
      count: store.getState().count - 1
    });
    /*我想随便改*/
    store.changeState({
      count: 'abc'
    });
    

    你一定发现了问题,count 被改成了字符串 abc,因为我们对 count 的修改没有任何约束,任何地方,任何人都可以修改。

    我们需要约束,不允许计划外的 count 修改,我们只允许 count 自增和自减两种改变方式!

    那我们分两步来解决这个问题

    制定一个 state 修改计划,告诉 store,我的修改计划是什么。
    修改 store.changeState 方法,告诉它修改 state 的时候,按照我们的计划修改。
    我们来设置一个 plan 函数,接收现在的 state,和一个 action,返回经过改变后的新的 state。

    /注意:action = {type:’’,other:’’}, action 必须有一个 type 属性/

    function plan(state, action) {
      switch (action.type) {
        case 'INCREMENT':
          return {
            ...state,
            count: state.count + 1
          }
        case 'DECREMENT':
          return {
            ...state,
            count: state.count - 1
          }
        default:
          return state;
      }
    }
    

    我们把这个计划告诉 store,store.changeState 以后改变 state 要按照我的计划来改。

    /*增加一个参数 plan*/
    const createStore = function (plan, initState) {
      let state = initState;
      let listeners = [];
    
      function subscribe(listener) {
        listeners.push(listener);
      }
    
      function changeState(action) {
        /*请按照我的计划修改 state*/  
        state = plan(state, action);
        for (let i = 0; i < listeners.length; i++) {
          const listener = listeners[i];
          listener();
        }
      }
    
      function getState() {
        return state;
      }
    
      return {
        subscribe,
        changeState,
        getState
      }
    }
    我们来尝试使用下新的 createStore 来实现自增和自减
    
    let initState = {
      count: 0
    }
    /*把plan函数*/
    let store = createStore(plan, initState);
    
    store.subscribe(() => {
      let state = store.getState();
      console.log(state.count);
    });
    /*自增*/
    store.changeState({
      type: 'INCREMENT'
    });
    /*自减*/
    store.changeState({
      type: 'DECREMENT'
    });
    /*我想随便改 计划外的修改是无效的!*/
    store.changeState({
      count: 'abc'
    });
    

    到这里为止,我们已经实现了一个有计划的状态管理器!

    我们商量一下吧?我们给 plan 和 changeState 改下名字好不好?**plan 改成 reducer,changeState 改成 dispatch!**不管你同不同意,我都要换,因为新名字比较厉害(其实因为 redux 是这么叫的)!

    本小节完整源码见 demo-2

    多文件协作
    reducer 的拆分和合并
    这一小节我们来处理下 reducer 的问题。啥问题?

    我们知道 reducer 是一个计划函数,接收老的 state,按计划返回新的 state。那我们项目中,有大量的 state,每个 state 都需要计划函数,如果全部写在一起会是啥样子呢?

    所有的计划写在一个 reducer 函数里面,会导致 reducer 函数及其庞大复杂。按经验来说,我们肯定会按组件维度来拆分出很多个 reducer 函数,然后通过一个函数来把他们合并起来。

    我们来管理两个 state,一个 counter,一个 info。

    let state = {
      counter: {
        count: 0
      },
      info: {
        name: '前端九部',
        description: '我们都是前端爱好者!'
      }
    }
    他们各自的 reducer
    
    /*counterReducer, 一个子reducer*/
    /*注意:counterReducer 接收的 state 是 state.counter*/
    function counterReducer(state, action) {
      switch (action.type) {
        case 'INCREMENT':
          return {
            count: state.count + 1
          }
        case 'DECREMENT':
          return {
            ...state,
            count: state.count - 1
          }
        default:
          return state;
      }
    }
    /*InfoReducer,一个子reducer*/
    /*注意:InfoReducer 接收的 state 是 state.info*/
    function InfoReducer(state, action) {
      switch (action.type) {
        case 'SET_NAME':
          return {
            ...state,
            name: action.name
          }
        case 'SET_DESCRIPTION':
          return {
            ...state,
            description: action.description
          }
        default:
          return state;
      }
    }
    那我们用 combineReducers 函数来把多个 reducer 函数合并成一个 reducer 函数。大概这样用
    
    const reducer = combineReducers({
        counter: counterReducer,
        info: InfoReducer
    });
    我们尝试实现下 combineReducers 函数
    
    function combineReducers(reducers) {
    
      /* reducerKeys = ['counter', 'info']*/
      const reducerKeys = Object.keys(reducers)
    
      /*返回合并后的新的reducer函数*/
      return function combination(state = {}, action) {
        /*生成的新的state*/
        const nextState = {}
    
        /*遍历执行所有的reducers,整合成为一个新的state*/
        for (let i = 0; i < reducerKeys.length; i++) {
          const key = reducerKeys[i]
          const reducer = reducers[key]
          /*之前的 key 的 state*/
          const previousStateForKey = state[key]
          /*执行 分 reducer,获得新的state*/
          const nextStateForKey = reducer(previousStateForKey, action)
    
          nextState[key] = nextStateForKey
        }
        return nextState;
      }
    }
    我们来尝试下 combineReducers 的威力吧
    
    const reducer = combineReducers({
      counter: counterReducer,
      info: InfoReducer
    });
    
    let initState = {
      counter: {
        count: 0
      },
      info: {
        name: '前端九部',
        description: '我们都是前端爱好者!'
      }
    }
    
    let store = createStore(reducer, initState);
    
    store.subscribe(() => {
      let state = store.getState();
      console.log(state.counter.count, state.info.name, state.info.description);
    });
    /*自增*/
    store.dispatch({
      type: 'INCREMENT'
    });
    
    /*修改 name*/
    store.dispatch({
      type: 'SET_NAME',
      name: '前端九部2号'
    });
    

    本小节完整源码见 demo-3

    state 的拆分和合并
    上一小节,我们把 reducer 按组件维度拆分了,通过 combineReducers 合并了起来。但是还有个问题, state 我们还是写在一起的,这样会造成 state 树很庞大,不直观,很难维护。我们需要拆分,一个 state,一个 reducer 写一块。

    这一小节比较简单,我就不卖关子了,用法大概是这样(注意注释)

    /* counter 自己的 state 和 reducer 写在一起*/
    let initState = {
      count: 0
    }
    function counterReducer(state, action) {
      /*注意:如果 state 没有初始值,那就给他初始值!!*/  
      if (!state) {
          state = initState;
      }
      switch (action.type) {
        case 'INCREMENT':
          return {
            count: state.count + 1
          }
        default:    
          return state;
      }
    }
    我们修改下 createStore 函数,增加一行 dispatch({ type: Symbol() })
    
    const createStore = function (reducer, initState) {
      let state = initState;
      let listeners = [];
    
      function subscribe(listener) {
        listeners.push(listener);
      }
    
      function dispatch(action) {
        state = reducer(state, action);
        for (let i = 0; i < listeners.length; i++) {
          const listener = listeners[i];
          listener();
        }
      }
    
      function getState() {
        return state;
      }
      /* 注意!!!只修改了这里,用一个不匹配任何计划的 type,来获取初始值 */
      dispatch({ type: Symbol() })
    
      return {
        subscribe,
        dispatch,
        getState
      }
    }
    

    我们思考下这行可以带来什么效果?

    createStore 的时候,用一个不匹配任何 type 的 action,来触发 state = reducer(state, action)
    因为 action.type 不匹配,每个子 reducer 都会进到 default 项,返回自己初始化的 state,这样就获得了初始化的 state 树了。
    你可以试试

    /*这里没有传 initState 哦 */
    const store = createStore(reducer);
    /这里看看初始化的 state 是什么/
    console.dir(store.getState());
    本小节完整源码见 demo-4

    到这里为止,我们已经实现了一个七七八八的 redux 啦!

    中间件 middleware
    中间件 middleware 是 redux 中最难理解的地方。但是我挑战一下用最通俗的语言来讲明白它。如果你看完这一小节,还没明白中间件是什么,不知道如何写一个中间件,那就是我的锅了!

    中间件是对 dispatch 的扩展,或者说重写,增强 dispatch 的功能!

    记录日志
    我现在有一个需求,在每次修改 state 的时候,记录下来 修改前的 state ,为什么修改了,以及修改后的 state。我们可以通过重写 store.dispatch 来实现,直接看代码

    const store = createStore(reducer);
    const next = store.dispatch;
    
    /*重写了store.dispatch*/
    store.dispatch = (action) => {
      console.log('this state', store.getState());
      console.log('action', action);
      next(action);
      console.log('next state', store.getState());
    }
    我们来使用下
    
    store.dispatch({
      type: 'INCREMENT'
    });
    日志输出为
    
    this state { counter: { count: 0 } }
    action { type: 'INCREMENT' }
    1
    next state { counter: { count: 1 } }
    现在我们已经实现了一个完美的记录 state 修改日志的功能!
    
    记录异常
    我又有一个需求,需要记录每次数据出错的原因,我们扩展下 dispatch
    
    const store = createStore(reducer);
    const next = store.dispatch;
    
    store.dispatch = (action) => {
      try {
        next(action);
      } catch (err) {
        console.error('错误报告: ', err)
      }
    }
    

    这样每次 dispatch 出异常的时候,我们都会记录下来。

    多中间件的合作
    我现在既需要记录日志,又需要记录异常,怎么办?当然很简单了,两个函数合起来呗!

    store.dispatch = (action) => {
      try {
        console.log('this state', store.getState());
        console.log('action', action);
        next(action);
        console.log('next state', store.getState());
      } catch (err) {
        console.error('错误报告: ', err)
      }
    }
    

    如果又来一个需求怎么办?接着改 dispatch 函数?那再来10个需求呢?到时候 dispatch 函数肯定庞大混乱到无法维护了!这个方式不可取呀!

    我们需要考虑如何实现扩展性很强的多中间件合作模式。

    我们把 loggerMiddleware 提取出来

    const store = createStore(reducer);
    const next = store.dispatch;
    
    const loggerMiddleware = (action) => {
      console.log('this state', store.getState());
      console.log('action', action);
      next(action);
      console.log('next state', store.getState());
    }
    
    store.dispatch = (action) => {
      try {
        loggerMiddleware(action);
      } catch (err) {
        console.error('错误报告: ', err)
      }
    }
    我们把 exceptionMiddleware 提取出来
    
    const exceptionMiddleware = (action) => {
      try {
        /*next(action)*/
        loggerMiddleware(action);
      } catch (err) {
        console.error('错误报告: ', err)
      } 
    }
    store.dispatch = exceptionMiddleware;
    现在的代码有一个很严重的问题,就是 exceptionMiddleware 里面写死了 loggerMiddleware,我们需要让 next(action)变成动态的,随便哪个中间件都可以
    
    const exceptionMiddleware = (next) => (action) => {
      try {
        /*loggerMiddleware(action);*/
        next(action);
      } catch (err) {
        console.error('错误报告: ', err)
      } 
    }
    /*loggerMiddleware 变成参数传进去*/
    store.dispatch = exceptionMiddleware(loggerMiddleware);
    

    同样的道理,loggerMiddleware 里面的 next 现在恒等于 store.dispatch,导致 loggerMiddleware 里面无法扩展别的中间件了!我们也把 next 写成动态的

    const loggerMiddleware = (next) => (action) => {
      console.log('this state', store.getState());
      console.log('action', action);
      next(action);
      console.log('next state', store.getState());
    }
    

    到这里为止,我们已经探索出了一个扩展性很高的中间件合作模式!

    const store = createStore(reducer);
    const next = store.dispatch;
    
    const loggerMiddleware = (next) => (action) => {
      console.log('this state', store.getState());
      console.log('action', action);
      next(action);
      console.log('next state', store.getState());
    }
    
    const exceptionMiddleware = (next) => (action) => {
      try {
        next(action);
      } catch (err) {
        console.error('错误报告: ', err)
      }
    }
    
    store.dispatch = exceptionMiddleware(loggerMiddleware(next));
    

    这时候我们开开心心的新建了一个 loggerMiddleware.js,一个exceptionMiddleware.js文件,想把两个中间件独立到单独的文件中去。会碰到什么问题吗?

    loggerMiddleware 中包含了外部变量 store,导致我们无法把中间件独立出去。那我们把 store 也作为一个参数传进去好了~

    const store = createStore(reducer);
        const next  = store.dispatch;
        
        const loggerMiddleware = (store) => (next) => (action) => {
          console.log('this state', store.getState());
          console.log('action', action);
          next(action);
          console.log('next state', store.getState());
        }
        
        const exceptionMiddleware = (store) => (next) => (action) => {
          try {
            next(action);
          } catch (err) {
            console.error('错误报告: ', err)
          }
        }
        
        const logger = loggerMiddleware(store);
        const exception = exceptionMiddleware(store);
        store.dispatch = exception(logger(next));
    

    到这里为止,我们真正的实现了两个可以独立的中间件啦!

    现在我有一个需求,在打印日志之前输出当前的时间戳。用中间件来实现!

    const timeMiddleware = (store) => (next) => (action) => {
      console.log('time', new Date().getTime());
      next(action);
    }
    
    ...
    const time = timeMiddleware(store);
    store.dispatch = exception(time(logger(next)))
    

    ;
    本小节完整源码见 demo-6

    中间件使用方式优化
    上一节我们已经完全实现了正确的中间件!但是中间件的使用方式不是很友好

    import loggerMiddleware from './middlewares/loggerMiddleware';
    import exceptionMiddleware from './middlewares/exceptionMiddleware';
    import timeMiddleware from './middlewares/timeMiddleware';
    
    ...
    
    const store = createStore(reducer);
    const next = store.dispatch;
    
    const logger = loggerMiddleware(store);
    const exception = exceptionMiddleware(store);
    const time = timeMiddleware(store);
    store.dispatch = exception(time(logger(next)));
    

    其实我们只需要知道三个中间件,剩下的细节都可以封装起来!我们通过扩展 createStore 来实现!

    先来看看期望的用法

    /*接收旧的 createStore,返回新的 createStore*/
    const newCreateStore = applyMiddleware(exceptionMiddleware, timeMiddleware, loggerMiddleware)(createStore);
    
    /*返回了一个 dispatch 被重写过的 store*/
    const store = newCreateStore(reducer);
    实现 applyMiddleware
    
    const applyMiddleware = function (...middlewares) {
      /*返回一个重写createStore的方法*/
      return function rewriteCreateStoreFunc(oldCreateStore) {
         /*返回重写后新的 createStore*/
        return function newCreateStore(reducer, initState) {
          /*1. 生成store*/
          const store = oldCreateStore(reducer, initState);
          /*给每个 middleware 传下store,相当于 const logger = loggerMiddleware(store);*/
          /* const chain = [exception, time, logger]*/
          const chain = middlewares.map(middleware => middleware(store));
          let dispatch = store.dispatch;
          /* 实现 exception(time((logger(dispatch))))*/
          chain.reverse().map(middleware => {
            dispatch = middleware(dispatch);
          });
    
          /*2. 重写 dispatch*/
          store.dispatch = dispatch;
          return store;
        }
      }
    }
    

    让用户体验美好
    现在还有个小问题,我们有两种 createStore 了

    /*没有中间件的 createStore*/
    import { createStore } from './redux';
    const store = createStore(reducer, initState);
    
    /*有中间件的 createStore*/
    const rewriteCreateStoreFunc = applyMiddleware(exceptionMiddleware, timeMiddleware, loggerMiddleware);
    const newCreateStore = rewriteCreateStoreFunc(createStore);
    const store = newCreateStore(reducer, initState);
    为了让用户用起来统一一些,我们可以很简单的使他们的使用方式一致,我们修改下 createStore 方法
    
    const createStore = (reducer, initState, rewriteCreateStoreFunc) => {
        /*如果有 rewriteCreateStoreFunc,那就采用新的 createStore */
        if(rewriteCreateStoreFunc){
           const newCreateStore =  rewriteCreateStoreFunc(createStore);
           return newCreateStore(reducer, initState);
        }
        /*否则按照正常的流程走*/
        ...
    }
    最终的用法
    
    const rewriteCreateStoreFunc = applyMiddleware(exceptionMiddleware, timeMiddleware, loggerMiddleware);
    
    const store = createStore(reducer, initState, rewriteCreateStoreFunc);
    

    本小节完整源码见 demo-7

    完整的 redux
    退订
    不能退订的订阅都是耍流浪!我们修改下 store.subscribe 方法,增加退订功能

      function subscribe(listener) {
        listeners.push(listener);
        return function unsubscribe() {
          const index = listeners.indexOf(listener)
          listeners.splice(index, 1)
        }
      }
    

    使用

    const unsubscribe = store.subscribe(() => {
      let state = store.getState();
      console.log(state.counter.count);
    });
    /*退订*/
    
    unsubscribe()
    

    中间件拿到的store
    现在的中间件拿到了完整的 store,他甚至可以修改我们的 subscribe 方法,按照最小开放策略,我们只用把 getState 给中间件就可以了!因为我们只允许你用 getState 方法!

    修改下 applyMiddleware 中给中间件传的 store

    /*const chain = middlewares.map(middleware => middleware(store));*/
    const simpleStore = { getState: store.getState };
    const chain = middlewares.map(middleware => middleware(simpleStore));
    compose
    我们的 applyMiddleware 中,把 [A, B, C] 转换成 A(B(C(next))),是这样实现的
    
    const chain = [A, B, C];
    let dispatch = store.dispatch;
    chain.reverse().map(middleware => {
       dispatch = middleware(dispatch);
    });
    

    redux 提供了一个 compose 方式,可以帮我们做这个事情

    const chain = [A, B, C]; dispatch = compose(...chain)(store.dispatch)

    看下他是如何实现的

    export default function compose(...funcs) {
      if (funcs.length === 1) {
        return funcs[0]
      }
      return funcs.reduce((a, b) => (...args) => a(b(...args)))
    }
    

    当然 compose 函数对于新人来说可能比较难理解,你只需要他是做什么的就行啦!

    省略initState
    有时候我们创建 store 的时候不传 initState,我们怎么用?

    const store = createStore(reducer, {}, rewriteCreateStoreFunc);
    

    redux 允许我们这样写

    const store = createStore(reducer, rewriteCreateStoreFunc);
    我们仅需要改下 createStore 函数,如果第二个参数是一个object,我们认为他是 initState,如果是 function,我们就认为他是 rewriteCreateStoreFunc。

    function craeteStore(reducer, initState, rewriteCreateStoreFunc){
        if (typeof initState === 'function'){
        rewriteCreateStoreFunc = initState;
        initState = undefined;
      }
      ...
    }
    2 行代码的 replaceReducer
    reducer 拆分后,和组件是一一对应的。我们就希望在做按需加载的时候,reducer也可以跟着组件在必要的时候再加载,然后用新的 reducer 替换老的 reducer。
    
    const createStore = function (reducer, initState) {
      ...
      function replaceReducer(nextReducer) {
        reducer = nextReducer
        /*刷新一遍 state 的值,新来的 reducer 把自己的默认状态放到 state 树上去*/
        dispatch({ type: Symbol() })
      }
      ...
      return {
        ...
        replaceReducer
      }
    }
    

    我们来尝试使用下

    const reducer = combineReducers({
      counter: counterReducer
    });
    const store = createStore(reducer);
    
    /*生成新的reducer*/
    const nextReducer = combineReducers({
      counter: counterReducer,
      info: infoReducer
    });
    /*replaceReducer*/
    store.replaceReducer(nextReducer);
    replaceReducer 示例源码见 demo-5
    
    bindActionCreators
    

    bindActionCreators 我们很少很少用到,一般只有在 react-redux 的 connect 实现中用到。

    他是做什么的?他通过闭包,把 dispatch 和 actionCreator 隐藏起来,让其他地方感知不到 redux 的存在。

    我们通过普通的方式来 隐藏 dispatch 和 actionCreator 试试,注意最后两行代码

    const reducer = combineReducers({
      counter: counterReducer,
      info: infoReducer
    });
    const store = createStore(reducer);
    
    /*返回 action 的函数就叫 actionCreator*/
    function increment() {
      return {
        type: 'INCREMENT'
      }
    }
    
    function setName(name) {
      return {
        type: 'SET_NAME',
        name: name
      }
    }
    
    const actions = {
      increment: function () {
        return store.dispatch(increment.apply(this, arguments))
      },
      setName: function () {
        return store.dispatch(setName.apply(this, arguments))
      }
    }
    /*注意:我们可以把 actions 传到任何地方去*/
    /*其他地方在实现自增的时候,根本不知道 dispatch,actionCreator等细节*/
    actions.increment(); /*自增*/
    actions.setName('九部威武'); /*修改 info.name*/
    

    我眼睛一看,这个 actions 生成的时候,好多公共代码,提取一下

    const actions = bindActionCreators({ increment, setName }, store.dispatch);
    来看一下 bindActionCreators 的源码,超级简单(就是生成了刚才的 actions)

    /核心的代码在这里,通过闭包隐藏了 actionCreator 和 dispatch/

    function bindActionCreator(actionCreator, dispatch) {
      return function () {
        return dispatch(actionCreator.apply(this, arguments))
      }
    }
    
    /* actionCreators 必须是 function 或者 object */
    export default function bindActionCreators(actionCreators, dispatch) {
      if (typeof actionCreators === 'function') {
        return bindActionCreator(actionCreators, dispatch)
      }
    
      if (typeof actionCreators !== 'object' || actionCreators === null) {
        throw new Error()
      }
    
      const keys = Object.keys(actionCreators)
      const boundActionCreators = {}
      for (let i = 0; i < keys.length; i++) {
        const key = keys[i]
        const actionCreator = actionCreators[key]
        if (typeof actionCreator === 'function') {
          boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)
        }
      }
      return boundActionCreators
    }
    

    bindActionCreators 示例源码见 demo-8

    大功告成
    完整的示例源码见 demo-9,你可以和 redux 源码做一下对比,你会发现,我们已经实现了 redux 所有的功能了。

    当然,为了保证代码的理解性,我们少了一些参数验证。比如 createStore(reducer)的参数 reducer 必须是 function 等等。

    最佳实践
    纯函数
    什么是纯函数?

    纯函数是这样一种函数,即相同的输入,永远会得到相同的输出,而且没有任何可观察的副作用。

    通俗来讲,就两个要素

    相同的输入,一定会得到相同的输出
    不会有 “触发事件”,更改输入参数,依赖外部参数,打印 log 等等副作用
    /不是纯函数,因为同样的输入,输出结果不一致/

    function a( count ){
       return count + Math.random();
    }
    
    /*不是纯函数,因为外部的 arr 被修改了*/
    function b( arr ){
        return arr.push(1);
    }
    let arr = [1, 2, 3];
    b(arr);
    console.log(arr); //[1, 2, 3, 1]
    
    /*不是纯函数,以为依赖了外部的 x*/
    let x = 1;
    function c( count ){
        return count + x;
    }
    

    我们的 reducer 计划函数,就必须是一个纯函数!

    只要传入参数相同,返回计算得到的下一个 state 就一定相同。没有特殊情况、没有副作用,没有 API 请求、没有变量修改,单纯执行计算。

    总结
    到了最后,我想把 redux 中关键的名词列出来,你每个都知道是干啥的吗?

    createStore

    创建 store 对象,包含 getState, dispatch, subscribe, replaceReducer

    reducer

    reducer 是一个计划函数,接收旧的 state 和 action,生成新的 state

    action

    action 是一个对象,必须包含 type 字段

    dispatch

    dispatch( action ) 触发 action,生成新的 state

    subscribe

    实现订阅功能,每次触发 dispatch 的时候,会执行订阅函数

    combineReducers

    多 reducer 合并成一个 reducer

    replaceReducer

    替换 reducer 函数

    middleware

    扩展 dispatch 函数!

    你再看 redux 流程图,是不是大彻大悟了?

    redux 流程图在这里插入图片描述

    展开全文
  • React是一个用于构建用户界面的 JavaScript 库。起源于 Facebook 的内部项目,并与2013年5月开源。React拥有较高的性能,代码逻辑非常简单,越来越多的人已开始关注和使用它
  • React是一个用于构建用户界面的 JavaScript 库。起源于 Facebook 的内部项目,并与2013年5月开源。React拥有较高的性能,代码逻辑非常简单,越来越多的人已开始关注和使用它
  • 从函数式编程到深入Redux应用实战
  • 阅读对象:使用过redux,对redux实现原理不是很理解的开发者。 在我实习入职培训的时候,我的前端组长就跟我说过,redux的核心源码很简洁,建议我有空去看一下,提升对redux系列的理解。 入职一个多月了,已经...

    前言

    阅读对象:使用过redux,对redux实现原理不是很理解的开发者。

    在我实习入职培训的时候,我的前端组长就跟我说过,redux的核心源码很简洁,建议我有空去看一下,提升对redux系列的理解。

    入职一个多月了,已经参与了公司的不少项目,redux也使用了一段时间,对于redux的理解却一直没有深入,还停留在“知道怎么用,但是不知道其核心原理”的阶段。

    所以就在github上拉了redux的源码,看了一会,发现东西确实不多,比较简洁。

    redux本身的功能是什么

    在项目中,我们往往不会纯粹的使用redux,而是会配合其他的一些工具库提升效率,比如react-redux,让react应用使用redux更容易,类似的也有wepy-redux,提供给小程序框架wepy的工具库。

    但是在本文中,我们讨论的范围就纯粹些,仅仅讨论redux本身

    redux本身有哪些作用?我们先来快速的过一下redux的核心思想(工作流程):

    • 将状态统一放在一个state中,由store来管理这个state
    • 这个store由reducer创建,reducer的作用是接受之前的状态,返回一个新的状态。
    • 外部改变state的唯一方法是通过调用store的dispatch方法,触发一个action,这个action被对应的reducer处理,于是state完成更新。
    • 可以通过subscribe在store上添加一个监听函数,store中dispatch方法被调用时,会执行这个监听函数。
    • 可以添加中间件(中间件是干什么的我们后面讲)

    在这个工作流程中,redux需要提供的功能是:

    • 创建store,即:createStore()
    • 将多个reducer合并为一个reducer,即:combineReducers()
    • 创建出来的store提供subscribe,dispatch,getState这些方法。
    • 应用中间件,即applyMiddleware()

    没错,就这么多方法,我们看下redux的源码目录:

     

    redux的源码目录

     

     

    却是也就这么多,至于其他的如compose,bindActionCreators都是一些工具方法。下面我们就逐个来看看其源码实现。

    你可以在github上克隆源码到本地,我后面的分析你可以参照着源码看。

    createStore的实现

    这个函数的大致结构是这样:

    function createStore(reducer, preloadedState, enhancer) {
        if(enhancer是有效的){  //这个我们后面再解释,现在可以先不管
            return enhancer(createStore)(reducer, preloadedState)
        } 
        
        let currentReducer = reducer // 当前store中的reducer
        let currentState = preloadedState // 当前store中存储的状态
        let currentListeners = [] // 当前store中放置的监听函数
        let nextListeners = currentListeners //下一次dispatch时的监听函数
        //注意:当我们新添加一个监听函数时,只会在下一次dispatch的时候生效。
        
        //...
        
        // 获取state
        function getState() {
            //...
        }
        
        // 添加一个监听函数,每当dispatch被调用的时候都会执行这个监听函数
        function subscribe() {
            //...
        }
        
        // 触发了一个action,因此我们调用reducer,得到的新的state,并且执行所有添加到store中的监听函数。
        function dispatch() {
            //...
        }
       
        //...
        
        //dispatch一个用于初始化的action,相当于调用一次reducer
        //然后将reducer中的子reducer的初始值也获取到
        //详见下面reducer的实现。
        
        
        return {
            dispatch,
            subscribe,
            getState,
            //下面两个是主要面向库开发者的方法,暂时先忽略
            //replaceReducer,
            //observable
        }
    }
    复制代码

    可以看出,createStore方法创建了一个store,但是并没有直接将这个store的状态state返回,而是返回了一系列方法,外部可以通过这些些方法(getState)获取state,或者间接地(通过调用dispatch)改变state。

    至于state呢,被存在了闭包中。(不理解闭包的同学可以先去了解一下先)

    我们再来详细的看看每个模块是如何实现的(省略了错误处理的代码):

    getState

    function getState() {
        return currentState
    }
    复制代码

    简单到发指,其实这很像面向对象编程中封装只读属性的方法,只提供数据的getter方法,而不直接提供setter。

    subscribe

    function subscribe(listener) {
        // 添加到监听函数数组
        nextListeners.push(listener)
        
        let isSubscribe = true //设置一个标志,标志该监听器已经订阅了
        // 返回取消订阅的函数,即从数组中删除该监听函数
        return function unsubscribe() {
            if(!isSubscribe) {
                return // 如果已经取消订阅过了,直接返回
            }
            
            isSubscribe = false
            // 从下一轮的监听函数数组(用于下一次dispatch)中删除这个监听器。
            const index = nextListeners.indexOf(listener)
            nextListeners.splice(index, 1)
        }
    }
    复制代码

    dispatch

    function dispatch(action) {
        //调用reducer,得到新state
        currentState = currentReducer(currentState, action);
        
        //更新监听数组
        currentListener = nextListener;
        //调用监听数组中的所有监听函数
        for(let i = 0; i < currentListener.length; i++) {
            const listener = currentListener[i];
            listener();
        }
    }
    复制代码

    createStore这个方法的基本功能我们已经实现了,但是调用createStore方法需要提供reducer,让我们来思考一下reducer的作用。

    combineReducers

    在理解combineReducers之前,我们先来想想reducer的功能:reducer接受一个旧的状态和一个action,当这个action被触发的时候,reducer处理后返回一个新状态。

    也就是说 ,reducer负责状态的管理(或者说更新)。在实际使用中,我们应用的状态是可以分成很多个模块的,比如一个典型社交网站的状态可以分为:用户个人信息,好友列表,消息列表等模块。理论上,我们可以手动用一个reducer去处理所有状态的更新,但是这样做的话,我们一个reducer函数的逻辑就会太多,容易产生混乱。

    因此我们可以将处理逻辑(reducer)也按照模块划分,每个模块再细分成各个子模块,这样我们的逻辑就能很清晰的组合起来。

    对于我们的这种需求,redux提供了combineReducers方法,可以把子reducer合并成一个总的reducer。

    来看看redux源码中combineReducers的主要逻辑:

    function combineReducers(reducers) {
        //先获取传入reducers对象的所有key
        const reducerKeys = Object.keys(reducers)
        const finalReducers = {} // 最后真正有效的reducer存在这里
        
        //下面从reducers中筛选出有效的reducer
        for(let i = 0; i < reducerKeys.length; i++){
            const key  = reducerKeys[i]
            
            if(typeof reducers[key] === 'function') {
                finalReducers[key] = reducers[key] 
            }
        }
        const finalReducerKeys = Object.keys(finalReducers);
        
        //这里assertReducerShape函数做的事情是:
        // 检查finalReducer中的reducer接受一个初始action或一个未知的action时,是否依旧能够返回有效的值。
        let shapeAssertionError
      	try {
        	assertReducerShape(finalReducers)
      	} catch (e) {
        	shapeAssertionError = e
      	}
        
        //返回合并后的reducer
        return function combination(state= {}, action){
      		//这里的逻辑是:
        	//取得每个子reducer对应得state,与action一起作为参数给每个子reducer执行。
        	let hasChanged = false //标志state是否有变化
            let nextState = {}
            for(let i = 0; i < finalReducerKeys.length; i++) {
                //得到本次循环的子reducer
                const key = finalReducerKeys[i]
                const reducer = finalReducers[key]
                //得到该子reducer对应的旧状态
                const previousStateForKey = state[key]
                //调用子reducer得到新状态
                const nextStateForKey = reducer(previousStateForKey, action)
                //存到nextState中(总的状态)
                nextState[key] = nextStateForKey
                //到这里时有一个问题:
                //就是如果子reducer不能处理该action,那么会返回previousStateForKey
                //也就是旧状态,当所有状态都没改变时,我们直接返回之前的state就可以了。
                hasChanged = hasChanged || previousStateForKey !== nextStateForKey
            }
            return hasChanged ? nextState : state
        }
    } 
    复制代码

    为什么需要中间件

    在redux的设计思想中,reducer应该是一个纯函数

    维基百科关于纯函数的定义:

    程序设计中,若一个函数符合以下要求,则它可能被认为是纯函数

    • 此函数在相同的输入值时,需产生相同的输出。函数的输出和输入值以外的其他隐藏信息或状态无关,也和由I/O设备产生的外部输出无关。
    • 该函数不能有语义上可观察的函数副作用,诸如“触发事件”,使输出设备输出,或更改输出值以外物件的内容等。

    纯函数的输出可以不用和所有的输入值有关,甚至可以和所有的输入值都无关。但纯函数的输出不能和输入值以外的任何资讯有关。纯函数可以传回多个输出值,但上述的原则需针对所有输出值都要成立。若引数是传引用调用,若有对参数物件的更改,就会影响函数以外物件的内容,因此就不是纯函数。

    总结一下,纯函数的重点在于:

    • 相同的输入产生相同的输出(不能在内部使用Math.random,Date.now这些方法影响输出)
    • 输出不能和输入值以外的任何东西有关(不能调用API获得其他数据)
    • 函数内部不能影响函数外部的任何东西(不能直接改变传入的引用变量),即不会突变

    reducer为什么要求使用纯函数,文档里也有提到,总结下来有这几点:

    • state是根据reducer创建出来的,所以reducer是和state紧密相关的,对于state,我们有时候需要有一些需求(比如打印每一次更新前后的state,或者回到某一次更新前的state)这就对reducer有一些要求。

    • 纯函数更易于调试

      • 比如我们调试时希望action和对应的新旧state能够被打印出来,如果新state是在旧state上修改的,即使用同一个引用,那么就不能打印出新旧两种状态了。
      • 如果函数的输出具有随机性,或者依赖外部的任何东西,都会让我们调试时很难定位问题。
    • 如果不使用纯函数,那么在比较新旧状态对应的两个对象时,我们就不得不深比较了,深比较是非常浪费性能的。相反的,如果对于所有可能被修改的对象(比如reducer被调用了一次,传入的state就可能被改变),我们都新建一个对象并赋值,两个对象有不同的地址。那么浅比较就可以了。

    至此,我们已经知道了,reducer是一个纯函数,那么如果我们在应用中确实需要处理一些副作用(比如异步处理,调用API等操作),那么该怎么办呢?这就是中间件解决的问题。下面我们就来讲讲redux中的中间件。

    中间件处理副作用的机制

    中间件在redux中位于什么位置,我们可以通过这两张图来看一下。

    先来看看不用中间件时的redux工作流程:

     

    redux工作流程_同步

     

     

    1. dispatch一个action
    2. 这个action被reducer处理
    3. reducer根据action更新store(中的state)

    而用了中间件之后的工作流程是这样的:

     

    redux工作流程_中间件

     

     

    1. dispatch一个action
    2. 这个action先被中间件处理(比如在这里发送一个异步请求)
    3. 中间件处理结束后,再发送一个action(有可能是原来的action,也可能是不同的action,视中间件功能而不同)
    4. 中间件发出的action可能继续被另一个中间件处理,进行类似3的步骤。即中间件可以链式串联。
    5. 最后一个中间件处理完后,dispatch一个符合reducer处理标准的action
    6. 这个标准的action被reducer处理,
    7. reducer根据action更新store(中的state)

    那么中间件该如何融合到redux中呢?

    在上面的流程中,2-4的步骤是关于中间件的,但凡我们想要添加一个中间件,我们就需要写一套2-4的逻辑。如果每个中间件我们手动串联的话,就不够灵活,增删改以及调整顺序,都需要修改中间件串联的逻辑。

    所以redux提供了一种解决方案,将中间件的串联操作进行了封装,经过封装后,上面的步骤2-5就可以成为一个整体,如下图:

     

    封装中间件后的逻辑

     

     

    我们只需要改造store自带的dispatch方法,action发生后,先给中间件处理,最后再dispatch一个action交给reducer去改变状态。

    中间件在redux的实现

    还记得redux 的createStore()方法的第三个参数enhancer吗:

    function createStore(reducer, preloadedState, enhancer) {
        if(enhancer是有效的){  
            return enhancer(createStore)(reducer, preloadedState)
        } 
        
        //...
    }
    复制代码

    在这里,我们可以看到,enhancer(可以叫做强化器)是一个函数,这个函数接受一个’常规createStore函数’作为参数,返回一个加强后的createStore函数。

    这个加强的过程中做的事情,其实就是改造dispatch,添加上中间件。redux提供的applyMiddleware()方法返回的就是一个enhancer。

    applyMiddleware,顾名思义,应用中间件,输入为若干中间件,输出为enhancer。下面就来看看这个方法的源码:

    function applyMiddleware(...middlewares) {
        // 返回一个函数A,函数A的参数是一个createStore函数。
        // 函数A的返回值是函数B,其实也就是一个加强后的createStore函数,大括号内的是函数B的函数体
        return createStore => (...args) => {
            //用参数传进来的createStore创建一个store
            const store  = createStore(...args)
            //注意,我们在这里需要改造的只是store的dispatch方法
            
            let dispatch = () => {  // 一个临时的dispatch
                					//作用是在dispatch改造完成前调用dispatch只会打印错误信息
                throw new Error(`一些错误信息`)
            } 
            //接下来我们准备将每个中间件与我们的state关联起来(通过传入getState方法),得到改造函数。
            const middlewareAPI = {
                getState: store.getState,
                dispatch: (...args) => dispatch(...args)
            }
            //middlewares是一个中间件函数数组,中间件函数的返回值是一个改造dispatch的函数
            //调用数组中的每个中间件函数,得到所有的改造函数
            const chain = middlewares.map(middleware => middleware(middlewareAPI))
            
            //将这些改造函数compose(翻译:构成,整理成)成一个函数
            //用compose后的函数去改造store的dispatch
            dispatch = compose(...chain)(store.dispatch)
            // compose方法的作用是,例如这样调用:
            // compose(func1,func2,func3)
            // 返回一个函数: (...args) => func1( func2( func3(...args) ) )
            // 即传入的dispatch被func3改造后得到一个新的dispatch,新的dispatch继续被func2改造...
            
            //返回store,用改造后的dispatch方法替换store中的dispatch
            return {
                ...store,
                dispatch
            }
        }
    }
    复制代码

    总结一下,applyMiddleware的作用是:

    1. 从middleware中获取改造函数
    2. 把所有改造函数compose成一个改造函数
    3. 改造dispatch方法

    总结

    至此,redux的核心源码已经讲完了,最后不得不感叹,redux写的真的美,真tm的简洁。

    redux的核心功能还是创建一个store来管理state。通过reducer的层级划分,可以得到一颗state树,这棵树如何与其他框架(如react)共同工作,我会再写一篇《react-redux源码解读》的博客探究探究这个问题,敬请期待。


    作者:村上春树
    链接:https://juejin.im/post/5b9617835188255c781c9e2f
    来源:掘金
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

    展开全文
  • Redux 中文文档

    2019-02-23 17:30:21
  • Redux

    2019-01-04 19:38:18
    Redux是为了解决应用程序状态(State)管理而提出的一种解决方案。对于应用开发来讲,UI上显示的数据、控件状态、登陆状态、数据加载画面的不同状态等等全部可以看作状态。 Redux 的三个概念:Reducer、Action、...

    转载:https://www.cnblogs.com/tarena/p/8473961.html

    Redux是为了解决应用程序状态(State)管理而提出的一种解决方案。对于应用开发来讲,UI上显示的数据、控件状态、登陆状态、数据加载画面的不同状态等等全部可以看作状态。

    Redux 的三个概念:Reducer、Action、Store。

    Store 一般负责:保存应用状态、提供访问状态的方法、派发Action的方法以及对于状态订阅者的注册和取消等。(可以理解成内存数据库)。

    Reducer 其实就是用来维护状态的。reducer就是对数组元素进行累加计算成为一个值。(可以理解成数据库表,但这种说法不太精确)。

    Action 在Redux规范中,所有的会引发状态更新的交互行为都必须通过一个显性定义的Action来进行。(Reducer和Store之间的通信靠Action)。

    注意:

    export interface Action {
    
      type: string;
    
      payload?: any; // 这个值可有可无
    
    }

    ngrx是一套利用RxJS的类库,其中@ngrx/store就是基于Redux规范制定的Angular2框架。

    @ngrx/store

    RxJS 基于Redux的设计思想,为Angular应用提供强有力的状态管理工具。

    @ngrx/store是一个旨在提高写性能的控制状态的容器,在angular的应用程序上是一致的。

    核心:

    1. State是一个不可变的数据结构
    2. Action描述State的改变
    3. Reducer(纯函数)拿到下一个State和之前的State来计算一个新的State
    4. 通过Store访问State,一个可观测state和一个actions观察者

    安装使用:npm install @ngrx/store --save 或 yarn add @ngrx/store

    State 与 Angular的Component的关系

    1. State驱动Component进行渲染(this.store.dispatch)
    2. Component发action来改变State

    @ngrx/effects

    @ngrx/effects 提供一套API(装饰器@Effect( )和Action)来帮助检查Store.dispatch( )出来的Action。将特定类型的Action过滤出来进行处理,监听特定Action,当发现特定的Action发出之后,自动执行某些操作,然后将处理的结果重新发送回store中。

    核心:

    1. 监听派发出来(@ngrx/store 的Store.dispatch)的Action
    2. 隔离业务和组件(Component只通过select state 和 dispatch actions即可)
    3. 提供新的reducer state(基于网络请求、web socket 消息 或 time事件驱动[定时刷新])

    安装使用:npm install @ngrx/effects --save 或 yarn add @ngrx/effects

    Effects 通常注入到service类

     

    注册Effect

    EffectsModule.forRoot( ) 必须在根模块下注册,如果不需要注册任何根级别的Effect,可以Provider一个空数组。

    //app.module.ts
    @NgModule({
      imports: [
        EffectsModule.forRoot([
          SourceA,
          SourceB,
          SourceC,
        ])
        //EffectsModule.forRoot([])  //提供一个空数组
      ]
    })
    export class AppModule {  }
    

    EffectsModule.forFeature( )可以在任何ng模块使用(导入)EffectsModule.forFeature(),无论是根模块(AppModule),还是任何一个功能模块。

    //feature.module.ts
    @NgModule({
      imports: [
        EffectsModule.forFeature([
          FeatureSourceA,
          FeatureSourceB,
          FeatureSourceC,
        ])
      ]
    })
    export class FeatureModule { }
    

    Init Action

    import { Injectable } from '@angular/core';
    import { Actions } from '@ngrx/effects';
    
    @Injectable()
    export class SomeEffectsClass {
      constructor(private actions$: Actions) {}
    }
    

    示例:

    import { Action } from '@ngrx/store';
    import { Actions, Effect } from '@ngrx/effects';
    import { defer } from 'rxjs/observable/defer';
    import * as auth from '../actions/auth.actions';
    
    @Injectable()
    export class AppEffects {
    
        @Effect()
        init$: Observable<Action> = defer(() => {
          return of(new auth.LoginAction());
        });
    
        constructor(private actions$: Actions) { }
    }
    

    ofType

    import { Injectable } from '@angular/core';
    import { Actions, Effect, ofType } from '@ngrx/effects';
    import { tap } from 'rxjs/operators';
    
    @Injectable()
    export class SomeEffectsClass {
      constructor(private actions$: Actions) {}
    
      @Effect() authActions$ = this.action$.pipe(
        ofType<LoginAction | LogoutAction>('LOGIN', 'LOGOUT'),
        tap(action => console.log(action))
      );
    }
    

    @ngrx/router-store

    通过@ngrx/store绑定连接angular路由

    安装使用:npm install @ngrx/router-store --save 或 yarn add @ngrx/router-store

    /**
     * Payload of ROUTER_NAVIGATION.
     */
    export declare type RouterNavigationPayload<T> = {
      routerState: T;
      event: RoutesRecognized;
    };
    /**
     * An action dispatched when the router navigates.
     */
    export declare type RouterNavigationAction<T = RouterStateSnapshot> = {
      type: typeof ROUTER_NAVIGATION;
      payload: RouterNavigationPayload<T>;
    };
    

     

    展开全文
  • redux

    2020-06-30 11:57:02
    Redux介绍 单一数据源 整个单页应用的 state 都被储存在store的内部,可以通过store.getState()获取,再作为props传给对应的组件。 state应该尽量少嵌套扁平化,通过id相互引用数据。 import { createStore } from...
  • Redux知识总结一(一篇就能搞明白redux的用法) 一、什么是Redux? 官网:Redux 是 JavaScript应用的状态容器,提供可预测化的状态管理; Javascript应用:是指任何Javascript构建的项目,而不是仅仅是React框架构建...
  • Redux 快速上手指南

    2019-07-02 09:13:32
    Redux简介 如果要用一句话来概括Redux,那么可以使用官网的这句话:Redux是针对JavaScript应用的可预测状态容器。此句话虽然简单,但包含了以下几个含义: 可预测性(predictable): 因为Redux用了reducer与纯函数...
  • Redux状态管理

    2019-08-14 21:21:39
    Redux 资料 react-redux流程与实现分析 《看漫画,学 Redux》 —— A cartoon intro to Redux Redux 中文网 React 实践心得:react-redux 之 connect 方法详解 解读redux工作原理 React 是构建 UI 的库,只是 DOM...
  • 前言 本文不会拿redux、react-redux等一些...本文通过一个简单的例子展开,一点点自己去实现一个redux+react-redux,让大家充分理解redux+react-redux出现的必要。 预备知识 在阅读本文之前,希望大家对以下知...
  • Redux之我见

    2020-04-10 11:13:05
    Redux 架构 1.介绍基本概念和用法: Redux 是一个有用的架构,但不是非用不可。事实上,大多数情况,你可以不用它,只用 React 就够了。 曾经有人说过这样一句话:“如果你不知道是否需要 Redux,那就是不需要它...
  • Redux 配置说明以及实例教程    为了形成对React 技术栈选型应用,在学习redux 过程中“痛心疾首” 般还是花费了不少的力气,整整花费了大概有2周的时间去了解文档中的API。   如果您在学习Redux,当然你可能...
  • Redux 是 JavaScript 状态容器,提供可预测化的状态管理 Redux 由 Flux 演变而来,但受 Elm 的启发,避开了 Flux 的复杂性 简单的说,redux把应用的所有状态集中在一起,这样可在应用越来越大、越来越复杂时...
  • Vuex与Redux对比

    2018-06-21 09:48:14
    Redux则是一个纯粹的状态管理系统,React利用React-Redux将它与React框架结合起来。VUEX与React-Redux:一个是针对VUE优化的状态管理系统,一个仅是常规的状态管理系统(Redux)与React框架的结合版...
  • 深入浅出React和Redux

    2020-07-30 23:33:28
    《深入浅出React和Redux》 ,本书作者是资深开发人员,有过多年的开发经验,总结了自己使用React和Redux的实战经验,系统分析React和Redux结合的优势,与开发技巧,为开发大型系统提供参考。主要内容包括:React的...
  • Vuex与Redux(React-Redux) Vuex是吸收了Redux的经验,放弃了一些特性并做了一些优化,代价就是Vuex只能和Vue配合。 Redux则是一个纯粹的状态管理系统,React利用React-Redux将它与React框架结合起来。 &...
  • redux学习笔记(一)地址:redux学习笔记(一) Redux Dev Tools的安装 之前已经制作了Redux中state仓库,也可以从仓库中取出数据了。接下来我们需要在控制台调试这些仓库里的数据,需要使用Redux DevTools。安装这...
1 2 3 4 5 ... 20
收藏数 29,701
精华内容 11,880
关键字:

redux