美文网首页
Redux初识一

Redux初识一

作者: 逗比寨主 | 来源:发表于2019-10-21 16:12 被阅读0次

写在前面:
新手学习,不喜轻喷。

1.个人初解

redux将整个应用的state存储树级机构的object中,树级结构的顶点为唯一Store,在应用的整个生命周期内,只会存在一个Store。
store中存放state,要修改store中的state必须通过action来触发,使用reducer里面的函数对state修改,个人觉得有点类似于函数式编程。

2.数据流向

2.1.redux是严格的单向数据流

即:view触发action,reducer接收action修改store中的state,view根据判断,重新渲染。

redux.png

2.2.同一个action得到的新的state数据值一定是相等的

reducer是纯函数,只是用来计算下一个state,函数结果是可以预测的,相同的输入,相同的输出,不应该有副作用。

3.redux在运行过程中到底做了什么事情

3.1. redux/src/index.js redux的入口文件

先看一段redux入口源码

import createStore from './createStore'
import combineReducers from './combineReducers'
import bindActionCreators from './bindActionCreators'
import applyMiddleware from './applyMiddleware'
import compose from './compose'
import warning from './utils/warning'
import __DO_NOT_USE__ActionTypes from './utils/actionTypes' //redux自带的action类型

/*
 * This is a dummy function to check if the function name has been altered by minification.
 * If the function has been minified and NODE_ENV !== 'production', warn the user.
 * 判断在非生产环境下,redux代码是否被压缩,使用function.name来进行判断,无实际意义和方法体
 */
function isCrushed() {}

if (
  process.env.NODE_ENV !== 'production' && //当前环境不是生产环境
  typeof isCrushed.name === 'string' && //方法名称是String类型,IE不支持Function.name
  isCrushed.name !== 'isCrushed' //当代码被编译压缩之后,方法名称会改变,该判断标识当前没有被编译和压缩
) {
  warning(
    '您当前正在使用node_env==“production”之外的缩小代码。 ' +
    '这意味着您正在运行一个较慢的redux开发版本。' +
    '你可以使用loose envify(https://github.com/zertosh/loose-envify)来浏览 ' +
    '或在webpack中设置生产模式(https://webpack.js.org/concepts/mode/)' +
    '以确保生产版本的代码正确。'
  )
}

export {
  createStore,
  combineReducers,
  bindActionCreators,
  applyMiddleware,
  compose,
  __DO_NOT_USE__ActionTypes //语义:不要使用ActionTypes
}

该文件主要导出变量和检查当前环境下是否有压缩代码,redux压缩之后,运行性能会有所降低。

关于代码压缩简单理解:
未压缩
function isCrushed() {}
if (
process.env.NODE_ENV !== 'production' &&
typeof isCrushed.name === 'string' &&
isCrushed.name !== 'isCrushed'
)
压缩之后,方法名称会改变,所以等式成立
function d(){}"string"==typeof d.name&&"isCrushed"!==d.name

3.2.redux/src/createStore

createStore(reducer:any,preloadedState?:any,enhancer?:middleware),最终返回一个\color{red}{state tree}实例。可以进行\color{red}{getState}\color{red}{subscribe}监听和 \color{red}{dispatch}派发。

看一段代码:

/**
*创建保存状态树的redux存储。
*更改存储区中数据的唯一方法是对其调用“dispatch()”。
*
*你的应用中应该只有一个store。指定不同的
*部分状态树响应操作,可以组合多个还原器
*通过使用“combinereducers”将其转换为单个reducer函数。
*
*@param{function}reducer返回下一个状态树的函数,给定
*当前状态树和要处理的操作。
*
*@param{any}[preloadedstate]初始状态。您可以选择指定它
*在通用应用程序中对服务器的状态进行优化,或恢复
*以前序列化的用户会话。
*如果使用'combinereducers'生成根还原函数,则必须
*与“combinereducers”键形状相同的对象。
*
*@param{function}[enhancer]存储增强器。您可以选择指定它
*为了增强store的第三方功能,如中间件,
*时间旅行、坚持等。redux附带的唯一存储增强功能
*是“applymiddleware()”。
*
*@return{store}一个redux存储,用于读取状态、分派操作
*并订阅更改。
*/
export default function createStore(reducer, preloadedState, enhancer) {
if (
    (typeof preloadedState === 'function' && typeof enhancer === 'function') ||
    (typeof enhancer === 'function' && typeof arguments[3] === 'function')
  ) {
    throw new Error(
      'It looks like you are passing several store enhancers to ' +
        'createStore(). This is not supported. Instead, compose them ' +
        'together to a single function.'
    )
  }
  
  //检查增强器
  if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
    enhancer = preloadedState
    preloadedState = undefined
  }
  //增强器只能是函数
  if (typeof enhancer !== 'undefined') {
    if (typeof enhancer !== 'function') {
      throw new Error('Expected the enhancer to be a function.')
    }
    //返回增强后的store
    return enhancer(createStore)(reducer, preloadedState)
  }
  //reducer也只能是函数
  if (typeof reducer !== 'function') {
    throw new Error('Expected the reducer to be a function.')
  }
}

