做为前端开发者,相信即便没有使用过 redux,必定也听过 redux 的大名。咱们经常和 react 搭配一块儿使用 redux,这样更能发挥 redux 的做用,由于这类库容许你经过改变 state 来更新试图。但 redux 一样能够与 Angular、jQuery 等库搭配使用,一样也能够在原生的 js 中使用。了解清楚了在原生 js 中的使用后更容易帮助开发者理解 redux。javascript
简单说,Redux 是 JavaScript 的一个状态容器,提供可预测化的状态管理。html
随着 JavaScript 单页应用开发日趋复杂,JavaScript 须要管理比任什么时候候都要多的 state (状态)。 这些 state 可能包括服务器数据、本地生成还没有持久化到服务器的数据,也包括 UI 状态,如激活的路由,被选中的标签,是否显示加载动效或者分页器等等。前端
管理不断变化的 state 很是困难。若是一个 model 的变化会引发另外一个 model 变化,那么当 view 变化时,就可能引发对应 model 以及另外一个 model 的变化,依次地,可能会引发另外一个 view 的变化。直至你搞不清楚到底发生了什么。state 在何时,因为什么缘由,如何变化已然不受控制。 当系统变得错综复杂的时候,想重现问题或者添加新功能就会变得举步维艰。java
而 Redux 要作的就是试图让 state 的变化变得可预测。react
redux 能够用这三个基本原则来描述:redux
整个应用的 state 被储存在一棵 object tree 中,而且这个 object tree 只存在于惟一一个 store 中。浏览器
惟一改变 state 的方法就是触发 action,action 是一个用于描述已发生事件的普通对象。bash
为了描述 action 如何改变 state tree,你须要编写 reducers服务器
为了可以在原生 js 中使用 Redux,咱们须要先来了解一个 Redux 的基本使用和经常使用的概念。架构
store 是 Redux 中最核心的概念,是 Redux 架构的根本。前文提到过 Redux 是一个可预测状态的 “容器”,这里所说的容器,其实就是指 store。store 是唯一的, 保存着整个页面的状态数据数,而且为开发者提供了重要的 API。
事实上,store 就是一个 JavaScript 对象,里面包含了如下方法:
import { createStore } from 'redux';
import reducer from './reducers';
// 利用 createStore 来建立
let store = createStore(reducer);
复制代码
reducer 参数为 createStore 的必传参数,也就是说,当开发者建立一个 store 的时候,必须同时定义一个 reducer,用来告知 store 数据状态应该如何根据 action 进行变动。
action 描述了状态变动的信息,也就是须要页面作出的变化。这是由开发者定义并借助于 store.dispatch 派发的,action 本质上也是一个 JavaScript 对象。咱们约定,action 对象内必需要有一个 type 属性,做为描述这个 action 的名称来惟一肯定这个 action。
const action = {
type: 'ADD_TODO',
text: 'Build my first Redux app'
}
复制代码
定义好了 action 以后,能够利用 dispatch 派发这个 action
store.dispatch(action)
复制代码
action 描述了一种变化,并携带了这种变化的数据信息,可是 action 只是描述了有事情发生这一事实,并无描述应用如何更新state。真正执行这种变化并生成正确数据状态的是 reducer 函数。
为了保证数据的可预测性,即相同的输入有相同的输出,因此 reducer 必须是一个纯函数。下面是一个最简单的 reducer 函数:
// initialState 为初始化的 state
function updateState(state = initialState, action) {
// 这里暂不处理任何 action,
// 仅返回传入的 state。
return state
}
复制代码
固然,一个完整的 reducer 函数可能须要对多个 action 进行处理,以下:
function updateState(state = initialState, action) {
switch (action.type) {
case 'case1':
return newState1;
case 'case2':
return newState2;
case 'case3':
return newState3;
default:
return state
}
}
复制代码
严格的单向数据流 是 Redux 架构的设计核心。这意味着应用中全部的数据都遵循相同的生命周期,这样可让应用变得更加可预测且容易理解。
Redux 应用中数据的生命周期遵循下面 4 个步骤:
备注: 当咱们执行 store.dispatch 派发一个 action 以后,Redux 会 “自动” 帮咱们执行处理变化并更新数据的 reducer 函数。从 store.dispatch 到 reducer 这个过程能够认为是由 Redux 内部处理的。
前面介绍了 Redux 的一些基础知识,可是可能仍是感到无从下手,有点 “镜花水月” 的感受,这是由于还须要打通 “任督二脉”,将全部的知识点进行链接,下面经过一个简单的在原生 JavaScript 中的实例来进一步加深理解。
如下是一个很是常见的场景,页面截图来自豆瓣 App,我能够对影评 “点赞” 或者 “踩”,而且记录 “点赞” 和 “踩” 的数量。以下图所示:
组件逻辑比较简单,点击 “有用” 按钮,页面展现点赞数 + 1,点击 “没用” 按钮,页面展现没用的数目 + 1。接下来咱们来看下在原生 JavaScript 中利用 Redux 架构应该如何实现:
<!DOCTYPE html>
<html lang="en">
<head>
<title>redux 在原生 JavaScript 中的使用</title>
<script src="https://unpkg.com/redux@latest/dist/redux.min.js"></script>
</head>
<body>
<div class="wrap">
<div class="like">点赞 <span>0</span></div>
<div class="hate">踩 <span>0</span></div>
</div>
</body>
</html>
复制代码
直接在浏览器打开上面的 html 文件, 而后在控制台经过全局变量 window.Redux 便可访问 Redux 对象
须要有两个状态来存储点击 “有用” 和 “无用” 的人数
// 定义初始状态
var initialState = {
likeNum: 0, // 点赞的总数
hateNum: 0 // 踩的总数
}
复制代码
分析后得知,这里只须要两个 action,一个为点击 “有用” 按钮,一个为点击 “没用” 按钮。
// 定义有用 action
var likeAction = {
type: 'like'
}
// 定义没用 action
var hateAction = {
type: 'hate'
}
复制代码
function reducer(state = initialState, action) {
switch(action.type){
case 'like':
return {
likeNum: state.likeNum + 1,
hateNum: state.hateNum
};
case 'hate':
return {
likeNum: state.likeNum,
hateNum: state.hateNum + 1
};
default:
return state;
}
}
复制代码
// 引入 createStore
var createStore = window.Redux.createStore;
// 建立 store
var store = createStore(reducer);
复制代码
// 点击有用按钮,派发 likeAction
var likeButton = document.querySelector('.like');
likeButton.addEventListener('click', function(){
store.dispatch(likeAction);
})
// 点击没用按钮,派发 hateAction
var hateButton = document.querySelector('.hate');
hateButton.addEventListener('click', function(){
store.dispatch(hateAction);
})
复制代码
// 定义更新视图的方法
var render = function() {
document.querySelector('.like span').innerText = store.getState().likeNum;
document.querySelector('.hate span').innerText = store.getState().hateNum;
}
// 监听 store 的变化,store 发生变化后调用 render 方法,利用原生 JavaScript 的方法更新视图
store.subscribe(render);
复制代码
以上就是 Redux 的一个最简单的实例了,比较清晰地阐述了 Redux 的思想,Redux 彻底能够独立于 React 而存在。
它的架构也很简单:须要开发者预先定义 action 及 reducer 函数,同时在恰当的时候派发 action,即 dispatch(action)。若是所编写的 reducer 函数没有问题,那么在正常更新状态以后,就能够经过 store.subscribe 注册每一次数据更新后的回调逻辑,这个回调逻辑每每就是对页面的渲染。在回调逻辑中,使用 store.subscribe() 获取最新数据,完成正确的页面响应,貌似一个发布订阅系统。
使用过 React 搭配 Redux 开发的读者可能对 store.subscribe() 有些陌生,由于它已经由 react-redux 库进行了封装,这也是 store 数据更新后即可以直接触发相关组件从新渲染的缘由。 后续有时间的话会讲一下 react-redux 相关的内容。