Redux與它的中間件:redux-thunk,redux-actions,redux-promise,redux-sage

来源:https://www.cnblogs.com/vvjiang/archive/2018/08/20/9505646.html
-Advertisement-
Play Games

序言 這裡要講的就是一個Redux在React中的應用問題,講一講Redux,react redux,redux thunk,redux actions,redux promise,redux sage這些包的作用和他們解決的問題。 因為不想把篇幅拉得太長,所以沒有太多源碼分析和語法講解,能怎麼簡單 ...


序言

這裡要講的就是一個Redux在React中的應用問題,講一講Redux,react-redux,redux-thunk,redux-actions,redux-promise,redux-sage這些包的作用和他們解決的問題。
因為不想把篇幅拉得太長,所以沒有太多源碼分析和語法講解,能怎麼簡單就怎麼簡單。

Redux

先看看百度百科上面Redux的一張圖:

這是Redux在Github上的介紹:Redux用於js程式,是一個可預測的狀態容器。

在這裡我們首先要明白的是什麼叫可預測?什麼叫狀態容器?

什麼叫狀態?實際上就是變數,對話框顯示或隱藏的變數,一杯奶茶多少錢的變數。

那麼這個狀態容器,實際上就是一個存放這些變數的變數。

你創建了一個全局變數叫Store,然後將代碼中控制各個狀態的變數存放在裡面,那麼現在Store就叫做狀態容器。

什麼叫可預測?

你在操作這個Store的時候,總是用Store.price的方式來設置值,這種操作數據的方式很原始,對於複雜的系統而言永遠都不知道程式在運行的過程中發生了什麼。

那麼現在我們都通過發送一個Action去做修改,而Store在接收到Action後會使用Reducer對Action傳遞的數據做處理,最後應用到Store中。

相對於Store.price的方式來修改者,這種方式無疑更麻煩,但是這種方式的好處就是,每一個Action裡面都可以寫日誌,可以記錄各種狀態的變動,這就是可預測。

所以如果你的程式很簡單,你完全沒有必要去用Redux。

看看Redux的示例代碼:

actionTypes.js:

export const CHANGE_BTN_TEXT = 'CHANGE_BTN_TEXT';

actions.js:

import * as T from './actionTypes';

export const changeBtnText = (text) => {
  return {
    type: T.CHANGE_BTN_TEXT,
    payload: text
  };
};

reducers.js:

import * as T from './actionTypes';

const initialState = {
  btnText: '我是按鈕',
};

const pageMainReducer = (state = initialState, action) => {
  switch (action.type) {
    case T.CHANGE_BTN_TEXT:
      return {
        ...state,
        btnText: action.payload
      };
    default:
      return state;
  }
};

export default pageMainReducer;

index.js

import { createStore } from 'redux';
import reducer from './reducers';
import { changeBtnText } from './actions';

const store = createStore(reducer);
// 開始監聽,每次state更新,那麼就會列印出當前狀態
const unsubscribe = store.subscribe(() => {
  console.info(store.getState());
});
// 發送消息
store.dispatch(changeBtnText('點擊了按鈕'));
// 停止監聽state的更新
unsubscribe();

這裡就不解釋什麼語法作用了,網上這樣的資料太多了。

Redux與React的結合:react-redux

Redux是一個可預測的狀態容器,跟React這種構建UI的庫是兩個相互獨立的東西。

Redux要應用到React中,很明顯action,reducer,dispatch這幾個階段並不需要改變,唯一需要考慮的是redux中的狀態需要如何傳遞給react組件。

很簡單,只需要每次要更新數據時運用store.getState獲取到當前狀態,並將這些數據傳遞給組件即可。

那麼問題來了,如何讓每個組件都獲取到store呢?

當然是將store作為一個值傳遞給根組件,然後store就會一級一級往下傳,使得每個組件都能獲取到store的值。

