Dieser Artikel stellt hauptsächlich die erste Einführung und Verwendung von Redux-Saga vor. Jetzt teile ich ihn mit Ihnen und gebe Ihnen eine Referenz.
redux-saga ist eine Middleware, die asynchrone Vorgänge von Redux-Anwendungen verwaltet. Ihre Funktion ähnelt redux-thunk + async/await. Sie speichert die gesamte asynchrone Betriebslogik an einem Ort für die zentrale Verarbeitung durch die Erstellung von Sagas.
Die Effekte von redux-saga
Die Effekte in redux-saga sind ein Nur-Text-JavaScript-Objekt, das einige Funktionen enthält, die ausgeführt werden durch die Saga-Middleware-Anweisung. Die von diesen Anweisungen ausgeführten Vorgänge umfassen die folgenden drei Typen:
Initiieren eines asynchronen Aufrufs (z. B. Senden einer Ajax-Anfrage)
Initiieren anderer Aktion zum Aktualisieren des Stores
Andere Sagas aufrufen
Effects enthält viele Anweisungen, die in der asynchronen API-Referenz zu finden sind
Features von redux-saga
Komfort zum Testen, zum Beispiel:
assert.deepEqual(iterator.next().value, call(Api.fetch, '/products'))
Aktion kann ihre Reinheit bewahren, und asynchrone Vorgänge werden in Saga zur Verarbeitung konzentriert
Beobachtung/Arbeiter (Überwachung->Ausführung) Arbeitsform
ist als Generator implementiert
hat gute Unterstützung für Anwendungsszenarien mit komplexer asynchroner Logik
Mehr Implementieren Sie asynchrone Logik auf feinkörnige Weise, um den Prozess klarer zu gestalten und die Verfolgung und Behebung von Fehlern zu erleichtern.
Schreiben Sie asynchrone Logik auf synchrone Weise, die eher der menschlichen Denklogik entspricht
Von Redux-Thunk zu Redux-Saga
Angenommen, es gibt jetzt ein Szenario: Wenn sich der Benutzer anmeldet, muss er überprüfen, ob der Benutzername und das Passwort des Benutzers den Anforderungen entsprechen.
Verwenden Sie Redux-Thunk, um
Die Logik zum Abrufen von Benutzerdaten (user.js) zu implementieren:
// user.js import request from 'axios'; // define constants // define initial state // export default reducer export const loadUserData = (uid) => async (dispatch) => { try { dispatch({ type: USERDATA_REQUEST }); let { data } = await request.get(`/users/${uid}`); dispatch({ type: USERDATA_SUCCESS, data }); } catch(error) { dispatch({ type: USERDATA_ERROR, error }); } }
Login-Verifizierungslogik (login.js):
import request from 'axios'; import { loadUserData } from './user'; export const login = (user, pass) => async (dispatch) => { try { dispatch({ type: LOGIN_REQUEST }); let { data } = await request.post('/login', { user, pass }); await dispatch(loadUserData(data.uid)); dispatch({ type: LOGIN_SUCCESS, data }); } catch(error) { dispatch({ type: LOGIN_ERROR, error }); } }
redux-saga
Alle asynchrone Logik kann in saga.js geschrieben werden:
export function* loginSaga() { while(true) { const { user, pass } = yield take(LOGIN_REQUEST) //等待 Store 上指定的 action LOGIN_REQUEST try { let { data } = yield call(loginRequest, { user, pass }); //阻塞,请求后台数据 yield fork(loadUserData, data.uid); //非阻塞执行loadUserData yield put({ type: LOGIN_SUCCESS, data }); //发起一个action,类似于dispatch } catch(error) { yield put({ type: LOGIN_ERROR, error }); } } } export function* loadUserData(uid) { try { yield put({ type: USERDATA_REQUEST }); let { data } = yield call(userRequest, `/users/${uid}`); yield put({ type: USERDATA_SUCCESS, data }); } catch(error) { yield put({ type: USERDATA_ERROR, error }); } }
Schwierige Interpretation
Für die Redux-Saga gibt es immer noch viele Dinge, die schwer zu verstehen und unklar sind. Der Autor organisiert unten die Konzepte, die ich verwirrender finde:
Verwendung von take
Sowohl take als auch takeEvery überwachen eine Aktion, aber ihre Funktionen sind inkonsistent, takeEvery reagiert jedes Mal, wenn die Aktion ausgelöst wird, während take nur reagiert, wenn der Ausführungsfluss die take-Antwort erreicht. takeEvery hört nur auf die Aktion und führt die entsprechende Verarbeitungsfunktion aus. Es hat nicht viel Kontrolle darüber, wann die Aktion ausgeführt wird und wie auf die Aktion reagiert wird. Sie können nicht steuern, wann sie aufgerufen werden Zuhören. Es kann nur immer wieder aufgerufen werden, wenn eine Aktion abgeglichen wird. Take kann jedoch entscheiden, wann auf eine Aktion und die nachfolgenden Vorgänge nach der Antwort in der Generatorfunktion reagiert werden soll.
Um beispielsweise den Logger-Vorgang bei der Überwachung aller Arten von Aktionsauslösern durchzuführen, verwenden Sie takeEvery, um Folgendes zu implementieren:
import { takeEvery } from 'redux-saga' function* watchAndLog(getState) { yield* takeEvery('*', function* logger(action) { //do some logger operation //在回调函数体内 }) }
Verwenden Sie take, um Folgendes zu implementieren :
import { take } from 'redux-saga/effects' function* watchAndLog(getState) { while(true) { const action = yield take('*') //do some logger operation //与 take 并行 }) }
Wobei (true) bedeutet, dass, sobald der letzte Schritt des Prozesses (Logger) erreicht ist, eine neue Iteration (Logger-Prozess) durch Warten gestartet wird für eine neue willkürliche Aktion.
Blockierend und nicht blockierend
Der Aufrufvorgang wird zum Initiieren asynchroner Vorgänge verwendet. Bei Generatoren ist der Aufruf ein blockierender Vorgang und kann erst ausgeführt werden, wenn der Generatoraufruf ausgeführt wird abgeschlossen. Führen oder bearbeiten Sie eine andere Angelegenheit. , aber Fork ist eine nicht blockierende Operation. Wenn Fork eine Aufgabe mobilisiert, wird die Aufgabe im Hintergrund ausgeführt. Zu diesem Zeitpunkt kann der Ausführungsfluss weiter ausgeführt werden, ohne auf die Rückgabe des Ergebnisses zu warten.
Zum Beispiel das folgende Anmeldeszenario:
function* loginFlow() { while(true) { const {user, password} = yield take('LOGIN_REQUEST') const token = yield call(authorize, user, password) if(token) { yield call(Api.storeItem({token})) yield take('LOGOUT') yield call(Api.clearItem('token')) } } }
Wenn das Ergebnis beim Aufruf zur Autorisierungsanforderung nicht zurückgegeben wird, der Benutzer es jedoch auslöst Zu diesem Zeitpunkt wird erneut die Aktion „LOGOUT“ ausgeführt. Das Abmelden zu diesem Zeitpunkt wird ignoriert und nicht verarbeitet, da loginFlow in der Autorisierung blockiert ist und nicht ausgeführt wird. take('LOGOUT')
Führen Sie mehrere gleichzeitig aus Zeitaufgabe
Wenn Sie auf ein Szenario stoßen, in dem Sie mehrere Aufgaben gleichzeitig ausführen müssen, z. B. das Anfordern von Benutzerdaten und Produktdaten, sollten Sie die folgende Methode verwenden:
import { call } from 'redux-saga/effects' //同步执行 const [users, products] = yield [ call(fetch, '/users'), call(fetch, '/products') ] //而不是 //顺序执行 const users = yield call(fetch, '/users'), products = yield call(fetch, '/products')
Wenn auf yield ein Array folgt, werden die Operationen im Array gemäß den Ausführungsregeln von Promise.all ausgeführt und der Generator blockiert, bis alle Effekte ausgeführt sind
Interpretation des Quellcodes
In jedem Projekt, das Redux-Saga verwendet, verfügt die Hauptdatei über die folgende Logik zum Hinzufügen von Sagas-Middleware zum Store:
const sagaMiddleware = createSagaMiddleware({sagaMonitor}) const store = createStore( reducer, applyMiddleware(sagaMiddleware) ) sagaMiddleware.run(rootSaga)
Wobei createSagaMiddleware die in der Redux-Saga-Core-Quellcodedatei src/middleware.js exportierte Methode ist:
export default function sagaMiddlewareFactory({ context = {}, ...options } = {}) { ... function sagaMiddleware({ getState, dispatch }) { const channel = stdChannel() channel.put = (options.emitter || identity)(channel.put) sagaMiddleware.run = runSaga.bind(null, { context, channel, dispatch, getState, sagaMonitor, logger, onError, effectMiddlewares, }) return next => action => { if (sagaMonitor && sagaMonitor.actionDispatched) { sagaMonitor.actionDispatched(action) } const result = next(action) // hit reducers channel.put(action) return result } } ... }
这段逻辑主要是执行了 sagaMiddleware(),该函数里面将 runSaga 赋值给 sagaMiddleware.run 并执行,最后返回 middleware。 接着看 runSaga() 的逻辑:
export function runSaga(options, saga, ...args) { ... const task = proc( iterator, channel, wrapSagaDispatch(dispatch), getState, context, { sagaMonitor, logger, onError, middleware }, effectId, saga.name, ) if (sagaMonitor) { sagaMonitor.effectResolved(effectId, task) } return task }
这个函数里定义了返回了一个 task 对象,该 task 是由 proc 产生的,移步 proc.js:
export default function proc( iterator, stdChannel, dispatch = noop, getState = noop, parentContext = {}, options = {}, parentEffectId = 0, name = 'anonymous', cont, ) { ... const task = newTask(parentEffectId, name, iterator, cont) const mainTask = { name, cancel: cancelMain, isRunning: true } const taskQueue = forkQueue(name, mainTask, end) ... next() return task function next(arg, isErr){ ... if (!result.done) { digestEffect(result.value, parentEffectId, '', next) } ... } }
其中 digestEffect 就执行了 effectTriggerd() 和 runEffect(),也就是执行 effect,其中 runEffect() 中定义了不同 effect 执行相对应的函数,每一个 effect 函数都在 proc.js 实现了。
除了一些核心方法之外,redux-saga 还提供了一系列的 helper 文件,这些文件的作用是返回一个类 iterator 的对象,便于后续的遍历和执行, 在此不具体分析。
上面是我整理给大家的,希望今后会对大家有帮助。
相关文章:
Das obige ist der detaillierte Inhalt vonWie verwende ich Redux-Saga und welche Methoden und Techniken gibt es für die Verwendung von Redux-Saga?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!