日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Redux 莞式教程 之 简明篇

發布時間:2025/4/5 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Redux 莞式教程 之 简明篇 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Redux 簡明教程

原文鏈接(保持更新):https://github.com/kenberkele...

寫在前面

本教程深入淺出,配套 簡明教程、進階教程(源碼精讀)以及文檔注釋豐滿的 Demo 等一條龍服務

§ 為什么要用 Redux

當然還有 Flux、Reflux、Mobx 等狀態管理庫可供選擇

拋開需求講實用性都是耍流氓,因此下面由我扮演您那可親可愛的產品經理

⊙ 需求 1:在控制臺上記錄用戶的每個動作

不知道您是否有后端的開發經驗,后端一般會有記錄訪問日志的中間件
例如,在 Express 中實現一個簡單的 Logger 如下:

var loggerMiddleware = function(req, res, next) {console.log('[Logger]', req.method, req.originalUrl)next() } ... app.use(loggerMiddleware)

每次訪問的時候,都會在控制臺中留下類似下面的日志便于追蹤調試:

[Logger] GET / [Logger] POST /login [Logger] GET /user?uid=10086 ...

如果我們把場景轉移到前端,請問該如何實現用戶的動作跟蹤記錄?
我們可能會這樣寫:

/** jQuery **/ $('#loginBtn').on('click', function(e) {console.log('[Logger] 用戶登錄')... }) $('#logoutBtn').on('click', function() {console.log('[Logger] 用戶退出登錄')... })/** MVC / MVVM 框架(這里以純 Vue 舉例) **/ methods: {handleLogin () {console.log('[Logger] 用戶登錄')...},handleLogout () {console.log('[Logger] 用戶退出登錄')...} }

上述 jQuery 與 MV* 的寫法并沒有本質上的區別
記錄用戶行為代碼的侵入性極強,可維護性與擴展性堪憂

⊙ 需求 2:在上述需求的基礎上,記錄用戶的操作時間

哼!最討厭就是改需求了,這種簡單的需求難道不是應該一開始就想好的嗎?
呵呵,如果每位產品經理都能一開始就把需求完善好,我們就不用加班了好伐

顯然地,前端的童鞋又得一個一個去改(當然 編輯器 / IDE 都支持全局替換):

/** jQuery **/ $('#loginBtn').on('click', function(e) {console.log('[Logger] 用戶登錄', new Date())... }) $('#logoutBtn').on('click', function() {console.log('[Logger] 用戶退出登錄', new Date())... })/** MVC / MVVM 框架(這里以 Vue 舉例) **/ methods: {handleLogin () {console.log('[Logger] 用戶登錄', new Date())...},handleLogout () {console.log('[Logger] 用戶退出登錄', new Date())...} }

而后端的童鞋只需要稍微修改一下原來的中間件即可:

var loggerMiddleware = function(req, res, next) {console.log('[Logger]', new Date(), req.method, req.originalUrl)next() } ... app.use(loggerMiddleware)

⊙ 需求 3:正式上線的時候,把控制臺中有關 Logger 的輸出全部去掉

難道您以為有了 UglifyJS,配置一個 drop_console: true 就好了嗎?圖樣圖森破,拿衣服!
請看清楚了,僅僅是去掉有關 Logger 的 console.log,其他的要保留哦親~~~
于是前端的童鞋又不得不乖乖地一個一個注釋掉(當然也可以設置一個環境變量判斷是否輸出,甚至可以重寫 console.log)

而我們后端的童鞋呢?只需要注釋掉一行代碼即可:// app.use(loggerMiddleware),真可謂是不費吹灰之力

⊙ 需求 4:正式上線后,自動收集 bug,并還原出當時的場景

收集用戶報錯還是比較簡單的,利用 window.error 事件,然后根據 Source Map 定位到源碼(但一般查不出什么)

但要完全還原出當時的使用場景,幾乎是不可能的。因為您不知道這個報錯,用戶是怎么一步一步操作得來的
就算知道用戶是如何操作得來的,但在您的電腦上,測試永遠都是通過的(不是我寫的程序有問題,是用戶用的方式有問題)

相對地,后端的報錯的收集、定位以及還原卻是相當簡單。只要一個 API 有 bug,那無論用什么設備訪問,都會得到這個 bug
還原 bug 也是相當簡單:把數據庫備份導入到另一臺機器,部署同樣的運行環境與代碼。如無意外,bug 肯定可以完美重現

在這個問題上拿后端跟前端對比,確實有失公允。但為了鼓吹 Redux 的優越,只能勉為其難了

實際上 jQuery / MV* 中也能實現用戶動作的跟蹤,用一個數組往里面 push 用戶動作即可
但這樣操作的意義不大,因為僅僅只有動作,無法反映動作前后,應用狀態的變動情況

