为什么使用 Redux-Observable 而不是 Redux-Saga?

Why use Redux-Observable over Redux-Saga?

我已经使用 Redux-Saga. Code written with it is easy to reason so far, except JS generator function is messing up my head from time to time. From my understanding, Redux-Observable 可以实现处理副作用的类似工作,但不使用生成器函数。

然而,Redux-Observable 的文档并没有提供很多关于为什么它优于 Redux-Saga 的观点。我想知道不使用生成器函数是否是使用 Redux-Observable 的唯一好处。使用 Redux-Observable 而不是 Redux-Saga 的缺点、陷阱或妥协可能是什么?

我使用 Redux-Observable 而不是 Redux-Saga,因为我更喜欢使用可观察对象而不是生成器。我将它与 RXJS 一起使用,RXJS 是一个用于处理数据流的强大库。可以把它想象成用于异步的 lodash。关于选择一个而不是另一个的任何缺点、问题和妥协,请查看 Jay Phelps 的

redux-saga as a project has existed longer than redux-observable so that's certainly one major selling point. You'll find more documentation, examples, and likely are have a better community to get support from.

The counter being that the operators and APIs you learn in redux-saga aren't nearly as transferable as learning RxJS, which is used all over the place. redux-observable is super super super simple internally, it's really just giving you a natural way for you to use RxJS. So if you know RxJS (or want to), it's an extremely natural fit.

My advice at the moment for most people is that if you have to ask which one you should use, you probably should choose redux-saga.

免责声明:我是 redux-observable 的作者之一,所以我很难做到 100% 公正。

我们目前不提供 redux-observable 比 redux-saga 更好的任何理由,因为...事实并非如此。

tl;dr 两者各有利弊。许多人会发现一个比另一个更直观,但如果您不了解 RxJS (redux-observable) 或生成器/"effects as data" (redux-saga),则两者都很难以不同的方式学习。

它们以极其相似的方式解决了同样的问题,但也有一些根本的区别,只有当你足够多地使用它们时,这些区别才会真正显现出来。

redux-observable 将几乎所有内容都推迟到惯用的 RxJS。所以如果你有 RxJS 知识(或获得它),学习和使用 redux-observable 是超级超级自然的。这也意味着这些知识可以转移到 redux 以外的东西上。如果你决定切换到 MobX,如果你决定切换到 Angular2,如果你决定切换到未来的热门 X,那么 RxJS 很有可能会帮助你。这是因为 RxJS 是一个通用的异步库,在很多方面它本身就像一种编程语言——整个 "Reactive Programming" 范式。 RxJS 自 2012 年以来就存在,并开始作为 Rx.NET 的端口(几乎所有主要语言中都有 "ports",它 很有用 )。

redux-saga 本身提供其 time-based 运算符,因此虽然您在这种 process-manager 风格中获得的有关生成器和处理副作用的知识是可转移的,但实际运算符和用法不是在任何其他主要图书馆中使用。所以这有点不幸,但肯定不应该是 deal-breaker 本身。

它还使用 "effects as data" (described here),一开始你可能很难理解,但这意味着你的 redux-saga 代码实际上并没有执行副作用本身。相反,您使用的辅助函数创建 objects,它们类似于代表执行副作用的意图的任务,然后内部库为您执行它。这使得测试非常容易,不需要模拟,并且对某些人非常有吸引力。然而,我个人发现这意味着你的单元测试重新实现了你的故事的大部分逻辑——使这些测试在我看来不是很有用(这个观点并不是每个人都同意)

人们经常问为什么我们不用 redux-observable 做类似的事情:对我来说,它与普通的惯用 ​​Rx 从根本上不兼容。在 Rx 中,我们使用像 .debounceTime() 这样的运算符来封装去抖动所需的逻辑,但这意味着如果我们想制作一个不实际执行去抖动而是发出任务的版本 objects有了意图,你现在已经失去了 Rx 的力量,因为你不能再只是链操作符,因为他们会在那个任务上操作 object,而不是操作的真正结果。这真的很难优雅地解释。它再次需要对 Rx 有深入的了解才能理解方法的不兼容性。如果您真的 想要这样的东西,请查看使用 cycle.js 并且主要有这些目标的 redux-cycles。我觉得这对我的口味来说需要太多仪式感,但如果您感兴趣,我鼓励您试一试。

正如 ThorbenA 提到的,我并不回避承认 redux-saga 目前(2016 年 10 月 13 日)是 redux 复杂副作用管理方面的明显领导者。它起步较早,拥有更强大的社区。因此,在街区的新孩子上使用事实上的标准有很大的吸引力。我认为可以肯定地说,如果你在没有先验知识的情况下使用其中任何一个,你就会感到困惑。我们都使用相当先进的概念,一旦你 "get",复杂的副作用管理就会变得容易得多,但在那之前很多人都会绊倒。

我能给出的最重要的建议是在需要之前不要引入这两个库中的任何一个。如果您只是进行简单的 ajax 调用,您可能不需要它们。 redux-thunk 学习起来非常简单,并为基础知识提供了足够的东西——但异步越复杂,redux-thunk 就变得越难(甚至不可能)。但是对于 redux-observable/saga 在许多方面来说,异步越复杂,它就越闪耀。在同一个项目中使用 redux-thunk 和其中一个 (redux-observable/saga) 也有很多好处! redux-thunk 用于常见的简单内容,然后仅使用 redux-observable/saga 用于复杂的内容。这是保持生产力的好方法,因此您不会为 redux-thunk.

微不足道的事情而奋斗 redux-observable/saga

我认为有些事情你需要考虑。

  1. 复杂性
  2. 编码风格
  3. 学习曲线
  4. 可测试性

Lets say we want to fetch user from API

