一、redux是什么
简单来说,redux就是JavaScript状态容器,提供可预测化的状态管理。也就是可以理解为state的升级版,不再依赖于组件自身的控制,可以独立于组件而存在。
二、为什么需要使用redux
从组件的角度看,如果你的应用有以下场景,可以考虑使用Redux
- 某个组件的状态,需要共享
- 某个组件状态需要在任何地方都可以拿到
- 一个组件需要改变全局状态
- 一个组件需要改变另一个组件的状态
发生上面情况是,如果不使用Redux或者其他状态管理工具,不按照一定规律处理状态的读写,代码很快会变成一团乱麻。你需要一种机制,可以在同一个地方查询状态、改拜年状态、传播状态的变化。
三、Action是什么
1.Action 概念
Action是把数据从应用传到store的有效载荷。它是store数据的唯一来源。一般来说你会通过store.dispacth()将action传到store。
添加新todo任务的action是这样的:
const ADD_TODO = 'ADD_TODO'
{
type: ADD_TODO,
text: 'Build my first Redux app'
}
Action 本质上是 JavaScript 普通对象。我们约定,action 内必须使用一个字符串类型的 type 字段来表示将要执行的动作。多数情况下,type 会被定义成字符串常量。当应用规模越来越大时,建议使用单独的模块或文件来存放 action。
2.Action 创建函数
Action 创建函数就是生成action的方法,"action"和"action 创建函数"这两个概念很容易混淆,所以在使用的时候最好注意区分。
在 Redux 中的 action 创建函数只是简单的返回一个 action:
function addTodo(text) {
return {
type: ADD_TODO,
text
}
}
四、Reducer是什么
1.Reducer概念
Reducers 指定了应用状态的变化如何响应 actions 并发送到 store 的,记住 actions 只是描述了_有事情发生了_这一事实,并没有描述应用如何更新 state。其实说白了,Reducer就是一个纯函数,接受旧的state和action,返回新的state。
(previousState, action) => newState
2.Reducer要注意的点
特别需要注意的一点是reducer必须是一个纯函数,也就是说永远不要reducer中做下面的操作:
- 修改传入的参数
- 执行有副作用的操作,如API 请求和路由跳转
- 调用非纯函数,如Date.now()或Math.random()
总之,就一句话,reducer一定要保持纯净,只要传入的参数相同,返回计算得到的下一个state就一定相同,没有特殊情况、没有副作用、没有API请求、没有变量修改,单纯执行计算。
3.如何定义Reducer
这是官网的一个简单的例子:
//reducer
function counter(state = 0, action) {
switch (action.type) {
case 'INCREMENT':
return state + 1
case 'DECREMENT':
return state - 1
default:
return state
}
}
因为reducer实在是会根据不同的action会返回不同的state,所以它的样式非常的多种多样,所以在这里只是简单的介绍官网给出的一个非常简单的例子而已。
4.combineReducers(reducers)
(1)随着应用变得越来越复杂,可以考虑将 reducer 函数 拆分成多个单独的函数,拆分后的每个函数负责独立管理 state 的一部分。
(2)combineReducers 辅助函数的作用是,把一个由多个不同 reducer 函数作为 value 的 object,合并成一个最终的 reducer 函数,然后就可以对这个 reducer 调用 createStore 方法。
官网的实例:
rootReducer = combineReducers({potato: potatoReducer, tomato: tomatoReducer})
// rootReducer 将返回如下的 state 对象
{
potato: {
// ... potatoes, 和一些其他由 potatoReducer 管理的 state 对象 ...
},
tomato: {
// ... tomatoes, 和一些其他由 tomatoReducer 管理的 state 对象,比如说 sauce 属性 ...
}
}
合并后的reducer可以调用各个子reducer,并将它们返回的结果合并成一个state对象。由 combineReducers() 返回的 state 对象,会将传入的每个 reducer 返回的 state 按其传递给 combineReducers() 时对应的 key 进行命名。
(3)combineReducers的返回值:(Function)一个调用reducers对象里所有reducer的reducer,并且构造一个与reducers对象结构相同的state对象。
(4)每个传入 combineReducers 的 reducer 都需满足以下规则:
- 所有未匹配到的action,必须把它接收到的第一个参数也就是那个state原封不动返回;
- 永远不能返回undefined。当过早return是非常容易犯这个错误,为了避免错误扩散,遇到这种情况时combineReducers会抛出异常;
- 如果传入的state就是undefined,一定要返回对应的reducer的初始state。根据上一条规则,初始state禁止使用undefined。使用ES6的默认参数至语法来设置state很容易,但你也可以手动检查第一个参数是否为undefined。
五、Store是什么
1.Store的概念
按照官网的说法,Store就是把action、reducer联系在一起的对象,Store有以下的职责:
- 维持应用的state;
- 提供 getState() 方法获取 state;
- 提供 dispatch(action) 方法更新 state;
- 通过 subscribe(listener) 注册监听器;
- 通过 subscribe(listener) 返回的函数注销监听器。
要注意的一点是每一个Redux应用只有一个store。
2.如何创建store
创建store非常的简单,只需要我们把之前使用combineReducers()生成的reducer导入即可,并传递createStore()
import { createStore } from 'redux'
import todoApp from './reducers'
let store = createStore(todoApp)
createStore() 的第二个参数是可选的, 用于设置 state 初始状态。这对开发同构应用时非常有用,服务器端 redux 应用的 state 结构可以与客户端保持一致, 那么客户端可以将从网络接收到的服务端 state 直接用于本地数据初始化。
let store = createStore(todoApp, window.STATE_FROM_SERVER)
六、简单实现一个redux
在最近这段时间学习redux的过程中,看了好几遍官方的文档之后还是感觉自己对redux的了解停留在表象,只是知道action、reducer、store分别是一个什么东西,怎么使用它们,但是却对其中的原理并不了解,于是我就去GitHub上将redux源码clone下来分析了一下,准备下一次写一篇关于redux的源码原理分析的文章(等我把源码吃透了先)。那么现在就先使用js来实现一个简单的redux吧。
下面实现的redux主要有如下的几个功能:
- 获取应用的state
- 发送action
- 监听state的变化
先来定义一个store:
const store = {
state: {}, // 全局唯一的state,内部变量,通过getState()获取
listeners: [], // listeners,用来诸如视图更新的操作
dispatch: () => {}, // 分发action
subscribe: () => {}, // 用来订阅state变化
getState: () => {}, // 获取state
}
手动实现一个createStore:
const createStore = (reducer, initialState) => {
// internal variables
const store = {};
store.state = initialState;
store.listeners = [];
// api-subscribe
store.subscribe = (listener) => {
store.listeners.push(listener);
};
// api-dispatch
store.dispatch = (action) => {
store.state = reducer(store.state, action);
store.listeners.forEach(listener => listener());
};
// api-getState
store.getState = () => store.state;
return store;
};
接下来,我们试一下它的效果:
// reducer
function counter(state = 0, action) {
switch (action.type) {
case 'INCREMENT':
return state + 1
case 'DECREMENT':
return state - 1
default:
return state
}
}
let store = createStore(counter)
store.subscribe(() =>
console.log(store.getState())
)
store.dispatch({ type: 'INCREMENT' })
// 1
store.dispatch({ type: 'INCREMENT' })
// 2
store.dispatch({ type: 'DECREMENT' })
// 1
发现已经实现redux最基本的功能了。
总之,我觉得学习一个新的东西最好的办法就是去读一下它的源码,然后在自己了解的基础上去手动实现一个类似的东西,我觉得应该会对理解这个东西的原理很有帮助。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。