※ 小結

為何前后端對于這類需求的處理竟然大相徑庭?后端為何可以如此優雅?
原因在于,后端具有統一的入口統一的狀態管理(數據庫),因此可以引入中間件機制統一實現某些功能

多年來,前端工程師忍辱負重,操著賣白粉的心,賺著買白菜的錢,一直處于程序員鄙視鏈的底層
于是有大牛就把后端 MVC 的開發思維搬到前端,將應用中所有的動作與狀態都統一管理,讓一切有據可循

使用 Redux,借助 Redux DevTools 可以實現出“華麗如時光旅行一般的調試效果”
實際上就是開發調試過程中可以撤銷與重做,并且支持應用狀態的導入和導出(就像是數據庫的備份)
而且,由于可以使用日志完整記錄下每個動作,因此做到像 Git 般,隨時隨地恢復到之前的狀態

由于可以導出和導入應用的狀態(包括路由狀態),因此還可以實現前后端同構(服務端渲染)
當然,既然有了動作日志以及動作前后的狀態備份,那么還原用戶報錯場景還會是一個難題嗎?

§ Store

首先要區分 store 和 state

state 是應用的狀態,一般本質上是一個普通對象
例如,我們有一個 Web APP,包含 計數器 和 待辦事項 兩大功能
那么我們可以為該應用設計出對應的存儲數據結構(應用初始狀態):

/** 應用初始 state,本代碼塊記為 code-1 **/ {counter: 0,todos: [] }

store 是應用狀態 state 的管理者,包含下列四個函數:

  • getState() # 獲取整個 state

  • dispatch(action) # ※ 觸發 state 改變的【唯一途徑】※

  • subscribe(listener) # 您可以理解成是 DOM 中的 addEventListener

  • replaceReducer(nextReducer) # 一般在 Webpack Code-Splitting 按需加載的時候用

二者的關系是:state = store.getState()

Redux 規定,一個應用只應有一個單一的 store,其管理著唯一的應用狀態 state
Redux 還規定,不能直接修改應用的狀態 state,也就是說,下面的行為是不允許的:

var state = store.getState() state.counter = state.counter + 1 // 禁止在業務邏輯中直接修改 state

若要改變 state,必須 dispatch 一個 action,這是修改應用狀態的不二法門

現在您只需要記住 action 只是一個包含 type 屬性的普通對象即可
例如 { type: 'INCREMENT' }

上面提到,state 是通過 store.getState() 獲取,那么 store 又是怎么來的呢?
想生成一個 store,我們需要調用 Redux 的 createStore:

import { createStore } from 'redux' ... const store = createStore(reducer, initialState) // store 是靠傳入 reducer 生成的哦!

現在您只需要記住 reducer 是一個 函數,負責更新并返回一個新的 state
而 initialState 主要用于前后端同構的數據同步(詳情請關注 React 服務端渲染)

§ Action

上面提到,action(動作)實質上是包含 type 屬性的普通對象,這個 type 是我們實現用戶行為追蹤的關鍵
例如,增加一個待辦事項 的 action 可能是像下面一樣:

/** 本代碼塊記為 code-2 **/ {type: 'ADD_TODO',payload: {id: 1,content: '待辦事項1',completed: false} }

當然,action 的形式是多種多樣的,唯一的約束僅僅就是包含一個 type 屬性罷了
也就是說,下面這些 action 都是合法的:

/** 如下都是合法的,但就是不夠規范 **/ {type: 'ADD_TODO',id: 1,content: '待辦事項1',completed: false }{type: 'ADD_TODO',abcdefg: {id: 1,content: '待辦事項1',completed: false} }

雖說沒有約束,但最好還是遵循規范

如果需要新增一個代辦事項,實際上就是將 code-2 中的 payload “寫入” 到 state.todos 數組中(如何“寫入”?在此留個懸念):

/** 本代碼塊記為 code-3 **/ {counter: 0,todos: [{id: 1,content: '待辦事項1',completed: false}] }

刨根問底,action 是誰生成的呢?

⊙ Action Creator

Action Creator 可以是同步的,也可以是異步的

