Skip to content

🚀 Redux使用与源码解析 ​

📚 Redux基础使用 ​

1. 基本概念 🌟 ​

Redux是一个可预测的状态容器,它遵循三大原则:

  • 单一数据源
  • 状态是只读的
  • 使用纯函数进行修改

2. 基础使用示例 ​

js
import { createStore } from 'redux';

// 1️⃣ 初始状态
const initial = {
    name: "chenyq",
    age: 18
}

// 2️⃣ Reducer函数:处理状态更新的纯函数
const reducer = function reducer(state = initial, action) {
    // 创建新的状态对象,避免直接修改
    let newState = {...state}
    
    switch(action.type){
        case 'changeName':
            newState.name = 'xxx';
            break;
        case 'changeAge':
            newState.age = 16;
            break;
        default:
            // 默认返回原状态
            return state;
    }
    return newState;
}

// 3️⃣ 创建Store
const store = createStore(reducer);
export default store;

// 4️⃣ 使用Store
import store from '@/store'

// 获取状态
let { name, age } = store.getState()

// 修改状态
store.dispatch({ type: "change_name", name: "abc" })

3. 组件更新控制 🔄 ​

js
// 方法一:通过数字自增控制更新
const [num, forceUpdate] = useState(0)
useEffect(() => {
    let unsubscribe = store.subscribe(() => {
        forceUpdate(num + 1)
    })
    return () => {
        unsubscribe()  // 清理订阅
    }
}, [num])

// 方法二:使用时间戳控制更新
const [_, forceUpdate] = useState(0)
useEffect(() => {
    store.subscribe(() => {
        forceUpdate(+new Date())
    })
}, [])

// 方法三:类组件更新方式
componentDidMount() {
    const { store } = this.context;
    store.subscribe(() => {
        this.forceUpdate();
    });
}

🌈 Redux工程化实践 ​

1. 目录结构 📁 ​

src/
  ├── store/
  │   ├── reducers/
  │   │   ├── index.js      # reducer合并
  │   │   └── xxxReducer.js # 具体模块reducer
  │   ├── actions/
  │   │   ├── index.js      # action合并
  │   │   └── xxxAction.js  # 具体模块action
  │   ├── index.js          # store配置
  │   └── action-types.js   # action类型常量

2. Reducer模块化 🔨 ​

Reducer模板 ​

js
import _ from '@/assets/utils'
import * as TYPES from '../action-types'

const initial = {}

export default function xxxReducer(state = initial, action) {
    // 深克隆确保状态不可变性
    state = _.clone(true, state)
    
    switch(action.type) {
        case TYPES.VOTE_OPP:
            state.xxx = action.xxx
            break;
        case TYPES.VOTE_SUP:
            state.xxx = action.xxx
            break;
        default:
    }
    return state
}

合并Reducer ​

js
import { combineReducers } from 'redux'
import xxxReducer from './xxxReducer'

// 使用combineReducers合并多个reducer
const reducer = combineReducers({
    xxx: xxxReducer,
    // ... 其他reducer
})

export default reducer

3. Action管理 📝 ​

action-types.js ​

js
/* 
 * 统一管理action类型
 * 命名规范:模块名_动作(大写)
 */
export const VOTE_SUP = 'VOTE_SUP'
export const VOTE_OPP = 'VOTE_OPP'
export const MAIN_SUP = 'MAIN_SUP'
export const MAIN_OPP = 'MAIN_OPP'

actionCreator ​

js
// actions/voteAction.js
import * as TYPES from '../action-types'

const voteAction = {
    support() {
        return {
            type: TYPES.VOTE_SUP
        }
    },
    oppose() {
        return {
            type: TYPES.VOTE_OPP
        }
    }
}

export default voteAction

🛠️ Redux中间件 ​

1. redux-logger 📊 ​

js
import { createStore, applyMiddleware } from 'redux';
import reducer from './reducers';
import reduxLogger from 'redux-logger';

const store = createStore(
    reducer,
    applyMiddleware(reduxLogger)
);

2. 异步Action处理 ⚡ ​

redux-promise ​

js
const action = {
    async support() {
        await delay()
        return {
            type: TYPES.VOTE_SUP
        }
    }
}

redux-thunk ​

js
const action = {
    support() {
        /* 
         * redux-thunk工作原理:
         * 1. 返回函数而不是对象
         * 2. 函数接收dispatch作为参数
         * 3. 在异步操作完成后手动dispatch
         */
        return async(dispatch) => {
            await delay()
            dispatch({ type: TYPES.VOTE_SUP })
        }
    }
}

🎯 Redux Toolkit(RTK) ​

1. 基本配置 ​

js
import { configureStore } from '@reduxjs/toolkit'
import reduxLogger from 'redux-logger'
import taskSliceReducer from './features/taskSlice'

const store = configureStore({
    // 指定reducer
    reducer: {
        task: taskSliceReducer
    },
    // 配置中间件
    middleware: [reduxLogger]
})

2. Slice示例 ​

js
import { createSlice } from '@reduxjs/toolkit'

const taskSlice = createSlice({
    name: 'task',
    initialState: {
        taskList: null
    },
    reducers: {
        getAllTaskList(state, action) {
            // RTK内部使用Immer,可以"直接修改"状态
            state.taskList = action.payload
        }
    }
})

💡 最佳实践 ​

  1. 使用常量定义action类型
  2. 遵循不可变性原则
  3. 保持reducer纯函数特性
  4. 合理拆分reducer模块

📚 参考资料 ​