但是這樣太繁瑣了,難道每個組件需要寫一個傳遞store的邏輯?為瞭解決這個問題,那麼得用到React的context玩法,通過在根組件上將store放在根組件的context中,然後在子組件中通過context獲取到store。

react-redux的主要思路也是如此,通過嵌套組件Provider將store放到context中,通過connect這個高階組件,來隱藏取store的操作,這樣我們就不需要每次去操作context寫一大堆代碼那麼麻煩了。

然後我們再來基於之前的Redux示例代碼給出react-redux的使用演示代碼,其中action和reduce部分不變,先增加一個組件PageMain:

const PageMain = (props) => {
  return (
    <div>
      <button onClick={() => {
        props.changeText('按鈕被點擊了');
      }}
      >
        {props.btnText}
      </button>
    </div>
  );
};
// 映射store.getState()的數據到PageMain
const mapStateToProps = (state) => {
  return {
    btnText: state.pageMain.btnText,
  };
};
// 映射使用了store.dispatch的函數到PageMain
const mapDispatchToProps = (dispatch) => {
  return {
    changeText: (text) => {
      dispatch(changeBtnText(text));
    }
  };
};

// 這個地方也可以簡寫,react-redux會自動做處理
const mapDispatchToProps = {
  changeText: changeBtnText
};

export default connect(mapStateToProps, mapDispatchToProps)(PageMain);

註意上面的state.pageMain.btnText,這個pageMain是我用redux的combineReducers將多個reducer合併後給的原先的reducer一個命名。

它的代碼如下:

import { combineReducers } from 'redux';
import pageMain from './components/pageMain/reducers';

const reducer = combineReducers({
  pageMain
});

export default reducer;

然後修改index.js:

import React from 'react';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import ReactDOM from 'react-dom';
import reducer from './reducers';
import PageMain from './components/pageMain';

const store = createStore(reducer);

const App = () => (
  <Provider store={store}>
    <PageMain />
  </Provider>
);

ReactDOM.render(<App />, document.getElementById('app'));

Redux的中間件

之前我們講到Redux是個可預測的狀態容器,這個可預測在於對數據的每一次修改都可以進行相應的處理和記錄。

假如現在我們需要在每次修改數據時,記錄修改的內容,我們可以在每一個dispatch前面加上一個console.info記錄修改的內容。

但是這樣太繁瑣了,所以我們可以直接修改store.dispatch:

let next = store.dispatch
store.dispatch = (action)=> {
  console.info('修改內容為:', action)
  next(action)
}

Redux中也有同樣的功能,那就是applyMiddleware。直譯過來就是“應用中間件”,它的作用就是改造dispatch函數,跟上面的玩法基本雷同。

來一段演示代碼:

import { createStore, applyMiddleware } from 'redux';
import reducer from './reducers';

const store = createStore(reducer, applyMiddleware(curStore => next => action => {
  console.info(curStore.getState(), action);
  return next(action);
}));

看起來挺奇怪的玩法,但是理解起來並不難。通過這種返回函數的方法,使得applyMiddleware內部以及我們使用時可以處理store和action,並且這裡next的應用就是為了使用多個中間件而存在的。

而通常我們沒有必要自己寫中間件,比如日誌的記錄就已經有了成熟的中間件:redux-logger,這裡給一個簡單的例子:

import { applyMiddleware, createStore } from 'redux';
import createLogger from 'redux-logger';
import reducer from './reducers';

const logger = createLogger();

const store = createStore(
  reducer,
  applyMiddleware(logger)
);

這樣就可以記錄所有action及其發送前後的state的日誌,我們可以瞭解到代碼實際運行時到底發生了什麼。

redux-thunk:處理非同步action

在上面的代碼中,我們點擊按鈕後,直接修改了按鈕的文本,這個文本是個固定的值。

actions.js:

import * as T from './actionTypes';

