Redux 在原生 JavaScript 中的使用,感觉最原汁原味的 Redux

前言

做为前端开发者,相信即便没有使用过 redux,必定也听过 redux 的大名。咱们经常和 react 搭配一块儿使用 redux,这样更能发挥 redux 的做用,由于这类库容许你经过改变 state 来更新试图。但 redux 一样能够与 Angular、jQuery 等库搭配使用,一样也能够在原生的 js 中使用。了解清楚了在原生 js 中的使用后更容易帮助开发者理解 redux。javascript

Redux 是什么

简单说,Redux 是 JavaScript 的一个状态容器,提供可预测化的状态管理html

随着 JavaScript 单页应用开发日趋复杂,JavaScript 须要管理比任什么时候候都要多的 state (状态)。 这些 state 可能包括服务器数据、本地生成还没有持久化到服务器的数据,也包括 UI 状态,如激活的路由,被选中的标签,是否显示加载动效或者分页器等等。前端

管理不断变化的 state 很是困难。若是一个 model 的变化会引发另外一个 model 变化,那么当 view 变化时,就可能引发对应 model 以及另外一个 model 的变化,依次地,可能会引发另外一个 view 的变化。直至你搞不清楚到底发生了什么。state 在何时,因为什么缘由,如何变化已然不受控制。 当系统变得错综复杂的时候,想重现问题或者添加新功能就会变得举步维艰。java

Redux 要作的就是试图让 state 的变化变得可预测react

Redux 的三大原则

redux 能够用这三个基本原则来描述:redux

1. 单一数据源

整个应用的 state 被储存在一棵 object tree 中,而且这个 object tree 只存在于惟一一个 store 中。浏览器

2. State 是只读的

惟一改变 state 的方法就是触发 action,action 是一个用于描述已发生事件的普通对象。bash

3. 使用纯函数来执行修改

为了描述 action 如何改变 state tree,你须要编写 reducers服务器

Redux 的基本使用

为了可以在原生 js 中使用 Redux,咱们须要先来了解一个 Redux 的基本使用和经常使用的概念。架构

初识 store

store 是 Redux 中最核心的概念,是 Redux 架构的根本。前文提到过 Redux 是一个可预测状态的 “容器”,这里所说的容器,其实就是指 store。store 是唯一的, 保存着整个页面的状态数据数,而且为开发者提供了重要的 API。

1. store 提供的方法

事实上,store 就是一个 JavaScript 对象,里面包含了如下方法:

  1. getState():获取当前页面状态数据数,即 store 中的 state
  2. dispatch(action): 派发 action,更新 state
  3. subscribe(listener):注册监听器,订阅页面数据状态,即 store 中 state 的变化

2. 如何建立一个 store

import { createStore } from 'redux';
import reducer from './reducers';
// 利用 createStore 来建立
let store = createStore(reducer);
复制代码

reducer 参数为 createStore 的必传参数,也就是说,当开发者建立一个 store 的时候,必须同时定义一个 reducer,用来告知 store 数据状态应该如何根据 action 进行变动。

构造 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)
复制代码

编写 reducer 函数更新数据

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 架构的设计核心。这意味着应用中全部的数据都遵循相同的生命周期,这样可让应用变得更加可预测且容易理解。

Redux 应用中数据的生命周期遵循下面 4 个步骤:

  1. 经过 Redux 的 createStore 方法建立了一个全局惟一的 store
  2. 调用 store.dispatch(action) 派发一个用来描述变化的 action
  3. Redux store 调用传入的 reducer 函数
  4. 若是有多个 reducer 函数,根 reducer 应该把多个子 reducer 输出合并成一个单一的 state 树
  5. Redux store 保存了根 reducer 返回的完整 state 树,这棵树就是最新的 state。
  6. 当 store 发生变化后,全部订阅 store.subscribe 的监听器都将被调用,监听器里能够调用 store.getState() 获取当前 state,能够应用最新的state 来更新 UI。

备注: 当咱们执行 store.dispatch 派发一个 action 以后,Redux 会 “自动” 帮咱们执行处理变化并更新数据的 reducer 函数。从 store.dispatch 到 reducer 这个过程能够认为是由 Redux 内部处理的。

Redux 在原生 JavaScript 中的开发基础实例

前面介绍了 Redux 的一些基础知识,可是可能仍是感到无从下手,有点 “镜花水月” 的感受,这是由于还须要打通 “任督二脉”,将全部的知识点进行链接,下面经过一个简单的在原生 JavaScript 中的实例来进一步加深理解。

如下是一个很是常见的场景,页面截图来自豆瓣 App,我能够对影评 “点赞” 或者 “踩”,而且记录 “点赞” 和 “踩” 的数量。以下图所示:

组件逻辑比较简单,点击 “有用” 按钮,页面展现点赞数 + 1,点击 “没用” 按钮,页面展现没用的数目 + 1。接下来咱们来看下在原生 JavaScript 中利用 Redux 架构应该如何实现:

1. 引入 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 对象

2. 定义初始状态

须要有两个状态来存储点击 “有用” 和 “无用” 的人数

// 定义初始状态
var initialState = {
    likeNum: 0,  // 点赞的总数
    hateNum: 0   // 踩的总数
}
复制代码

3. 建立 action

分析后得知,这里只须要两个 action,一个为点击 “有用” 按钮,一个为点击 “没用” 按钮。

// 定义有用 action
var likeAction = {
    type: 'like'
}

// 定义没用 action 
var hateAction = {
    type: 'hate'
}
复制代码

4. 编写 reducer 函数

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;
    }
}
复制代码

建立惟一的 store

// 引入 createStore
var createStore = window.Redux.createStore;
// 建立 store
var store = createStore(reducer);
复制代码

点击按钮的时候派发 action

// 点击有用按钮,派发 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 相关的内容。

相关文章
相关标签/搜索