Redux Logo Landscape

redux-saga

文档版本号:1.0.0-beta

英文原版:https://redux-saga.js.org/

redux-saga 是一个用于管理应用程序 Side Effect(副作用,例如异步获取数据,访问浏览器缓存等)的 library,它的目标是让副作用管理更容易,执行更高效,测试更简单,在处理故障时更容易。

可以想像为,一个 saga 就像是应用程序中一个单独的线程,它独自负责处理副作用。 redux-saga 是一个 redux 中间件,意味着这个线程可以通过正常的 redux action 从主应用程序启动,暂停和取消,它能访问完整的 redux state,也可以 dispatch redux action。

redux-saga 使用了 ES6 的 Generator 功能,让异步的流程更易于读取,写入和测试。(如果你还不熟悉的话,这里有一些介绍性的链接 通过这样的方式,这些异步的流程看起来就像是标准同步的 Javascript 代码。(有点像 async/await,但 Generator 还有一些更棒而且我们也需要的功能)。

你可能已经用了 redux-thunk 来处理数据的读取。不同于 redux thunk,你不会再遇到回调地狱了,你可以很容易地测试异步流程并保持你的 action 是干净的。

开始

安装

$ npm install --save redux-saga

$ yarn add redux-saga

或者,你可以直接在 HTML 页面的 <script> 标签中使用提供的 UMD 构建文件。参见 本节

使用示例

假设我们有一个 UI 界面,在单击按钮时从远程服务器获取一些用户数据(为简单起见,我们只列出 action 触发代码)。

class UserComponent extends React.Component {
  ...
  onSomeButtonClicked() {
    const { userId, dispatch } = this.props
    dispatch({type: 'USER_FETCH_REQUESTED', payload: {userId}})
  }
  ...
}

这个组件 dispatch 一个 plain Object 的 action 到 Store。我们将创建一个 Saga 来监听所有的 USER_FETCH_REQUESTED action,并触发一个 API 调用获取用户数据。

sagas.js

import { call, put, takeEvery, takeLatest } from 'redux-saga/effects'
import Api from '...'

// worker Saga : 将在 USER_FETCH_REQUESTED action 被 dispatch 时调用
function* fetchUser(action) {
   try {
      const user = yield call(Api.fetchUser, action.payload.userId);
      yield put({type: "USER_FETCH_SUCCEEDED", user: user});
   } catch (e) {
      yield put({type: "USER_FETCH_FAILED", message: e.message});
   }
}

/*
  在每个 `USER_FETCH_REQUESTED` action 被 dispatch 时调用 fetchUser
  允许并发(译注:即同时处理多个相同的 action)
*/
function* mySaga() {
  yield takeEvery("USER_FETCH_REQUESTED", fetchUser);
}

/*
  也可以使用 takeLatest

  不允许并发,dispatch 一个 `USER_FETCH_REQUESTED` action 时,
  如果在这之前已经有一个 `USER_FETCH_REQUESTED` action 在处理中,
  那么处理中的 action 会被取消,只会执行当前的
*/
function* mySaga() {
  yield takeLatest("USER_FETCH_REQUESTED", fetchUser);
}

export default mySaga;

为了能跑起 Saga,我们需要使用 redux-saga 中间件将 Saga 与 Redux Store 建立连接。

main.js

import { createStore, applyMiddleware } from 'redux'
import createSagaMiddleware from 'redux-saga'

import reducer from './reducers'
import mySaga from './sagas'

// create the saga middleware
const sagaMiddleware = createSagaMiddleware()
// mount it on the Store
const store = createStore(
  reducer,
  applyMiddleware(sagaMiddleware)
)

// then run the saga
sagaMiddleware.run(mySaga)

// render the application

文档

在浏览器中使用 umd 构建版本

dist/ 文件夹有一个可用的 umd redux-saga 构建文件。redux-sagaReduxSaga 挂载在全局 window 对象中。这能让你在创建 Saga 中间件时不需要使用 ES6 的 import 语法:

var sagaMiddleware = ReduxSaga.default()

umd 版本在你不使用 Webpack 或 Browserify 时相当有用。你可以从 unpkg 直接读取。

以下是可用的构建好的文件:

The runtime must be imported before redux-saga: 重要! 如果你的目标浏览器不支持 ES2015 generators,那么你必须使用转换器来编译它们(比如 babel plugin) 并提供一个有效的 runtime,比如 这个。 这个 runtime 必须在 redux-saga 之前引入:

import 'regenerator-runtime/runtime'
// then
import sagaMiddleware from 'redux-saga'

从资源构建示例

$ git clone https://github.com/redux-saga/redux-saga.git
$ cd redux-saga
$ npm install
$ npm test

以下的例子是从 Redux 仓库移植过来的(截至目前)。

计数器示例

有 3 个计数器例子。

counter-vanilla

这个例子使用了 vanilla Javascript 和 UMD 构建版本。所有资源都在 index.html 中引入。

在浏览器中打开 index.html 运行这个例子。

重要:你的浏览器必须支持 Generator。最新版本的 Chrome/Firefox/Edge 已经支持。

counter

这个例子使用了 webpack 和高阶 API takeEvery

$ npm run counter

# test sample for the generator
$ npm run test-counter

cancellable-counter

这个例子使用了低阶 API 来演示任务取消的场景。

$ npm run cancellable-counter

购物车示例

$ npm run shop

# test sample for the generator
$ npm run test-shop

异步示例

$ npm run async

# test sample for the generators
$ npm run test-async

真实项目示例(使用 webpack 的热重载)

$ npm run real-world

# sorry, no tests yet

TypeScript

Redux-Saga 与 TypeScript 配合使用需要 DOM.IterableES2015.Iterable。如果你的 targetES6,则不需要再设置,然而如果是 ES5,你将需要自己把它们加进来。 检查你的 tsconfig.json 文件和官方的 compiler options 文档。

Logo

你可以在 logo 目录 中找到不同风格的 Redux-Saga 官方 logo。

贡献者

定期更新

如果看到翻译不准确、句子不通顺的地方,欢迎随时指出。本文档翻译流程按照 ETC 翻译规范,欢迎你来一起完善。