enhancer?:middle,增强器,若干个中间件可以通过applymiddleware产生一个增强器,增强器通过Compose函数合并成一个增强器。
applymiddleware代码如下:

export default function applyMiddleware(...middlewares) {
    // 接受若干个中间件参数
   // 返回一个enhancer增强器函数,enhancer的参数是一个createStore函数。等待被enhancer(createStore)
  return createStore => (...args) => {
    // 先创建store或者更新Store
    const store = createStore(...args)
    // 如果没有创建完成就被调用,抛出异常
    let dispatch = () => {
      throw new Error(
        'Dispatching while constructing your middleware is not allowed. ' +
          'Other middleware would not be applied to this dispatch.'
      )
    }
    //暂时存储更新前的Store
    const middlewareAPI = {
      getState: store.getState,
      dispatch: (...args) => dispatch(...args)
    }
    //遍历中间件,将更新前的Store传入,获取更新后的tore数组
    const chain = middlewares.map(middleware => middleware(middlewareAPI))
    //组合中间件,将dispatch传入,即每个中间件都有一个增强后的dispatch
    dispatch = compose(...chain)(store.dispatch)
    //返回一个store和dispatch,即:返回实现了中间件的store增强器
    return {
      ...store,
      dispatch
    }
  }
}

调用逻辑:
1.通过createStore方法创建出一个store
2.定一个dispatch,如果在中间件构造过程中调用,抛出错误提示
3.定义middlewareAPI,有两个方法,一个是getState,另一个是dispatch,将其作为中间件调用的store的桥接
4.middlewares调用Array.prototype.map进行改造,存放在chain
5.用compose整合chain数组,并赋值给dispatch
6.将新的dispatch替换原先的store.dispatch

compose代码如下:

export default function compose(...funcs) {
  if (funcs.length === 0) {
    return arg => arg
  }

  if (funcs.length === 1) {
    return funcs[0]
  }

  return funcs.reduce((a, b) => (...args) => a(b(...args)))
}

compose接收>0个的增强器,如果没有增强器,就返回一个空函数,如果有一个函数,返回函数本身,如果是多个,才会产生合并的过程即compose的过程,最后一行,通过迭代器生成组合迭代函数。
看到这里,很懵逼有没有?有没有?


6af89bc8gw1f8rntaeadij20hs0hsgm7.jpg

redux-thunk、redux-saga都是redux常用的中间件,一般配合applyMiddleware使用,applyMiddleware的作用就是将这些enhancer格式化成redux想要的enhancer,以redux-thunk举个栗子

function createThunkMiddleware(extraArgument) {
  return ({ dispatch, getState }) => next => action => {
    if (typeof action === 'function') {
      return action(dispatch, getState, extraArgument);
    }

    return next(action);
  };
}

const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;

export default thunk;

根据源码来看,我们如果单独使用thunk,应该如下:

thunk = ({ dispatch, getState })=>{
    return next => action => {
        if (typeof action === 'function') {
            return action(dispatch, getState);
        }
        return next(action);
    };
 } 

applyMiddleware处理之后,应该是china数组如下:

const newDispatch;
const middlewareAPI={
  getState:store.getState,
  dispatch: (...args) => newDispatch(...args)
}
const { dispatch, getState } = middlewareAPI;
const  fun1 = (next)=>{
  return action => {
    if (typeof action === 'function') {
        return action(dispatch, getState);
    }
    return next(action);
  }
}
const chain = [fun1]

模拟整个过程:

function makeASandwichWithSecretSauce(forPerson) {
  return function (dispatch) {
    return fetchSecretSauce().then(
      sauce => dispatch(makeASandwich(forPerson, sauce)),
      error => dispatch(apologize('The Sandwich Shop', forPerson, error))
    );
  };
}
// store.dispatch就等价于newDispatch
store.dispatch(makeASandwichWithSecretSauce('Me'))

====> 转换
const forPerson = 'Me';
const action = (dispatch)=>{
    return fetchSecretSauce().then(
      sauce => dispatch(makeASandwich(forPerson, sauce)),
      error => dispatch(apologize('The Sandwich Shop', forPerson, error))
    );
}
newDispatch()

===> typeof action === 'function' 成立时

 ((dispatch)=>{
    return fetchSecretSauce().then(
      sauce => dispatch(makeASandwich(forPerson, sauce)),
      error => dispatch(apologize('The Sandwich Shop', forPerson, error))
    );
  })( (...args) => newDispatch(...args), getState)

====> 计算运行结果
const forPerson = 'Me';
const dispatch = (...args) => newDispatch(...args) ;
fetchSecretSauce().then(
      sauce => dispatch(makeASandwich(forPerson, sauce)),
      error => dispatch(apologize('The Sandwich Shop', forPerson, error))
);
// 其中:
function fetchSecretSauce() {
  return fetch('https://www.google.com/search?q=secret+sauce');
}
function makeASandwich(forPerson, secretSauce) {
  return {
    type: 'MAKE_SANDWICH',
    forPerson,
    secretSauce
  };
}

function apologize(fromPerson, toPerson, error) {
  return {
    type: 'APOLOGIZE',
    fromPerson,
    toPerson,
    error
  };
}
====> 我们这里只计算Promise.resolve的结果,并且假设fetchSecretSauce返回值为'666',即sauce='666'

const forPerson = 'Me';
const dispatch = (...args) => newDispatch(...args) ;
dispatch({
    type: 'MAKE_SANDWICH',
    'Me',
    '666'
})
====> 为了方便对比,我们再次转换一下

const action = {
    type: 'MAKE_SANDWICH',
    'Me',
    '666'
};

const next = store.dispatch

const newDispatch = action =>{
  if (typeof action === 'function') {
    return action(dispatch, getState);
  }
  return next(action);
}

newDispatch(action)

====> 最终结果
store.dispatch({
    type: 'MAKE_SANDWICH',
    'Me',
    '666'
});

相关文章

  • Redux初识一

    写在前面:新手学习,不喜轻喷。 1.个人初解 redux将整个应用的state存储树级机构的object中,树级结...

  • 初识redux

    Redux 是 JavaScript 状态容器,提供可预测化的状态管理。 随着 JavaScript 单页应用开发...

  • 初识Redux

    简书 i蒹葭从风转载请注明原创出处,谢谢!如果读完觉得有收获的话,欢迎点赞加关注 简单介绍一下redux 的一些术...

  • 初识redux

    Redux是一个状态管理的库,它提供可预测的状态管理。原来我们一个js文件只需要维护一个视图的情况下,我们使用原生...

  • 初识Redux

    Redux的基本元素组成 Action本质上就是普通的JavaScript对象。我们约定,action内使用一个字...

  • 初识redux

  • redux-saga 初识

    原文链接如果感兴趣可以加我微信: xiaobei060537, 一起交流。redux-saga 是一个管理 Red...

  • 初识Redux-Saga

    Redus-saga是一个redux的中间件,主要用来简便而优雅的处理redux应用里的副作用(side effe...

  • 初识redux-saga

    最近项目用了dva,dva对于异步action的处理是用了redux-saga,故简单学习了下redux-saga...

  • 六、使用redux管理数据

    一、安装redux 、redux-pack、redux-thunk npm i redux redux-pack ...

网友评论

      本文标题:Redux初识一

      本文链接:https://www.haomeiwen.com/subject/luevmctx.html