Redux快速上手之【Redux工程化】

前言

  • 细阅此文章大概需要 <math xmlns="http://www.w3.org/1998/Math/MathML"> 7 分钟 \color{red}{7分钟} </math>7分钟左右
  • 本篇中讲述了:
    1. 第一步:reducer的拆分和合并
    2. 第二步:派发行为标识宏管理
    3. 第三步:actionCreator的创建
    4. combineReducers源码解析(待补充)
  • 欢迎在评论区探讨、留言,如果认为有任何错误都还请您不吝赐教,万分感谢。希望今后能和大家共同学习、进步。
  • 下一篇会尽快更新,已经写好的文章也会在今后进行不定期的修订、更新。
  • 如果觉得这篇文章对您有帮助,还请点个赞支持一下,谢谢大家!
  • 欢迎转载,注明出处即可。

redux工程化简述

在现在的前端开发工作中,一般一个项目都是由一个或多个团队共同维护的,当不同的人同时对不同的模块进行开发时,需要操作的redux部分也肯定会有重叠,不仅需要频繁的处理冲突,而且可能一个公共容器文件就有成百上千行的代码,非常的不利于我们的维护和管理。所以我们在redux的使用上需要进行一些工程化的改造。

第一步:reducer的拆分和合并

首先,将reducer按照模块进行拆分,每个模块都创建自己的reducer。最后把所有的reducer合并为一个整体,传递给store。

创建reducer目录

store文件夹下,创建一个reducers文件夹

  1. 创建一个index.js文件,最终我们将会在该文件中进行不同reducer的合并。
  2. 分别创建不同的xxxReducer.js文件,用来创建不同模块的reducer

创建不同模块的reducer

javascript 复制代码
/* moduleAReducer.js */ 

// 为reducer创建一个初始的状态值
const initState = {
   a:0,
   b:0
} 

/*管理员:修改store中的公共状态*/
export default function moduleAReducer(state=initState, action){
// state: store中的公共状态(最开始没有时,为自己设置的初始值)
// action:dispatch执行时传入的行为对象,行为对象中必须包含type属性(代表派发的行为标识)

// 为了避免在过程中对`state`进行修改,而是需要在`return时`对`state`进行整体替换,所以就需要我们在最开始把获取的state克隆一份
state={...state}

  switch(action.type){
    // ...根据传递的type不同,修改不同的状态信息
    case "adda":
        state.a++
        break;
    case "addb":
        state.b++
        break;
    default:
        break;
            
  }
  return state // 返回的信息将会替换store容器中所有的公共状态
}
javascript 复制代码
/* moduleBReducer.js */ 

// 为reducer创建一个初始的状态值
const initState = {
   c:0,
   d:0
} 

/*管理员:修改store中的公共状态*/
export default function moduleBReducer(state=initState, action){
  state={...state}

  switch(action.type){
    case "addc":
        state.c++
        break;
    case "addd":
        state.d++
        break;
    default:
        break;
            
  }
  return state
}

合并reducer

可以利用redux当中的combinReducers方法来合并多个reducer

js 复制代码
/* 合并各模块的reducer */
import {combinReducers} from 'redux'
import moduleBReducer from './moduleBReducer'
import moduleAeducer from './moduleAeducer'

const reducer = combinReducers({
    moduleA: moduleAeducer
    moduleB: moduleBReducer
})

export default reducer

将reducer合并之后,公共容器中的公共状态,也将按照我们设置的成员名字,分模块来进行管理。

js 复制代码
/* 合并后的公共状态如下 */
state={
    moduleA={
     a:0,
     b:0
    },
    moduleB={
     c:0,
     d:0
    }
}

combinReducers设置什么模块名,合并后公共容器就会以模块名来分别管理各自模块的公共状态,类似作用域,即使不同模块的状态名重复,也并不会冲突。

在使用不同模块的状态时,可以使用对象成员访问的方式进行

js 复制代码
store.getState().moduleA
store.getState().moduleB
// ....

引入到公共容器中

javascript 复制代码
// 创建公共容器
import {createStore} from 'redux'
import reducer from './reducers'

/*创建公共容器*/
const store = createStore(reducer)

export default store

在后代组件中的使用变化

获取不同模块的状态时,可以使用对象成员访问的方式进行

js 复制代码
store.getState().moduleA
store.getState().moduleB
// ....

派发任务时的流程变化

派发的操作没有变化,只是执行dispatch时,会去reducer当中按照代码顺序,逐一从每个模块的case当中匹配行为标识,若当前模块没有,就会去下个模块接着找,直到找到对应的行为标识的case,并执行相应的操作。


第二步:派发行为标识宏管理

背景

在各模块的reducer合并后,当执行dispatch时,会去reducer当中逐一从每个模块的case当中匹配行为标识,这个时候可能会出现一种情况,那就是两个不同模块中,具有两个相同名称的行为标识。虽然名称相同,但是执行对应的操作,可能会导致出现不期望出现的更新。

统一管理行为标识

所以不管是哪个模块,我们派发的行为标识,一定要保证其唯一性。

创建action-types文件

store目录下,创建action-types.js文件,在此文件中,对所有需要派发的行为标识进行统一管理。

js 复制代码
/* 统一管理需要派发的行为标识 */
// 为了保证不冲突,需要有个统一的命名规范,如:模块名_派发的行为标识(整体大写)、
// 使用谓语:变量的存储的值是一致的
// 所有需要派发的行为标识,都放在这里定义

export const moduleA_ADDA = moduleA_ADDA
export const moduleA_ADDB = moduleA_ADDB
export const moduleB_ADDC = moduleA_ADDC
export const moduleB_ADDD = moduleA_ADDD