顧名思義,Action Creator 是 action 的創造者,本質上就是一個函數,返回值是一個 action(對象
例如下面就是一個 “新增一個待辦事項” 的 Action Creator:

/** 本代碼塊記為 code-4 **/ var id = 1 function addTodo(content) {return {type: 'ADD_TODO',payload: {id: id++,content: content, // 待辦事項內容completed: false // 是否完成的標識}} }

將該函數應用到一個表單(假設 store 為全局變量,并引入了 jQuery ):

<--! 本代碼塊記為 code-5 --> <input type="text" id="todoInput" /> <button id="btn">提交</button><script> $('#btn').on('click', function() {var content = $('#todoInput').val() // 獲取輸入框的值var action = addTodo(content) // 執行 Action Creator 獲得 actionstore.dispatch(action) // 改變 state 的不二法門:dispatch 一個 action!!! }) </script>

在輸入框中輸入 “待辦事項2” 后,點擊一下提交按鈕,我們的 state 就變成了:

/** 本代碼塊記為 code-6 **/ {counter: 0,todos: [{id: 1,content: '待辦事項1',completed: false}, {id: 2,content: '待辦事項2',completed: false}] }

通俗點講,Action Creator 用于綁定到用戶的操作(點擊按鈕等),其返回值 action 用于之后的 dispatch(action)

剛剛提到過,action 明明就沒有強制的規范,為什么 store.dispatch(action) 之后,
Redux 會明確知道是提取 action.payload,并且是對應寫入到 state.todos 數組中?
又是誰負責“寫入”的呢?懸念即將揭曉...

§ Reducer

Reducer 必須是同步的純函數

用戶每次 dispatch(action) 后,都會觸發 reducer 的執行
reducer 的實質是一個函數,根據 action.type 來更新 state 并返回 nextState
最后會用 reducer 的返回值 nextState 完全替換掉原來的 state

注意:上面的這個 “更新” 并不是指 reducer 可以直接對 state 進行修改
Redux 規定,須先復制一份 state,在副本 nextState 上進行修改操作
例如,可以使用 lodash 的 deepClone,也可以使用 Object.assign / map / filter/ ... 等返回副本的函數

在上面 Action Creator 中提到的 待辦事項的 reducer 大概是長這個樣子 (為了容易理解,在此不使用 ES6 / Immutable.js):

/** 本代碼塊記為 code-7 **/ var initState = {counter: 0,todos: [] }function reducer(state, action) {// ※ 應用的初始狀態是在第一次執行 reducer 時設置的(除非是服務端渲染) ※if (!state) state = initStateswitch (action.type) {case 'ADD_TODO':var nextState = _.deepClone(state) // 用到了 lodash 的深克隆nextState.todos.push(action.payload) return nextStatedefault:// 由于 nextState 會把原 state 整個替換掉// 若無修改,必須返回原 state(否則就是 undefined)return state} }

通俗點講,就是 reducer 返回啥,state 就被替換成啥

§ 總結

  • store 由 Redux 的 createStore(reducer) 生成

  • state 通過 store.getState() 獲取,本質上一般是一個存儲著整個應用狀態的對象

  • action 本質上是一個包含 type 屬性的普通對象,由 Action Creator (函數) 產生

  • 改變 state 必須 dispatch 一個 action

  • reducer 本質上是根據 action.type 來更新 state 并返回 nextState 的函數

  • reducer 必須返回值,否則 nextState 即為 undefined

  • 實際上,state 就是所有 reducer 返回值的匯總(本教程只有一個 reducer,主要是應用場景比較簡單)

Action Creator => action => store.dispatch(action) => reducer(state, action) => 原 state state = nextState

⊙ Redux 與傳統后端 MVC 的對照

Redux傳統后端 MVC
store數據庫實例
state數據庫中存儲的數據
dispatch(action)用戶發起請求
action: { type, payload } type 表示請求的 URL,payload 表示請求的數據
reducer路由 + 控制器(handler)
reducer 中的 switch-case 分支路由,根據 action.type 路由到對應的控制器
reducer 內部對 state 的處理控制器對數據庫進行增刪改操作
reducer 返回 nextState 將修改后的記錄寫回數據庫

§ 最簡單的例子 ( 在線演示 )

<!DOCTYPE html> <html> <head><script src="//cdn.bootcss.com/redux/3.5.2/redux.min.js"></script> </head> <body> <script> /** Action Creators */ function inc() {return { type: 'INCREMENT' }; } function dec() {return { type: 'DECREMENT' }; }function reducer(state, action) {// 首次調用本函數時設置初始 statestate = state || { counter: 0 };switch (action.type) {case 'INCREMENT':return { counter: state.counter + 1 };case 'DECREMENT':return { counter: state.counter - 1 };default:return state; // 無論如何都返回一個 state} }var store = Redux.createStore(reducer);console.log( store.getState() ); // { counter: 0 }store.dispatch(inc()); console.log( store.getState() ); // { counter: 1 }store.dispatch(inc()); console.log( store.getState() ); // { counter: 2 }store.dispatch(dec()); console.log( store.getState() ); // { counter: 1 } </script> </body> </html>

由上可知,Redux 并不一定要搭配 React 使用。Redux 純粹只是一個狀態管理庫,幾乎可以搭配任何框架使用
(上述例子連 jQuery 都沒用哦親)

§ 下一章:Redux 進階教程

總結

以上是生活随笔為你收集整理的Redux 莞式教程 之 简明篇的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。