1.一文彻底搞懂 DvaJS 原理
2.react项目使用redux-saga
3.探索React异步解决方案之redux-saga
4.redux-saga 浅析
一文彻底搞懂 DvaJS 原理
dva 首先是一个基于 redux 和 redux-saga 的数据流方案,同时为了简化开发体验,dva 还额外内置了 react-router 和 fetch,因此可以理解为一个轻量级的应用框架。
在使用 dva 时,经常会遇到疑问:如何解决概念多、财神驾到源码 reducer, saga, action 分离等问题?dva 的优势在于整合了这些概念,使得开发者可以更轻松地管理和控制应用的逻辑。
然而,dva 的劣势在于它并不是一个完全独立的框架,而是基于 redux 和 redux-saga 的扩展,因此可能在某些特定需求上不够灵活。
dva 适用于需要简化开发流程、整合多种功能的中小型应用,特别适合那些希望快速上手并专注于业务逻辑的开发者。
dva 的核心概念主要包括:app 实例的初始化、使用 create 方法创建 app、中间件的配置与管理、以及应用的路由管理。
dva 的应用最简结构包括:不带 Model 的基本应用结构,以及带 Model 的应用结构,后者允许更细粒度的管理状态和逻辑。
dva 的底层原理涉及了 dva-core 的核心功能,包括 app 实例的构造、store 的初始化、以及中间件的配置与管理等关键实现。
dva 与 React、React-Redux、Redux-Saga 的差异主要体现在数据流的控制、组件间的交互以及异步操作的处理方式上。原生 React 强调纯函数组件和状态的集中管理,React-Redux 则通过 Redux 提供了状态管理能力,而 Redux-Saga 则专注于处理异步操作。
Dva 的优势在于它整合了这些功能,提供了约定大于配置的asp转盘程序源码 API,以及基于 Elm 的概念,使得编码体验更加友好和高效。它旨在简化应用开发流程,提升编码效率,同时保持代码的可读性和可维护性。
Dva 背后值得学习的思想是:关注编程的趣味性和简单性,追求代码的可复用性和易理解性。通过最小化框架的复杂性,Dva 使得切换框架变得轻松,同时鼓励开发者关注代码本身的质量和用户界面的友好性。
最后,dva 是支付宝前端应用架构发展中的一种选择,它代表了在 React + Redux 最佳实践基础上的进一步探索。通过 dva,开发者可以更高效地构建和管理复杂的应用。
react项目使用redux-saga
使用Redux-Saga之前,了解Redux的基本使用流程至关重要。从Redux官网获取详细信息。
在Redux-Saga的使用过程中,有一些关键步骤需遵循。解答1:Redux的使用流程包括store、actions、reducers和dispatch。解答2:Redux-Saga的使用流程则引入了effects,用于处理异步操作。
如何在项目中集成Redux-Saga?对于大量异步请求的处理,通过以下步骤来实现:首先,确保你的目录结构清晰。
在项目中,通常会有一个`Async.js`组件,负责展示加载状态或错误信息。`index.js`在store的入口文件中引入Redux-Saga,`reducer.js`则负责状态管理。`actionType.js`和`actionCreate.js`定义了actions类型和创建函数,小师妹源码编译`sagas.js`专门用于编写Saga逻辑,`api/index.js`和`fetch.js`用于封装AJAX请求。最后,通过`Provider`组件,将store注入到根组件中。
使用Redux-Saga时,遵循文档指引和示例代码,遵循基本流程:异步操作、效果(effects)处理、状态管理。确保你的项目结构清晰,遵循Redux-Saga的逻辑,实现高效、可维护的异步操作处理。
探索React异步解决方案之redux-saga
redux-saga是什么? Redux-saga是一个用于简化Redux应用程序中异步操作的库,目标是更优雅地管理副作用、提高执行效率、方便测试并在处理错误时提供更好的支持。它的设计基于康奈尔大学的研究,旨在解决分布式系统中长时运行事务的数据一致性问题。 什么是SideEffects? 在编程中,副作用指的是程序与外部世界(如用户、文件系统、网络上的其他计算机)进行交互的方式。在JavaScript中,这通常包括异步网络请求和浏览器缓存的读取等。 如何区别saga与thunk? 尽管都作为Redux的中间件,saga和thunk在实现上和设计理念上存在差异。saga通过命令/回答模式进行通信,每个saga都是生成器函数,以同步的方式处理异步逻辑。而thunk则通过将函数作为action传递给store,支持异步操作。 学习saga使用 saga提供了两个主要的ab通讯协议 源码中间件API,用于创建和运行saga。在安装依赖并关联store后,可以利用这些API来实现saga的运行。 saga中的关键概念 1. Task: 是saga运行的结果,提供执行和控制的接口。2. Channel: 用于在saga间传递消息,消息在被接收者请求前被缓存。
3. Buffer: 实现了消息的缓存策略。
4. SagaMonitor: 用于启动和监控saga事件。
saga的Effect创建器 Effect是包含执行指令的对象,用于指导saga中间件执行特定任务。例如,take用于监听特定action,put用于在store中发起action,call用于调用外部函数等。 saga的辅助函数和组合器 提供了一组函数来简化saga的编写,如TakeEvery、TakeLatest等,用于根据action模式生成saga。 Redux-Saga的测试 得益于saga的细粒度和低耦合性,使其在单元测试中表现出色。例如,可以测试特定的saga响应特定的action。 Redux-Saga使用技巧 1. 重试Ajax请求:在请求失败后自动重试。2. 撤销操作:确保在操作撤销时可以回滚到原始状态。
参考资料Redux-Saga 漫谈
Saga Pattern
Redux-Saga官方文档
Why saga
手写Redux-Saga源码
redux-saga 浅析
Redux提供了一种通过中间件来处理action的方式,其中,我们常用的中间件如redux-thunk(以下简称为thunk),它主要用于处理函数类型的action。除了thunk,还有其他常用的中间件,例如redux-saga(以下简称为saga)。saga提供了丰富的API,使我们能够更容易地处理异步操作。全民奇迹源码8.3本文将主要探讨saga的实现方式。
saga这一概念源自后端事物,一个事物通常包含多个子事物,这些子事物组合在一起,就构成了一个saga。例如,用户购买火车票这一事物,就包括了订票、付款、退票、出票等动作。
在redux-saga中,一个generator函数被定义为saga函数,在此generator函数中,会包含对一个异步事务的完整处理流程。
effect在saga中是一个常见的概念,任务的发起都是通过effect来实现的,下文将进一步阐述这个概念。
让我们从saga提供的一个简单的API开始,即take。take是saga提供的一个监听action的API,其基本用法如下:
一个基本的结构还是很简单的,从这个示例中,我们可以发现几个特点:
总结一下,saga在初始情况下会进行注册操作,然后在saga函数中使用take进行监听。这个流程似乎有点发布订阅模式的身影,那我们就顺着这个思路来模拟实现一下take函数。
这里我们主要是模拟实现sagaMiddleware.run和take这两个函数。实现之前我们先了解下两个概念:
先实现一个task:
下面分析一下task函数的执行过程:
接下来的主要工作是实现runTakeEffect,这个函数的作用主要有两个:
由于这里需要对迭代器做存储,所以这里还需要引入saga中的另外一个概念:
runTakeEffect的实现:
解析一下runTakeEffect的逻辑:
现在一个大体的take函数基本实现完毕了,我们来模拟用户的操作事件来测试一下:
执行结果如下:
我们触发了两次chan.put,但实际上监听函数只执行了一次,这是由于在第一次执行完后就被销毁了。
saga提供了一个takeEvery的API,作为对take的补充,不同于take监听的一次性有效,它对action的监听是始终有效的,其基本使用方法如下:
可以看到,takeEvery有两个参数,第一个参数是监听的action,第二个参数是监听的执行函数
实际上,在实现takeEvery之前,我们可以使用一个无限循环的take来模拟下takeEvery,达到无限监听的目的:
原理很简单,就是使用一个死循环来实现,当上一个taker执行完并被销毁后,此时会再次进入下次迭代循环:
再次执行next,又到yield take('start')这句,再次向chan中添加监听方法,重复之前的过程,达到持续监听的目的。
了解了takeEvery要做到的事情,接下来就模拟实现一下。在这之前,还需要再了解一下saga提供的另外一个API:
接下来就是要重点实现task的部分:
修改后的task还是比较绕的,我们来分析一下它的执行过程:
最后,加入如下测试方法,会发现每次执行chan.put都实现了监听:
至此,就实现了takeEvery方法,现在小总结一下:
到此,对effect有一些认识了,这里对effect总结一下,先看一下官方的解释:
An effect is a plain JavaScript Object containing some instructions to be executed by the saga middleware.
意思是,effect是一个纯对象,包含一些指令信息,这些指令会被middleware解释执行。
通过上面的两个例子,我们已经认识到了两个effect,一个是take,一个是frok,它们实际上都是一个对象,相当于给执行器task发送指令,task通过判断effect的不同类型知道要处理什么样的任务。这就是task的本质。
所以基于这种通过effect给task发送指令的做法,我们再来模拟实现如call、cancle等API,实际上,saga中提供的effects中的API也是这么做的。
fork与call
在上面实现takeEvery的过程中,我们实现了一个辅助函数fork,fork的作用是启动一个新的task实现无阻塞调用。与之相对的是另外一个effect叫call,call的作用也是调用有副作用的函数,它是阻塞调用的。它的基本用法如下:
如上所述,调用yield call后,函数将被阻塞住,2秒过后才会继续向下执行到console语句
它的实现也是比较简单的,我们需要判断一下,call调用的函数返回的结果,如果是promise,就等其结果resolve后再继续执行迭代器的next方法,如果不是promise,就直接调用next方法继续迭代器的执行。
首先实现一下call,和fork类似,它也是返回描述effect的纯对象的函数:
然后再改造一下task函数,需要变动的地方就是在判断effect的类型的条件中增加一项:
最后再实现runCallEffect函数:
到此,一个基本的call方法就完成了。
需要提醒的是,此处我们使用了call直接调用了delay方法call(delay),这是没有问题的,但我们自己在上文中实现的fork却不能直接这么调用,这是由于fork是重启了一个task,而task的参数我们设定的只能是iterator和generator函数,因此fork的参数不能是一个基本的函数,所以在我们的fork版本中,需要这么来调用:
这里我们传递给fork的是一个generator函数,在此方法中再调用delay方法。
实际上,在saga的使用中,我们传给fork的函数不必是一个generator函数,可以是一个普通的函数,这是由于saga在runForkEffect函数中做了兼容处理,它会判断函数参数的返回值是否为一个iterator,如果不是则转化为一个iterator,这里我们为了方便描述,未做此兼容。saga做的兼容代码如下:
以上是对call和fork的使用简单的对比,并且基本能了解到阻塞与非阻塞调用的实现的原理。
在使用场景上,fork的非阻塞特性更适用于后台不影响主流程的代码(比如后台打点/开启监听等),这往往是加快页面渲染的一种方式。而call更适用于需要立即获取结果的异步调用,将异步的回调转为同步的写法,这就和async、await很类似,关于generator与async、await的联系可以参考我之前的这篇 文章。
cancle
saga还提供了一个很实用的effect类型即cancel,它可以取消正在运行中的task,saga在取消task后还做了两个工作,一个是取消saga中还未完成的逻辑,另一个是可以执行清理操作,以保持前后状态的一致性,比如异步操作中的loading状态。
先看一下它是如何使用的吧:
这是官网提供的一个例子,在这个saga中实现的功能是,fork一个登陆的方法authorize,同时监听用户的剩余操作,正常情况下,登陆流程走完会执行try中的语句,如果登陆出现异常会执行catch语句,这两个流程属于正常流程。
如果在登陆的过程中,用户发起了取消操作,saga检测到发起的action的type是LOGOUT,此时就取消掉fork发起的task,authorize进入到finally语句,并通过yield cancelled()判断是否是取消操作,此时再做取消操作的特殊处理
取消任务的操作实际上使用的场景并不是很多,主要是给用户撤销或是反悔的操作,弥补用户的误操作。下面简析一下实现原理
如官网所述,若要取消正在运行的任务,middleware将调用底层Generator对象上的return。这将取消任务中的当前Effect,并跳转至finally区块(若有定义的话)。
先设定一下我们要达到的目的:
在这个例子中,先使用fork来发起一个task,在这个task中2秒后会打印出sleep_done,然后监听了用户发起的action,如果action是cancle,就执行cancle取消掉task,最终打印出cancle_end。
然后在调用中,我们在1秒后发起了cancel的action,此时由于fork的task还未完成,所以将被取消掉,task中的consle语句将不被执行到。
接下来的任务就是修改task方法和创建一个cancel方法。首先cancle仍然是effect类型:
然后再改造task方法,主要是增加了两个地方:
再添加runCancleEffect方法:
在runCancleEffect中的操作很简单,就是执行了迭代器的return方法,终止迭代器的向下执行。
测试后,执行的结果符合我们的预期。
现在修改下测试例子,在3秒后再执行chan.put('cancel'):
期望的结果是,由于此时fork的task已经执行完毕,所以cancel并不能起到效果。sleep_done会被正常打印出来。
put
put的作用就是纯粹的发起一个action到store中,功能和dispatch一致。我们已经得出了实现一个effect的一般方法了,这里对于put的实现就不再展开了。当然实现put需要和redux联系起来,毕竟还需要引用其dispatch。
总结
通过上面的实践,我们大致能了解到saga的工作原理,实现的还比较的粗浅,很多场景也未考虑到,本文的主要目的是对于saga的工作原理有个认识。saga中还提供了很多强大的功能及对异常的捕获处理,这里未涉及到。