日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網(wǎng)站首頁 編程語言 正文

React開發(fā)進階redux?saga使用原理詳解_React

作者:空山與新雨 ? 更新時間: 2022-12-26 編程語言

前言

工作中使用了redux-saga這個redux中間件,如果不明白內(nèi)部原理使用起來會讓人摸不著頭腦,閱讀源碼后特意對其原理做下總結(jié)。

redux的特點

  • 一個標準、管理應(yīng)用副作用的redux中間件
  • 實現(xiàn)切面編程方式
  • 聲明式的編寫方式

訂閱發(fā)布的設(shè)計模式

優(yōu)點:

  • 把異步操作轉(zhuǎn)移到單獨 saga文件中,而不是糅雜在action或者component中;
  • dispatch的參數(shù)保持為純粹的action而不是thunk function;
  • 大量的saga輔助函數(shù)和effect創(chuàng)建器減少了開發(fā)者的開發(fā)成本;
  • 靈活的串行或并行能夠?qū)崿F(xiàn)復(fù)雜的異步流程。

分析原理

先舉個實踐的例子

import { createStore, applyMiddleware } from 'redux';
import createSagaMiddleware from 'redux-saga';
import thunk from 'redux-thunk';
import { put, takeEvery, delay, call, select } from 'redux-saga/effects';
const reducer = (state = 0, action) => {
  switch (action.type) {
    case 'put':
      return state + action.payload;
    default:
      return state;
  }
};
const sagaMiddleware = createSagaMiddleware()
export const store = createStore(reducer, applyMiddleware(sagaMiddleware, thunk));
function* main() {
  // 工具函數(shù) delay 阻塞1s
  var start = Date.now();
  yield delay(1000);
  console.log(Date.now() - start);// 1秒多
  // put 類似于 dispatch
  yield put({ type: 'put' , payload:10});
  // takeEvery 不阻塞程序
  yield takeEvery('takeEvery111', function ({ type, payload }) {
    console.log('takeEvery', type, payload); // yield put({ type: 'takeEvery111' , payload:10}); 觸發(fā)
  });
  // select 獲取state中的數(shù)據(jù)
  const state = yield select((state) => state);
  console.log(state); // 10
  // call 阻塞程序
  yield call(function* () { // 阻塞
    yield delay(1000);
  });
  console.log(Date.now() - start);// 2秒多
  yield put({ type: 'takeEvery111' , payload:10});
}
sagaMiddleware.run(main);

依次打印出如下結(jié)果:

1001
10
2004
takeEvery takeEvery111 10

1. 自動執(zhí)行Generator

從執(zhí)行結(jié)果來看,這個main函數(shù)能自動按順序執(zhí)行說明在redux-saga的程序代碼中有自動執(zhí)行g(shù)en的機制,其實源碼就是./internal/proc.js文件中,通過函數(shù)之間循環(huán)調(diào)用的方式執(zhí)行這個gen函數(shù)。

這樣的自動執(zhí)行機制在generator中是比較常見的,比如co模塊就具有這樣的功能,其實現(xiàn)巧妙卻不復(fù)雜,如下例子:

function makePromisify(source) {
    if (source.then && typeof source.then === "function") return source
    return Promise.resolve(source)
}
function run(generatorFunc) {
    let it = generatorFunc()
    let result = it.next()
    return new Promise((resolve, reject) => {
        const next = function (result) {
            if (result.done) {
                resolve(result.value)
            }
            //保證返回的是一個promise
            result.value = makePromisify(result.value)
            result.value.then(res => {
                //將promise的返回值res傳入iterator迭代器的next方法中,作為yield后面表達式的返回值
                //it.next將停止的yield繼續(xù)執(zhí)行到下一個yield,返回的result是一個value,done屬性組成的對象
                let result = it.next(res)
                //遞歸執(zhí)行next函數(shù)
                next(result)
            }).catch(err => {
                reject(err)
            })
        }
        next(result)
    })
}

2. 發(fā)布訂閱模式

我們看到takeEvery是可以攔截到{type: 'takeEvery111'}這個action,說明在redux-saga內(nèi)部有類似發(fā)布訂閱on/trigger這樣的機制,通過閱讀源碼我們發(fā)現(xiàn),內(nèi)部通過channel這個東西來做發(fā)布訂閱的處理;在./internal/channel.js就有這樣的源碼:

put(input) {
  const takers = (currentTakers = nextTakers)
  for (let i = 0, len = takers.length; i < len; i++) {
    const taker = takers[i]
    // 如果take匹配到, 執(zhí)行它
    if (taker[MATCH](input)) {
      taker.cancel()
      taker(input) // 發(fā)布
    }
  }
},
take(cb, matcher = matchers.wildcard) {
  cb[MATCH] = matcher
  ensureCanMutateNextTakers()
  nextTakers.push(cb) // 訂閱
},

3. put, takeEvery, delay, call返回effect

put執(zhí)行并不是直接dispatch一個action,而是通過yield向redux-saga內(nèi)部傳遞參數(shù)(這個參數(shù)在redux-saga中叫effect),內(nèi)部根據(jù)這個參數(shù)決定具體的執(zhí)行內(nèi)容。

在源碼中put、fork、call 等是通過 makeEffect 創(chuàng)建了一系列 effect,這個 effect 是一個普通的 js 對象,上面掛載了一些相關(guān)的信息,并且把這個effect yield到內(nèi)部的runEffejct中,然后根據(jù)type字段決定真正需要執(zhí)行的程序過程。

const makeEffect = (type, payload) => ({
  [IO]: true,
  combinator: false,
  type,
  payload,
});

總結(jié)

原文鏈接:https://www.cnblogs.com/walkermag/p/16913838.html

欄目分類
最近更新