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 相關的內容。

相關文章
相關標籤/搜索