================================================================
1. 解决什么问题?
2. 基本概念
-
Redux主要作用就是统一状态管理,将分散在各个组件中的状态全部放在统一的store对象中进行管理,适用于状态特别分散、状态共用较多、状态传递较深的情形。
-
Redux 是一个提供可预测化状态管理的容器,主要包含store,reducer,action
① 应用中的 state 以对象树的形式存储在 store 中
② state是只读的,惟一改变 state 的方法就是触发 action
③ action 是一个用于描述已发生事件的普通对象
④ 通过 dispatch action来改变 state,描述 action如何改变 state 的函数叫 reducer
3. 分解
- action
可参考action设计规范, type,payload,error.meta,其中 type 是必须的
异步action,通过中间件实现
- reducer
reducer必须是纯函数
可以拆分成多个reducer, 通过combineReducers合并
- store
单一Store
根据reducer创建 store.createStore(reducer,initialState)
1. 图示
2. 步骤分解
store.dispatch(action);
let nextState = todoApp(previousState, action);
// 设置监听函数
store.subscribe(listener);
- listener可以通过store.getState()得到当前状态。如果使用的是 React,这时可以触发重新渲染 View
3. redux的原则
-
state 是只读的,唯一修改它的方式是 actions
-
更新的唯一方式:dispatch(action) -> reducer -> new state
-
Reducer 函数必须是“纯”的 —— 不能修改它的参数,也不能有副作用(不能对函数作用域之外的变量做任何更改。不要改变函数作用域以外的变量,不要调用其他会改变的函数,也不要dispatch actions等)
-
将分散在各个组件的状态统一放在store全局对象上,这样在每个组件需要状态时可以直接引入store取出状态,避免了状态之间的多层传递
-
对state的所有操作必须通过dispatch发出action给reducer,由reducer根据action的type对state进行操作并返回新的state,这使state的改变可容易追踪和可控
-
因为store是全局的,因此子组件不需要组件向父组件开放过多的接口(不需要传入那么多的变量给prop)
1. 目录结构规划
src |
-| commponents
-| store
App.js
index.css
index.js
store |
-| index.js
-| reducers.js
-| actionCreators
-| actionTypes
commponents |
-| Foot.jsx
-| Item.jsx
-| List.jsx
-| Top.jsx
2. store/index.js
- 说明:全局状态对象store创建和导出,它需要提供一个返回 state 的函数——reducers
import {createStore, applyMiddleware, compose} from ‘redux’
import reducers from ‘./reducers’
import ReduxThunk from ‘redux-thunk’
// 处理redux-thunk的兼容问题
const composeEnhancers =
typeof window === ‘object’ &&
window.REDUX_DEVTOOLS_EXTENSION_COMPOSE ?
window.REDUX_DEVTOOLS_EXTENSION_COMPOSE({}) : compose;
const enhancer = composeEnhancers(
applyMiddleware(ReduxThunk)
);
//创建store状态管理对象
const store = createStore(reducers, enhancer);
export default store;
3. store/actionTypes
- 说明:
① Action对象描述你想做出的改变或者将触发的事件,它必须带有一个type 属性,为action提供type描述(说明),约定action在reducers中的匹配,这个文件就是将所有的type定义为常量,避免编写时出错
② actionCreators中定义的每个回调函数返回的action对象上都有一个type属性,用以标识每个action
③ 在界面通过store.dispatch(action)(这里的action都是从actionCreators中引入的)派发到renducers时,renducers函数是通过actions.type来判断传过来的是哪个action
export const GET_ALL_ITEM = ‘get_all_item’; // 存TODO
export const CHANGE_TODO_ITEM = ‘change_todo_item’; // 修改一条TODO的选中状态
export const DEL_TODO_ITEM = ‘del_todo_item’; //删除一条TODO
export const ADD_TODO_ITEM = ‘add_todo_item’; //添加一条TODO
export const DEL_FINISHED_TODO_ITEM = ‘del_finished_todo_item’; //删除所有已经完成的TODO
export const IS_CHECKED_ALL_TODO_ITEM = ‘is_checked_all_todo_item’; //删除所有已经完成的TODO
4. store/actionCreators.js
- 说明:
① 这里定义了所有的action,通过回调函数的形式返回action对象,使用回调函数的目的是为了让界面组件可以调用
② 每个action都有一个type属性,它定义了该action的类型;同时接受一个参数,是界面组件需要的状态,由action带给reducer
③ 界面上调用这里定义的回调函数(返回一个对象,对象的第一个属性type是,第二个参数是从界面接收的参数),通过store.dispatch(action)将action派发到reducers,reducres根据action.type属性对state进行相应处理
④ action充当了界面与reducers的桥梁,将界面中组件需要的数据和对数据进行的处理传给renducers,由reducers负责更新state
import {
GET_ALL_ITEM,
CHANGE_TODO_ITEM,
DEL_TODO_ITEM,
ADD_TODO_ITEM,
DEL_FINISHED_TODO_ITEM,
IS_CHECKED_ALL_TODO_ITEM
} from ‘./actionTypes’
// 1. 存Todo
export const getAllItemAction = (todos)=>({
type: GET_ALL_ITEM,
todos
});
// 2. 单个TODO选中与否
export const getChangeItemFinishedAction = (todoId, flag)=>({
type: CHANGE_TODO_ITEM,
todoId,
flag
});
// 3. 单个TODO删除
export const getDelItemAction = (todoId)=>({
type: DEL_TODO_ITEM,
todoId
});
// 4. 添加一条记录
export const getAddItemAction = (todo)=>({
type: ADD_TODO_ITEM,
todo
});
// 5. 删除所有已经完成的记录
export const getRemoveFinishedItemAction = ()=>({
type: DEL_FINISHED_TODO_ITEM
});
// 6. 全选与非全选
export const getIsCheckedAllAction = (flag)=>({
type: IS_CHECKED_ALL_TODO_ITEM,
flag
});
5. store/reducers
- 说明:
① reducer 的职责是接收当前 state 和一个 action 然后返回新的 state
② 状态state以及操作状态的方法都在这里,符合了数据和操作数据的方法在同一个文件的原则
③ 根据在界面组件中通过store.dispatch(action)派发过来的action.type,匹配到相应的操作状态的方法,并根据组件传入的 参数,对state进行相应操作,将当前state直接进行替换,并合并旧的state返回给store
④ reducer是直接用新的state替换旧的state,而不是更新旧的state,就是说旧的state还是保留的;返回最新的state给界面组件,同时保留了新state和旧state存入store,使state可以回溯和方便进行时间旅行调试
import {
GET_ALL_ITEM,
CHANGE_TODO_ITEM,
DEL_TODO_ITEM,
ADD_TODO_ITEM,
DEL_FINISHED_TODO_ITEM,
IS_CHECKED_ALL_TODO_ITEM
} from ‘./actionTypes’
// 初始状态数据
const defaultState = {
todos: [],
finishedCount: 0
};
//根据action的type,对state进行相应操作,并返回新的state
export default (state = defaultState, action)=>{
console.log(state, action);
// 1. 存所有的Todo
if(action.type === GET_ALL_ITEM){
const newState = JSON.parse(JSON.stringify(state));
newState.todos = action.todos;
return newState;
}
// 2. 选中与取消选中
if(action.type === CHANGE_TODO_ITEM){
const newState = JSON.parse(JSON.stringify(state));
// 1. 遍历
let tempFinishedCount = 0;
newState.todos.forEach((todo, index)=>{
if(action.todoId === todo.id){
todo.finished = action.flag;
}
if(todo.finished){
tempFinishedCount += 1;
}
});
// 2. 返回一个新的状态
newState.finishedCount = tempFinishedCount;
return newState;
}
// 3. 单个TODO删除
if(action.type === DEL_TODO_ITEM){
const newState = JSON.parse(JSON.stringify(state));
// 1. 遍历
let tempFinishedCount = 0;
newState.todos.forEach((todo, index)=>{
if(action.todoId === todo.id){
newState.todos.splice(index, 1);
}
});
// 处理选中的
newState.todos.forEach((todo, index)=>{
if(todo.finished){
tempFinishedCount += 1;
}
});
// 2. 返回新状态
newState.finishedCount = tempFinishedCount;
return newState;
}
// 4. 添加一条记录
if(action.type === ADD_TODO_ITEM){
console.log(action);
const newState = JSON.parse(JSON.stringify(state));
newState.todos.push(action.todo);
return newState;
}
// 5. 删除所有已经完成的记录
if(action.type === DEL_FINISHED_TODO_ITEM){
const newState = JSON.parse(JSON.stringify(state));
let tempArr = [];
newState.todos.forEach((todo, index)=>{
if(!todo.finished){ // 没有完成的任务
tempArr.push(todo);
}
});
// 2. 返回新状态
newState.finishedCount = 0;
newState.todos = tempArr;
return newState;
}
// 6. 全选与非全选
if(action.type === IS_CHECKED_ALL_TODO_ITEM){
const newState = JSON.parse(JSON.stringify(state));
// 6.1. 遍历
let tempFinishedCount = 0;
newState.todos.forEach((todo, index)=>{
todo.finished = action.flag;
});
// 处理选中的
newState.todos.forEach((todo, index)=>{
if(todo.finished){
tempFinishedCount += 1;
}
});
// 6.2. 返回新状态
newState.finishedCount = tempFinishedCount;
return newState;
}
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:前端)
![](https://i-blog.csdnimg.cn/blog_migrate/e85dfb084ab61b75582c333c5973b448.jpeg)
最后的最后
面试题千万不要死记,一定要自己理解,用自己的方式表达出来,在这里预祝各位成功拿下自己心仪的offer。
需要完整面试题的朋友可以点击蓝色字体获取
img-u2a2EsAv-1712953156666)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!
[外链图片转存中…(img-KOSHkCb9-1712953156667)]
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:前端)
![](https://i-blog.csdnimg.cn/blog_migrate/e85dfb084ab61b75582c333c5973b448.jpeg)
最后的最后
面试题千万不要死记,一定要自己理解,用自己的方式表达出来,在这里预祝各位成功拿下自己心仪的offer。
需要完整面试题的朋友可以点击蓝色字体获取
[外链图片转存中…(img-Ou0nwlXv-1712953156667)]
[外链图片转存中…(img-2uWINOwr-1712953156667)]
[外链图片转存中…(img-N7EdwPwv-1712953156667)]
[外链图片转存中…(img-KSdQE9GD-1712953156668)]