// Redux-Saga

import axios from 'axios' 

function* watchSaga(){
  yield takeEvery('fetch_user', fetchUser) // waiting for action (fetch_user)
}

function* fetchUser(action){
    try {
        yield put({type:'fetch_user_ing'})
        const response = yield call(axios.get,'/api/users/1')
        yield put({type:'fetch_user_done',user:response.data})
  } catch (error) {
        yield put({type:'fetch_user_error',error})
  }
}

// Redux-Observable
import axios from 'axios'

const fetchUserEpic = action$ => 
    action$
        .ofType('fetch_user')
        .flatMap(()=>
          Observable.from(axios.get('/api/users/1')) // or use Observable.ajax
            .map(response=>({type:'fetch_user_done', user:response.data}))
            .catch(error => Observable.of({type:'fetch_user_error',error}))
            .startWith({type:'fetch_user_ing'})
        )

此外,我写这篇文章是为了深入比较Redux-saga和Redux-Observable之间的差异。查看 this link here or presentation

我重视 Rx 具有的跨语言和运行时的可移植性。即使您的应用程序不会更改语言,您的职业可能会。尽可能地利用你的学习,但你可以自己调整。它是通往 .Net LINQ 的绝佳门户。

Redux-Observable 是一个了不起的库,我们在生产环境中使用了它 1.5 年,到目前为止没有任何问题,它是完全可测试的并且可以轻松地与任何框架集成。我们有极度超载的并行套接字通道,唯一使我们免于冻结的是 Redux-Observable

我有3点想在这里提一下。

1.复杂性和学习曲线

Redux-saga 在这里轻松击败 redux-observable。如果你只需要一个简单的请求来完成授权,并且出于某些原因不想使用 redux-thunk,你应该考虑使用 redux-saga,它更容易理解。

如果你之前没有 Observable 的知识,这对你来说会很痛苦,你的团队会训练你:)

2。 Observable 和 RxJS 能给我带来什么?

当谈到异步逻辑时,Observable 是您的瑞士刀,Observable 几乎可以为您做任何事情。你永远不应该将它们与 promises 或 generators 进行比较,它更强大,就像将 Optimus Prime 与 Chevrolet 进行比较一样。

那么 RxJS 呢?就像 lodash.js 但对于异步逻辑,一旦你进入,你将永远不会切换到不同的东西。

3。响应式扩展

只需检查这个 link

http://reactivex.io/languages.html

所有现代编程语言都实现了响应式扩展,它只是您函数式编程的关键。

所以明智地花时间学习 RxJS 和使用 redux-observable :)

由于这里有一大堆关于 redux-observable 的讨论,我想我应该给出论点的传奇方面。我不使用 redux-observable 或 RxJS,所以我无法进行并排比较,但我使用 sagas 效果很好。

为了它的价值,我在 Web 应用程序的生产中使用了 sagas。

Sagas vs. Thunk

Saga 轻松获胜。我不喜欢 thunk 如何在我的动作创作者中加入逻辑。这也使得连续执行几个请求很麻烦。我简要地查看了 redux-observable 以完成这项工作,但最后选择了 Sagas。

传奇的学习曲线

了解生成器是什么以及它们为何重要是理解传奇的关键。但我要强调的是,您不需要 了解发电机的内在和外在。您只需要知道您正在通过 yield 语句传递控制权,并且 saga 将在您的异步代码解析后交回控制权。在那之后,就不难理解传奇中发生的事情了。

核心传奇方法是(根据我的经验):

  • call - 调用任意一段代码并获取 return 值。支持承诺。异步处理和传奇之间的巨大协同作用。
  • select - 调用选择器。这一点相当精彩。选择器是 redux 的核心,并且 100% 支持!
  • put - 又名 dispatch 一个动作。想派多少就派多少!

还有其他的功能,但是如果你能掌握这三个,那你就大有可为了。

结论

我选择 sagas 的原因是它的易用性。 redux-observable 看起来像是一个挑战。我对传奇 100% 满意。比我想象的还要开心。

根据我的经验,Sagas(方式)比 thunk 好并且相对容易理解。 Rx 不是每个人都喜欢的。如果你不是来自那个生态系统,我会强烈考虑 sagas 而不是 redux-observable and/or 不打算在未来使用 Rx。

如果您使用 Typescript 编写应用程序,我建议检查 typeless。 它受到 Redux-Observable 的启发,也依赖于 RxJS,但是有构建应用程序的整个生态系统。

redux-observable/redux-saga 的最大缺点是缺乏指南。没有关于如何延迟加载 reducer、sagas 或 epics 的官方指南。在扩展更大的应用程序时,代码拆分至关重要。 延迟加载的自定义解决方案通常不适用于 HMR,导致开发人员体验不佳。

无类型专家:

  1. 专为 TypeScript 设计
    所有 API 都是为打字稿和类型安全而设计的:
    • Typescript 将提高您的工作效率,而不是减慢您的速度。
    • 只需要必要的注释:状态、动作参数。
    • 没有类型转换。一切都是自动推断的。 95% 的代码看起来像纯 javascript。
    • 没有 RootAction、RootEpic、RootState 或其他辅助类型。
  2. 提供所有积木
    • Typeless 包括构建中型或企业级应用程序的一切。
    • 你不需要依赖多个小库。
  3. 模块化
    • 适当的模块化对于构建可扩展的应用程序至关重要。
    • 无需为 epics、reducer、类型等创建根文件。创建新模块后,您可以从任何地方附加它。类似于标准的 React 组件。
  4. 固执己见
    • 默认解决所有常见用例和问题。无需过多考虑如何解决琐碎的问题。
    • 已提供所有建议和最佳做法!

查看 https://typeless.js.org/