// .....

在不同模块的reducer中使用

action-types.js文件中引入所有的行为标识,然后进行使用。由于是引用使用,还能更好的避免拼写错误

javascript 复制代码
/* moduleBReducer.js */ 

import * as TYPES from '../action-types'

// 为reducer创建一个初始的状态值
const initState = {
   c:0,
   d:0
} 

/*管理员:修改store中的公共状态*/
export default function moduleBReducer(state=initState, action){
  state={...state}

  switch(action.type){
    case TYPES.moduleB_ADDC: // 使用同一管理的宏标识,不再使用自己写的字符串了
        state.c++
        break;
    case TYPES.moduleB_ADDD: // 使用同一管理的宏标识,不再使用自己写的字符串了
        state.d++
        break;
    default:
        break;
            
  }
  return state
}

在后代组件中派发使用

js 复制代码
/*引入上下文*/
import AppContext from './AppContext';
import * as TYPES from '../action-types'

const moduleB = function () {
  const {store} = useContext(AppContext)
  
   const handleAddC = () => {
        store.dispatch({type:TYPES.moduleB_ADDC})// 使用同一管理的宏标识,不再使用自己写的字符串了
  }
   const handleAddD = () => {
       store.dispatch({type:TYPES.moduleB_ADDD})// 使用同一管理的宏标识,不再使用自己写的字符串了
  }

  return <div>
      <button onclick={handleAddC}>add C</button>
      <button onclick={handleAddD}>add D</button>
  </div>;
};

export default moduleB

第三步:actionCreator的创建

将各模块中派发的行为对象进行统一管理

把派发的行为对象,按照模块进行统一管理。【就是指执行dispatch时,传递进去的那个对象】

创建actions文件

store目录下,创建actions文件夹,并创建index.js文件,在此文件中,我们会将所有的action(行为对象)合并到一起。

创建各个模块的行为对象文件

store目录下的actions文件夹中创建各个模块的action文件,该文件会导出一个对象,对象当中存储的是若干方法,每一个方法执行返回的就是该模块中的某个需要派发的行为对象。该文件涵盖了该模块所有需要派发的行为对象

js 复制代码
/* moduleAAction.js */
import * as TYPES from '../action-types'
const moduleAAction = {
    adda(){ return {type:TYPES.moduleA_ADDA} }
    addb(){ return {type:TYPES.moduleA_ADDB} }
}

export default moduleAAction
js 复制代码
/* moduleBAction.js */
import * as TYPES from '../action-types'
const moduleBAction = {
    addc(){ return {type:TYPES.moduleB_ADDC} }
    addd(){ return {type:TYPES.moduleB_ADDD} }
}

export default moduleBAction

合并为一个action

js 复制代码
/* 合并各模块的action */
import moduleAAction from './moduleAAction'
import moduleBAction from './moduleBAction'

const action = {
    moduleA: moduleAAction
    moduleB: moduleBAction
}

export default action
js 复制代码
/* 合并后的action如:*/

action={
    moduleA:{
        adda(){ return {type:TYPES.moduleA_ADDA} }
        addb(){ return {type:TYPES.moduleA_ADDB} }
    },
    moduleB:{
        addc(){ return {type:TYPES.moduleB_ADDC} }
        addd(){ return {type:TYPES.moduleB_ADDD} }
    },
}

在后代组件中派发

js 复制代码
/*引入上下文*/
import AppContext from './AppContext';
import action  from '../store/actions'

const moduleB = function () {
  const {store} = useContext(AppContext)
  
   const handleAddC = () => {
        // store.dispatch({type:TYPES.moduleB_ADDC})// 使用同一管理的宏标识,不再使用自己写的字符串了
        store.dispatch(action.moduleB.addc) // 直接使用合并后的action中统一管理的行为派发对象,和上面是一致的
  }
   const handleAddD = () => {
       // store.dispatch({type:TYPES.moduleB_ADDD})// 使用同一管理的宏标识,不再使用自己写的字符串了
       store.dispatch(action.moduleB.addd)// 直接使用合并后的action中统一管理的行为派发对象,和上面是一致的
  }

  return <div>
      <button onclick={handleAddC}>add C</button>
      <button onclick={handleAddD}>add D</button>
  </div>;
};

export default moduleB

此操作被称为actionCreator,在目前看来虽然仍是套娃,而且更繁琐,但是在处理react-redux时,会非常有用。

相关推荐
Martin -Tang32 分钟前
vite和webpack的区别
前端·webpack·node.js·vite
迷途小码农零零发33 分钟前
解锁微前端的优秀库
前端
王解1 小时前
webpack loader全解析,从入门到精通(10)
前端·webpack·node.js
老码沉思录1 小时前
写给初学者的React Native 全栈开发实战班
javascript·react native·react.js
老码沉思录1 小时前
React Native 全栈开发实战班 - 第四部分:用户界面进阶之动画效果实现
react native·react.js·ui
我不当帕鲁谁当帕鲁2 小时前
arcgis for js实现FeatureLayer图层弹窗展示所有field字段
前端·javascript·arcgis
那一抹阳光多灿烂2 小时前
工程化实战内功修炼测试题
前端·javascript
放逐者-保持本心,方可放逐2 小时前
微信小程序=》基础=》常见问题=》性能总结
前端·微信小程序·小程序·前端框架
毋若成5 小时前
前端三大组件之CSS,三大选择器,游戏网页仿写
前端·css
红中马喽5 小时前
JS学习日记(webAPI—DOM)
开发语言·前端·javascript·笔记·vscode·学习