export const changeBtnText = (text) => {
  return {
    type: T.CHANGE_BTN_TEXT,
    payload: text
  };
};

但是在我們實際生產的過程中,很多情況都是需要去請求服務端拿到數據再修改的,這個過程是一個非同步的過程。又或者需要setTimeout去做一些事情。

我們可以去修改這一部分如下:

const mapDispatchToProps = (dispatch) => {
  return {
    changeText: (text) => {
      dispatch(changeBtnText('正在載入中'));
      axios.get('http://test.com').then(() => {
        dispatch(changeBtnText('載入完畢'));
      }).catch(() => {
        dispatch(changeBtnText('載入有誤'));
      });
    }
  };
};

實際上,我們每天不知道要處理多少這樣的代碼。

但是問題來了,非同步操作相比同步操作多了一個很多確定因素,比如我們展示正在載入中時,可能要先要做非同步操作A,而請求後臺的過程卻非常快,導致載入完畢先出現,而這時候操作A才做完,然後再展示載入中。

所以上面的這個玩法並不能滿足這種情況。

這個時候我們需要去通過store.getState獲取當前狀態,從而判斷到底是展示正在載入中還是展示載入完畢。

這個過程就不能放在mapDispatchToProps中了,而需要放在中間件中,因為中間件中可以拿到store。

首先創造store的時候需要應用react-thunk,也就是

import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import reducer from './reducers';

const store = createStore(
  reducer,
  applyMiddleware(thunk)
);

它的源碼超級簡單:

function createThunkMiddleware(extraArgument) {
  return ({ dispatch, getState }) => next => action => {
    if (typeof action === 'function') {
      return action(dispatch, getState, extraArgument);
    }
    return next(action);
  };
}

const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;

export default thunk;

從這個裡面可以看出,它就是加強了dispatch的功能,在dispatch一個action之前,去判斷action是否是一個函數,如果是函數,那麼就執行這個函數。

那麼我們使用起來就很簡單了,此時我們修改actions.js

import axios from 'axios';
import * as T from './actionTypes';

export const changeBtnText = (text) => {
  return {
    type: T.CHANGE_BTN_TEXT,
    payload: text
  };
};

export const changeBtnTextAsync = (text) => {
  return (dispatch, getState) => {
    if (!getState().isLoading) {
      dispatch(changeBtnText('正在載入中'));
    }
    axios.get(`http://test.com/${text}`).then(() => {
      if (getState().isLoading) {
        dispatch(changeBtnText('載入完畢'));
      }
    }).catch(() => {
      dispatch(changeBtnText('載入有誤'));
    });
  };
};

而原來mapDispatchToProps中的玩法和同步action的玩法是一樣的:

const mapDispatchToProps = (dispatch) => {
  return {
    changeText: (text) => {
      dispatch(changeBtnTextAsync(text));
    }
  };
};

通過redux-thunk我們可以簡單地進行非同步操作,並且可以獲取到各個非同步操作時期狀態的值。

redux-actions:簡化redux的使用

Redux雖然好用,但是裡面還是有些重覆代碼,所以有了redux-actions來簡化那些重覆代碼。

這部分簡化工作主要集中在構造action和處理reducers方面。

先來看看原先的actions

import axios from 'axios';
import * as T from './actionTypes';

export const changeBtnText = (text) => {
  return {
    type: T.CHANGE_BTN_TEXT,
    payload: text
  };
};

export const changeBtnTextAsync = () => {
  return (dispatch, getState) => {
    if (!getState().isLoading) {
      dispatch(changeBtnText('正在載入中'));
    }
    axios.get('http://test.com').then(() => {
      if (getState().isLoading) {
        dispatch(changeBtnText('載入完畢'));
      }
    }).catch(() => {
      dispatch(changeBtnText('載入有誤'));
    });
  };
};

然後再來看看修改後的:

import axios from 'axios';
import * as T from './actionTypes';
import { createAction } from 'redux-actions';

