文档版本号:0.9.5

在线 Gitbook 地址:http://superRaytin.github.io/redux-saga-in-chinese

英文原版:http://yelouafi.github.io/redux-saga

redux-saga 是一个用于管理 Redux 应用异步操作(Side Effects。译注:直译成 “副作用” 不太通顺,所以这里译为 “异步操作” 更好理解)的中间件(又称异步 action)。 redux-saga 通过创建 Sagas 将所有的异步操作逻辑收集在一个地方集中处理,可以用来代替 redux-thunk 中间件。

这意味着应用的逻辑会存在两个地方:

  • Reducers 负责处理 action 的 state 更新。

  • Sagas 负责协调那些复杂或异步的操作。

Sagas 是通过 Generator 函数来创建的。如果你还不熟悉 Generator,可以在这里找到 一些有用的链接

Sagas 不同于 Thunks,Thunks 是在 action 被创建时调用,而 Sagas 只会在应用启动时调用(但初始启动的 Sagas 可能会动态调用其他 Sagas)。 Sagas 可以被看作是在后台运行的进程。Sagas 监听发起的 action,然后决定基于这个 action 来做什么:是发起一个异步调用(比如一个 Ajax 请求),还是发起其他的 action 到 Store,甚至是调用其他的 Sagas。

redux-saga 的世界里,所有的任务都通用 yield Effects 来完成(译注:Effect 可以看作是 redux-saga 的任务单元)。Effects 都是简单的 Javascript 对象,包含了要被 Saga middleware 执行的信息(打个比方,你可以看到 Redux action 其实是一个个包含执行信息的对象)。 redux-saga 为各项任务提供了各种 Effect 创建器,比如调用一个异步函数,发起一个 action 到 Store,启动一个后台任务或者等待一个满足某些条件的未来的 action。

因为使用了 Generator,redux-saga 让你可以用同步的方式写异步代码。就像你可以使用 async/await 函数所能做的一样。但 Generator 可以让你做一些 async 函数做不到的事情。

事实上 Sagas yield 普通对象的方式让你能容易地测试 Generator 里所有的业务逻辑,可以通过简单地迭代 yield 过的对象进行简单的单元测试。

此外,redux-saga 启动的任务可以在任何时候通过手动取消,也可以把任务和其他的 Effects 放到 race 方法里以自动取消。

开始

安装

npm install --save 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}})
  }
  ...
}

这个组件发起一个普通对象格式的 action 到 Store。我们将创建一个 Saga 来监听所有的 USER_FETCH_REQUESTED action,并触发一个 API 调用以获取用户数据。

sagas.js

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

// workder Saga : 将在 USER_FETCH_REQUESTED action 被发起时调用
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 被发起时调用 fetchUser
  允许并发(译注:即同时处理多个相同的 action)
*/
function* mySaga() {
  yield* takeEvery("USER_FETCH_REQUESTED", fetchUser);
}

/*
  也可以使用 takeLatest

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

为了能跑起 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'

const sagaMiddleware = createSagaMiddleware(mySaga)
const store = createStore(
  reducer,
  applyMiddleware(sagaMiddleware)
)

// render the application

文档

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

dist/ 文件夹有一个可用的 umd redux-saga 构建文件。redux-sagaReduxSaga 挂载在全局 window 对象中。

如果你不使用 Webpack 或 Browserify,umd 版本非常有用。你可以通过 unpkg 直接使用。

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

重要提示! 如果你的目标浏览器不支持 es2015 generators,那么你必须再使用一个可用的 polyfill,比如 babel 提供的:browser-polyfill.min.js。 这个 polyfill 必须在 redux-saga 之前被加载。

import 'babel-polyfill'
// then
import sagaMiddleware from 'redux-saga'

从资源构建示例

git clone https://github.com/yelouafi/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

//sorry, no tests yet

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

npm run real-world

//sorry, no tests yet

贡献者

定期更新

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