为啥使用 Redux-Observable 而不是 Redux-Saga?
Posted
技术标签:
【中文标题】为啥使用 Redux-Observable 而不是 Redux-Saga?【英文标题】:Why use Redux-Observable over Redux-Saga?为什么使用 Redux-Observable 而不是 Redux-Saga? 【发布时间】:2017-02-22 14:21:02 【问题描述】:我用过Redux-Saga。到目前为止,用它编写的代码很容易推理,除了 JS 生成器函数不时让我头疼。据我了解,Redux-Observable 可以实现类似的处理副作用但不使用生成器功能的工作。
然而,来自 Redux-Observable 的文档并没有提供很多关于为什么它优于 Redux-Saga 的意见。我想知道不使用生成器函数是否是使用 Redux-Observable 的唯一好处。使用 Redux-Observable 而不是 Redux-Saga 可能有什么缺点、问题或妥协?提前致谢。
【问题讨论】:
我写了一个有趣但详细的博客,我发现 Redux-Saga 比 Redux-Observable 更适合那些整天不生活/吃/呼吸 observables 的人。我敢肯定,如果您的整个堆栈都是可观察的,那就太好了。 shift.infinite.red/… 【参考方案1】:免责声明:我是 redux-observable 的作者之一,所以我很难做到 100% 公正。
我们目前没有提供任何理由 redux-observable 比 redux-saga 更好,因为......它不是。 ?
tl;dr 两者各有利弊。许多人会发现其中一个比另一个更直观,但如果您不了解 RxJS(redux-observable)或生成器/“作为数据的效果”(redux-saga),那么两者都很难以不同的方式学习。
它们以极其相似的方式解决相同的问题,但有一些根本差异,只有在你充分使用它们后才会真正显现出来。
redux-observable 几乎将一切都推迟到惯用的 RxJS。所以如果你有 RxJS 知识(或获得它),学习和使用 redux-observable 是超级超级自然的。这也意味着这些知识可以转移到 redux 以外的东西上。如果您决定切换到 MobX,如果您决定切换到 Angular2,如果您决定切换到未来的热门 X,那么 RxJS 可以帮助您的机会非常好。这是因为 RxJS 是一个通用的异步库,在很多方面它本身就像一门编程语言——整个“反应式编程”范式。 RxJS 自 2012 年以来就存在,最初是作为 Rx.NET 的一个端口(几乎所有主要语言都有“端口”,它很有用)。
redux-saga 自己提供了基于时间的运算符,因此虽然您在这种流程管理器样式中获得的有关生成器和处理副作用的知识是可以转移的,但实际的运算符和用法并未在任何其他主要库中使用。所以这有点不幸,但它本身当然不应该破坏交易。
它还使用“作为数据的效果”(described here),一开始您可能难以理解,但这意味着您的 redux-saga 代码本身并没有真正执行副作用。相反,您使用的辅助函数会创建类似于任务的对象,这些任务表示执行副作用的意图,然后内部库会为您执行它。这使得测试变得非常容易,无需模拟,并且对某些人非常有吸引力。但是,我个人发现这意味着您的单元测试重新实现了您传奇的大部分逻辑——使这些测试在 IMO 中不是很有用(这种观点并非所有人都同意)
人们经常问为什么我们不使用 redux-observable 做类似的事情:对我来说,它与普通的惯用 Rx 根本不兼容。在 Rx 中,我们使用像 .debounceTime()
这样的运算符来封装去抖动所需的逻辑,但这意味着如果我们想要创建一个不实际执行去抖动的版本,而是根据意图发出任务对象,你已经现在失去了 Rx 的力量,因为你不能再链接操作符了,因为它们会在那个任务对象上操作,而不是操作的真正结果。这真的很难优雅地解释。它再次需要对 Rx 有深入的了解才能理解方法的不兼容性。如果您真的想要这样的东西,请查看redux-cycles,它使用了cycle.js,并且主要具有这些目标。我发现这对我的口味来说需要太多的仪式,但如果你感兴趣,我鼓励你试一试。
正如 ThorbenA 所提到的,我不会回避承认 redux-saga 目前(10/13/16)是 redux 复杂副作用管理方面的明确领导者。它起步较早,并且拥有更强大的社区。因此,使用事实上的标准对街区的新孩子有很大的吸引力。我认为可以肯定地说,如果您在没有先验知识的情况下使用其中任何一种,您就会感到困惑。我们都使用了相当先进的概念,一旦你“掌握”了这些概念,复杂的副作用管理就会变得容易得多,但在那之前,很多人都会遇到困难。
我能给出的最重要的建议是,在你需要这些库之前不要引入它们。如果您只是进行简单的 ajax 调用,您可能不需要它们。 redux-thunk 简单易学,并且为基础提供了足够的基础——但异步越复杂,redux-thunk 就越难(甚至不可能)。但是对于 redux-observable/saga 在许多方面它最闪耀,异步越复杂。在同一个项目中将 redux-thunk 与其他一个(redux-observable/saga)一起使用也有很多优点! redux-thunk 用于常见的简单内容,然后仅将 redux-observable/saga 用于复杂的内容。这是保持生产力的好方法,所以你不会为了那些对 redux-thunk 微不足道的事情而与 redux-observable/saga 抗争。
【讨论】:
刚看到your talk(嗯!声音!),并立即点击 ⌘+T + "redux-saga vs redux-observable"。我使用 redux-saga 已经有一段时间了(尤其是在 React Native 中),但是在看了你的演讲和这篇文章之后,我可以看到一些使用 redux-obs 的用例(对我来说)。实际上会更合适。您关于debounceTime()
的示例以及“失去”对一种非常通用的逻辑的控制让我很受打击。感谢您的解释。
刚刚也看到了演讲,并做了更多的谷歌搜索。好东西@jayphelps,谢谢分享。我特别喜欢你关于将 redux-thunk 与 redux-observable/saga 结合使用的评论。这很有意义,为什么在不必要的时候过于复杂的简单 AJAX 请求。也就是说,对于统一性和保持人们的一致性,有一些话要说。再次感谢!
在升级到 redux-saga/redux-observable 之前,你可以试试 redux-dispatch-listener,它非常简单,已经可以解决你的一些用例:github.com/slorber/redux-dispatch-subscribe
这是一个非常有用的答案。谢谢!我喜欢能够将 RxJS 的知识转移到其他领域/框架的观点。
@jayphelps 什么是“复杂异步”的例子。目前正在尝试评估我是否应该从 thunk 切换到项目的 saga/observables。谢谢:)【参考方案2】:
我认为你需要考虑一些事情。
-
复杂性
编码风格
学习曲线
可测试性
假设我们想从 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 或presentation。
【讨论】:
链接中的这种并排比较很棒,谢谢 我喜欢这个比较,但是我想提出一个问题。当您使用 api 调用比较它们时 - 您正在使用 fetch 进行 redux-observable。凉爽的。但是,当您显示“可取消”差异时..您不使用 fetch - 而是使用内部 Observable.ajax ...为什么?我更喜欢使用“fetch”或“axios”来保留它。否则,那里做得很好。 @jamesemanon 我认为他没有使用 fetch 因为 fetch API 还没有取消的选项。 (更多信息:github.com/whatwg/fetch/issues/27) 哇,与所有示例进行深入比较是最好的。谢谢!【参考方案3】:我使用 Redux-Observable 而不是 Redux-Saga,因为我更喜欢使用 observables 而不是生成器。我将它与 RXJS 一起使用,RXJS 是一个用于处理数据流的强大库。把它想象成异步的 lodash。关于选择其中一个的任何缺点、陷阱和妥协,请查看 Jay Phelps 的 this answer:
redux-saga 作为一个项目存在的时间比 redux-observable 要长,所以这肯定是一个主要卖点。您会发现更多文档、示例,并且可能有更好的社区可以从中获得支持。
反之,你在 redux-saga 中学习的操作符和 API 并不像学习 RxJS 那样可迁移,RxJS 到处都在使用。 redux-observable 内部超级超级超级简单,它实际上只是为您提供了一种使用 RxJS 的自然方式。因此,如果您了解 RxJS(或想了解),那将是非常自然的选择。
目前我对大多数人的建议是,如果你不得不问应该使用哪一个,你可能应该选择 redux-saga。
【讨论】:
【参考方案4】:Redux-Observable 是一个了不起的库,我们在生产环境中使用了 1.5 年,到目前为止没有任何问题,它完全可测试并且可以轻松地与任何框架集成。我们的并行套接字通道非常过载,唯一让我们免于冻结的是 Redux-Observable
我想在这里提 3 点。
1.复杂性和学习曲线
Redux-saga 在这里轻松击败了 redux-observable。如果你只需要一个简单的请求来完成授权,并且由于某些原因不想使用 redux-thunk,你应该考虑使用 redux-saga,它更容易理解。
如果您没有 Observable 的先验知识,这对您来说会很痛苦,您的团队会为您提供培训 :)
2。 Observable 和 RxJS 能为我提供什么?
说到异步逻辑,Observable 是你的瑞士刀,Observable 几乎可以为你做所有事情。你永远不应该将它们与更强大的 Promise 或生成器进行比较,就像将 Optimus Prime 与雪佛兰进行比较一样。
那么 RxJS 呢?它类似于 lodash.js,但对于异步逻辑,一旦你进入,你将永远不会切换到不同的东西。
3。反应式扩展
请查看此链接
http://reactivex.io/languages.html
响应式扩展适用于所有现代编程语言,它只是您进行函数式编程的关键。
因此,明智地花时间学习 RxJS 并使用 redux-observable :)
【讨论】:
【参考方案5】:我看重 Rx 所具有的跨语言和运行时的可移植性。即使您的应用程序不会改变语言,您的职业生涯也可能会改变。在学习中获得最大的影响力,但您可以根据自己的情况进行调整。它是通向 .Net LINQ 的绝佳门户。
【讨论】:
明智的选择,尽管生成器也与语言无关。【参考方案6】:由于这里有一大堆可观察到 redux 的讨论,我想我会给出论点的传奇方面。我不使用 redux-observable 或 RxJS,所以我无法并列比较,但我使用 sagas 效果很好。
为了它的价值,我在 Web 应用程序的生产环境中使用 sagas。
Sagas vs. Thunk
佐贺胜出。我不喜欢 thunk 如何在我的动作创建者中添加逻辑。这也使得连续执行一些请求很麻烦。我简要地查看了这项工作的 redux-observable,但最终选择了 Sagas。
Sagas 的学习曲线
了解什么是生成器以及它们为何如此重要是了解 sagas 的关键。但我要强调的是,您不需要了解生成器的里里外外。您只需要知道您正在使用 yield 语句传递控制权,并且在您的异步代码解析后,saga 将传递回控制权。在那之后,不难理解传奇中发生了什么。
核心 saga 方法是(根据我的经验):
call
- 调用任意位代码并获取返回值。支持承诺。异步处理和 saga 之间的协同作用非常好。
select
- 调用选择器。这一点相当精彩。选择器是 redux 的核心,并且 100% 受支持!
put
- 又名 dispatch
一个动作。事实上,随心所欲地发送!
还有其他功能,但如果你能掌握这三个功能,你将处于一个非常好的位置。
结论
我选择 sagas 的原因是易于使用。 redux-observable 看起来像是一个挑战。我对传奇百分百满意。比我想象的还要开心。
根据我的经验,Sagas (方式)比 thunk 好,而且相对容易理解。 Rx 不是每个人的一杯茶。如果你不是来自那个生态系统和/或不打算在未来使用 Rx,我会强烈考虑 sagas 而不是 redux-observable。
【讨论】:
虽然与 Q 无关,但很好。【参考方案7】:如果您使用 Typescript 编写应用程序,我建议您查看typeless。 它受到 Redux-Observable 的启发,也依赖于 RxJS,但是构建应用程序的整个生态系统。
redux-observable/redux-saga 的最大缺点是缺乏指导方针。没有关于如何延迟加载减速器、sagas 或史诗的官方指南。扩展更大的应用程序时,代码拆分至关重要。 延迟加载的自定义解决方案通常不适用于 HMR,从而导致开发人员体验不佳。
无类型专家:
-
专为 TypeScript 设计
所有 API 都是为打字稿和类型安全而设计的:
Typescript 将提高您的工作效率,而不是减慢您的速度。
只需要必要的注释:状态、动作参数。
没有类型转换。一切都是自动推断的。 95% 的代码看起来像纯 javascript。
没有 RootAction、RootEpic、RootState 或其他帮助程序类型。
提供所有构建模块
无类型包括构建中型或企业级应用程序的所有内容。
您无需依赖多个小型库。
模块化
适当的模块化对于构建可扩展的应用程序至关重要。
无需为史诗、reducers、类型等创建根文件。创建新模块后,您可以从任何位置附加它。类似于标准的 React 组件。
自以为是
默认解决所有常见用例和问题。无需过度思考如何解决琐碎的问题。
提供所有建议和最佳做法!
查看https://typeless.js.org/
【讨论】:
在推荐您作为主要贡献者的软件时,您应该添加免责声明。以上是关于为啥使用 Redux-Observable 而不是 Redux-Saga?的主要内容,如果未能解决你的问题,请参考以下文章
在 redux-observable 中,我如何控制 reducer 或 epics 是不是首先对动作做出反应?
redux-observable 无法从发出的动作中捕获错误,错误会停止动作流
NextJS:使用“redux-observable”时,“HYDRATION”操作未接收服务器有效负载
如何使用 TypeScript 为 Redux-Observable 的 Epic 输入 Actions