export const changeBtnText = createAction(T.CHANGE_BTN_TEXT, text => text);

export const changeBtnTextAsync = () => {
  return (dispatch, getState) => {
    if (!getState().isLoading) {
      dispatch(changeBtnText('正在載入中'));
    }
    axios.get('http://test.com').then(() => {
      if (getState().isLoading) {
        dispatch(changeBtnText('載入完畢'));
      }
    }).catch(() => {
      dispatch(changeBtnText('載入有誤'));
    });
  };
};

這一塊代碼替換上面的部分代碼後,程式運行結果依然保持不變,也就是說createAction只是對上面的代碼進行了簡單的封裝而已。

這裡註意到,非同步的action就不要用createAction,因為這個createAction返回的是一個對象,而不是一個函數,就會導致redux-thunk的代碼沒有起到作用。

這裡也可以使用createActions這個函數同時創建多個action,但是講道理,這個語法很奇怪,用createAction就好。

同樣redux-actions對reducer的部分也進行了處理,比如handleAction以及handelActions。

先來看看原先的reducers

import * as T from './actionTypes';

const initialState = {
  btnText: '我是按鈕',
};

const pageMainReducer = (state = initialState, action) => {
  switch (action.type) {
    case T.CHANGE_BTN_TEXT:
      return {
        ...state,
        btnText: action.payload
      };
    default:
      return state;
  }
};

export default pageMainReducer;

然後使用handleActions來處理

import { handleActions } from 'redux-actions';
import * as T from './actionTypes';

const initialState = {
  btnText: '我是按鈕',
};

const pageMainReducer = handleActions({
  [T.CHANGE_BTN_TEXT]: {
    next(state, action) {
      return {
        ...state,
        btnText: action.payload,
      };
    },
    throw(state) {
      return state;
    },
  },
}, initialState);

export default pageMainReducer;

這裡handleActions可以加入異常處理,並且幫助處理了初始值。

註意,無論是createAction還是handleAction都只是對代碼做了一點簡單的封裝,兩者可以單獨使用,並不是說使用了createAction就必須要用handleAction。

redux-promise:redux-actions的好基友,輕鬆創建和處理非同步action

還記得上面在使用redux-actions的createAction時,我們對非同步的action無法處理。

因為我們使用createAction後返回的是一個對象,而不是一個函數,就會導致redux-thunk的代碼沒有起到作用。

而現在我們將使用redux-promise來處理這類情況。

可以看看之前我們使用 createAction的例子:

export const changeBtnText = createAction(T.CHANGE_BTN_TEXT, text => text);

現在我們先加入redux-promise中間件:

import thunk from 'redux-thunk';
import createLogger from 'redux-logger';
import promiseMiddleware from 'redux-promise';
import reducer from './reducers';

const store = createStore(reducer, applyMiddleware(thunk, createLogger, promiseMiddleware));

然後再處理非同步action:

export const changeBtnTextAsync = createAction(T.CHANGE_BTN_TEXT_ASYNC, (text) => {
  return axios.get(`http://test.com/${text}`);
});

可以看到我們這裡返回的是一個Promise對象.(axios的get方法結果就是Promise對象)

我們還記得redux-thunk中間件,它會去判斷action是否是一個函數,如果是就執行。

而我們這裡的redux-promise中間件,他會在dispatch時,判斷如果action不是類似

{
  type:'',
  payload: ''
}

這樣的結構,也就是 FSA,那麼就去判斷是否為promise對象,如果是就執行action.then的玩法。

很明顯,我們createAction後的結果是FSA,所以會走下麵這個分支,它會去判斷action.payload是否為promise對象,是的話那就

action.payload
  .then(result => dispatch({ ...action, payload: result }))
  .catch(error => {
    dispatch({ ...action, payload: error, error: true });
    return Promise.reject(error);
  })

也就是說我們的代碼最後會轉變為:

