关于redux生态中循序渐进出现的redux-thunk、redux-promise、redux-saga、redux-observable等异步中间件库其实本质上都是为了处理一个状态追踪管理问题,至于为什么会出现那么多方案,实际上都是为了使我们的组件逻辑表述更加纯粹。
假如说我们完全不需要对我们的全局状态修改进行追踪(对应的action
派发,Redux Devtools观察),也没有非常复杂的异步处理场景,React自身提供的Context API是完全够用了…
社区中有一篇文章对渐进式演变的描述我觉得比较贴切,内容我贴在下面,原文链接🔗
状态管理渐进式提升使用对比
Context
- 我需要一个全局数据源且其他组件可以直接获取/改变全局数据源中的数据
Redux
- 我需要一个全局数据源且其他组件可以直接获取/改变全局数据源中的数据
- 我需要全程跟踪/管理 action 的分发过程/顺序
actions
符合FSA
标准,即返回的都是一个纯对象(Plain Object)
redux-thunk
- 需要一个全局数据源且其他组件可以直接获取/改变全局数据源中的数据
- 我需要全程跟踪/管理 action 的分发过程/顺序
- 我需要组件对同步或异步的 action 无感,调用异步 action 时不需要显式地传入 dispatch
redux-saga
- 我需要一个全局数据源且其他组件可以直接获取/改变全局数据源中的数据
- 我需要全程跟踪/管理 action 的分发过程/顺序
- 我需要组件对同步或异步的 action 无感,调用异步 action 时不需要显式地传入 dispatch
- 我需要声明式地来表述复杂异步数据流(如长流程表单,请求失败后重试等),命令式的 thunk 对于复杂异步数据流的表现力有限
- 在
redux-saga/effects
下导出的诸如take
、call
、fork
这些api
实际上返回的就是一个纯对象,通过yield
,中间件可以拿到这个副作用的描述并执行对应动作 select
类似于直接从store
映射出制定key
的状态,yield select(state => state.id)
另外既然是generator
实现的同步编程异步场景方案,那副作用提供的方法其实相互直接的差异主要在于是否会阻塞saga
。
1 | const effect = call(ajax.get, '/userLogin') |
就拿call
和fork
来说,call
会阻塞当前saga
执行,直到被调用函数fn
返回结果,才会执行下一步代码;而fork
不会阻塞,它会直接返回对应的副作用描述对象。
fork
异步非阻塞特性更适合在后台运行不影响主流程的代码,
当然saga
还有个比较有意思也同样重要的概念:父子关系
。准确来说,正常嵌套的task
间会存在父子关系:
1 | function* rootSaga() { |
如果其中有一个saga
子任务抛出错误了,如throw new Error('')
这种,那就会导致整个应用无法使用。
要改进这种场景,就需要将fork
替换为spawn
,区别在于后者返回的是一个独立(isolate)任务,不存在父子关系,不会影响父级及嵌套的其他任务正常执行:
1 | function* rootSaga() { |
当然这样的做法,除了刷新页面无法恢复出问题的saga
正常运作,所以我们须要一个更好的重试机制:
1 | function* rootSaga() { |
dva
- dva是支付宝团队
redux
最佳实践的一个落地方案,它是一个framework
,我们按照它的设定,在一个文件下进行统一副作用处理、事件分发 - 上述的异步处理方案中,我们都发现了一个问题,正如sorrycc所言:概念太多,并且 reducer, saga, action 都是分离的(分文件);编辑成本高,需要在 reducer, saga, action 之间来回切换;不便于组织业务模型 (或者叫 domain model) 。比如我们写了一个 userlist 之后,要写一个 productlist,需要复制很多文件。
- 底层还是saga那一套
saga这个命名怎么来的
科学上网后的回答解释是redux-saga
的saga
这一命名其实来源于后端的概念,在后端领域中saga
描述的是一个处理分布式事务的概念,而在redux-saga
中则变成了一个流程管理(process manager)一样的概念。
传送链接🔗, 原文其实讨论的是一个为什么我们要在Redux下整那么多异步中间件的话题讨论,下面的回答都非常专业及具有指导意义,推荐阅读。
副作用
根据Wikipedia的解释,在js世界中,副作用(Side Effects)通常指接口数据请求、读取浏览器本地缓存(Local Storage、Cookie)等。
redux-saga初体验(伪)
严格来说,我并不是初次接触redux-saga
(的语法),曾经做过的一个后台管理系统用的是阿里的dva全家桶搭建的,他们内部集成的状态管理就是封装的saga的东西。不过那个时候,只是一个单纯的,它提供的是这个方法,我就用它的脑回路。自身没有一个系统性的考量,为什么用它,仅是因为就当时而言比较新?现在review回过去的代码,其实也发现当时并没有使用到任何redux-saga
真正具有优势的东西,也就是跟着以前的redux-thunk
照猫画虎的简单替换了一下。
回到正题,学习一门库的最快方式肯定是去官方文档查阅,基本上成熟的库的官方都会集成完成的使用api说明和提供给接触者的demo。
官方提供了一个初学者使用的简易计数demo,我们可以通过下面的方式拉取测试。PS,完整功能在sagas分支上,默认启动在9966端口。
1 | git clone https://github.com/redux-saga/redux-saga-beginner-tutorial.git |