axios.get(`http://test.com/${text}`)
  .then(result => dispatch({ ...action, payload: result }))
  .catch(error => {
    dispatch({ ...action, payload: error, error: true });
    return Promise.reject(error);
  })

這個中間件的代碼也很簡單,總共19行,大家可以在github上直接看看。

redux-sage:控制器與更優雅的非同步處理

我們的非同步處理用的是redux-thunk + redux-actions + redux-promise,其實用起來還是蠻好用的。

但是隨著ES6中Generator的出現,人們發現用Generator處理非同步可以更簡單。

而redux-sage就是用Generator來處理非同步。

以下講的知識是基於Generator的,如果您對這個不甚瞭解,可以簡單瞭解一下相關知識,大概需要2分鐘時間,並不難。

redux-sage文檔並沒有說自己是處理非同步的工具,而是說用來處理邊際效應(side effects),這裡的邊際效應你可以理解為程式對外部的操作,比如請求後端,比如操作文件。

redux-sage同樣是一個redux中間件,它的定位就是通過集中控制action,起到一個類似於MVC中控制器的效果。

同時它的語法使得複雜非同步操作不會像promise那樣出現很多then的情況,更容易進行各類測試。

這個東西有它的好處,同樣也有它不好的地方,那就是比較複雜,有一定的學習成本。

並且我個人而言很不習慣Generator的用法,覺得Promise或者await更好用。

這裡還是記錄一下用法,畢竟有很多框架都用到了這個。

應用這個中間件和我們的其他中間件沒有區別:

import React from 'react';
import { createStore, applyMiddleware } from 'redux';
import promiseMiddleware from 'redux-promise';
import createSagaMiddleware from 'redux-saga';
import {watchDelayChangeBtnText} from './sagas';
import reducer from './reducers';

const sagaMiddleware = createSagaMiddleware();

const store = createStore(reducer, applyMiddleware(promiseMiddleware, sagaMiddleware));

sagaMiddleware.run(watchDelayChangeBtnText);

創建sage中間件後,然後再將其中間件接入到store中,最後需要用中間件運行sages.js返回的Generator,監控各個action。

現在我們給出sages.js的代碼:

import { delay } from 'redux-saga';
import { put, call, takeEvery } from 'redux-saga/effects';
import * as T from './components/pageMain/actionTypes';
import { changeBtnText } from './components/pageMain/actions';

const consoleMsg = (msg) => {
  console.info(msg);
};
/**
 * 處理編輯效應的函數
 */
export function* delayChangeBtnText() {
  yield delay(1000);
  yield put(changeBtnText('123'));
  yield call(consoleMsg, '完成改變');
}
/**
 * 監控Action的函數
 */
export function* watchDelayChangeBtnText() {
  yield takeEvery(T.WATCH_CHANGE_BTN_TEXT, delayChangeBtnText);
}

在redux-sage中有一類用來處理邊際效應的函數比如put、call,它們的作用是為了簡化操作。

比如put相當於redux的dispatch的作用,而call相當於調用函數。(可以參考上面代碼中的例子)

還有另一類函數就是類似於takeEvery,它的作用就是和普通redux中間件一樣攔截到action後作出相應處理。

比如上面的代碼就是攔截到T.WATCH_CHANGE_BTN_TEXT這個類型的action,然後調用delayChangeBtnText。

然後可以回看我們之前的代碼,有這麼一行代碼:

sagaMiddleware.run(watchDelayChangeBtnText);

這裡實際就是引入監控的這個生成器後,再運行監控生成器。

這樣我們在代碼裡面dispatch類型為T.WATCH_CHANGE_BTN_TEXT的action時就會被攔截然後做出相應處理。

當然這裡有人可能會提出疑問,難道每一個非同步都要這麼寫嗎,那豈不是要run很多次?

當然不是這個樣子,我們可以在sage中這麼寫:

export default function* rootSaga() {
  yield [
    watchDelayChangeBtnText(),
    watchOtherAction()
  ]
}

我們只需要按照這個格式去寫,將watchDelayChangeBtnText這樣用於監控action的生成器放在上面那個代碼的數組中,然後作為一個生成器返回。

現在只需要引用這個rootSaga即可,然後run這個rootSaga。

以後如果要監控更多的action,只需要在sages.js中加上新的監控的生成器即可。

通過這樣的處理,我們就將sages.js做成了一個像MVC中的控制器的東西,可以用來處理各種各樣的action,處理複雜的非同步操作和邊際效應。

但是這裡要註意,一定要加以區分sages.js中使用監控的action和真正功能用的action,比如加個watch關鍵字,以免業務複雜後代碼混亂。

總結

總的來說:

  • redux是一個可預測的狀態容器,
  • react-redux是將store和react結合起來,使得數據展示和修改對於react項目而言更簡單
  • redux中間件就是在dispatch action前對action做一些處理
  • redux-thunk用於對非同步做操作
  • redux-actions用於簡化redux操作
  • redux-promise可以配合redux-actions用來處理Promise對象,使得非同步操作更簡單
  • redux-sage可以起到一個控制器的作用,集中處理邊際效用,並使得非同步操作的寫法更優雅。

OK,雖然說不想寫那麼多,結果還是寫了一大堆。

如果您覺得對您還有幫助,那麼也請點個贊吧。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 這個系列將從基礎語法講起,把react全家桶都講到,然後到具體的使用,最後完成後,會寫一個完整的demo。 前置要求: 基本的CSS,JS要熟練。 部分ES6語法需要瞭解。可以參考下麵提到的阮一峰老師的《ECMAScript 6 入門》和MDN文檔。 目前已經完成的內容: "react教程(零)安裝 ...
  • CSS3實現各種表情 效果圖: 代碼如下,複製即可使用: 如有錯誤,歡迎聯繫我改正,非常感謝!!! ...
  • 最近剛剛看完了《你不知道的 JavaScript》上捲,對 JavaScript 有了更進一步的瞭解。 《你不知道的 JavaScript》上捲由兩部分組成,第一部分是《作用域和閉包》,第二部分是《this 和對象原型》。下麵我會按照簡單介紹一下每一章的主要內容及閱讀感受。 第一部分《作用域和閉包》 ...
  • 最近學習了 HTML5 中的重頭戲 。利用 canvas,前端人員可以很輕鬆地、進行圖像處理。其 API 繁多,這次主要學習常用的 API,並且完成以下兩個代碼: 1. 實現去色濾鏡 2. 實現負色(反色)濾鏡 歡迎入群:_857989948_ 。IT 技術深度交流和分享,涉及方麵包括但不限於:網站 ...
  • jQuery中常用的事件: blur:失去焦點 focus:獲取焦點 click:點擊事件 keydown:鍵盤按下的事件 keyup:鍵盤按下鬆開後的事件 mousemove:滑鼠移動事件 hover:滑鼠懸浮事件 mousedown:滑鼠按下發生的事件 mouseup:滑鼠按下鬆開發生的事件 . ...
  • 1、實現動態圖片的切換隻需要改變目標圖片的路徑 ...
  • FormData格式提交的post參數可以直接從req.body里取,而axios用request payload,req.body都是空對象。 百度一圈,全是你抄我我抄你,要麼讓你繞路用contentType+JSON.stringfy,要麼就res.on流讀取請求數據,一點都不優雅好麽。其實沒那 ...
  • 得益於 JavaScript 加入的 decorator 特性,可以使我們跟 Java/C 一樣,更加直觀自然的,做面向切麵編程。而隨著 TypeScript 的成熟,類型系統也讓我們增強了信心,面對複雜的業務邏輯,也更有底氣。 "egg controller" 是集合了一些在 Controller ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...