前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片NEW
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 业务场景题真实业务问题与追问
  • 查漏补缺常见问题解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
    • AI 定制路线NEW按你的简历现排
    • AI 知识地图NEW串起全站知识点
  • 动态
    • AI 热点NEWAI 每日动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
AI 助手NEW
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片NEW
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 业务场景题真实业务问题与追问
  • 查漏补缺常见问题解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
    • AI 定制路线NEW按你的简历现排
    • AI 知识地图NEW串起全站知识点
  • 动态
    • AI 热点NEWAI 每日动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
AI 助手NEW
旧版
  • JavaScript Part 1

    • Ajax总结篇
    • Canvas 绘制八大行星
    • DOM编程之API学习总结篇
    • JavaScript事件机制
    • JavaScript代码片段100个
    • JavaScript作用域分析总结
    • JavaScript原型链回顾
    • JavaScript及jQuery中的各种宽高属性图解
    • JavaScript工程项目的一系列最佳实践
    • JavaScript常用API合集
    • JavaScript数组、字符串、对象常用方法
    • JavaScript数组方法总结篇
    • JavaScript深浅拷贝
    • JavaScript运动框架之速度时间版本
    • JavaScript运行机制Event Loop
    • JavaScript防抖节流原理
    • Javascript中的复制粘贴功能
    • Javascript数组详解
    • OOP之原型与原型链
    • Object.defineProperty详解
    • V8源码浅析JS数组常见方法
    • await 在 forEach 中不生效解决方案
    • iframe+表单跨域提交POST请求
    • javascript笔记总结篇
    • 业务中处理数据结构常用的JS方法
    • 作用域
    • 你真的掌握变量和类型了吗
    • 前后端分离之数据Mock
    • 原型与原型链
    • 原生JS补给(上)
    • 如何写出一个惊艳面试官的深拷贝
    • 带你填一些JS容易出错的坑
    • 彻底弄懂 JavaScript 执行机制
    • 执行上下文 执行栈
    • 正则回顾总结
    • 正则基础知识
    • 正则完整篇
    • 正则表达式
    • 浅析Promise原理
    • 浅谈JavaScript中的异步处理
    • 深拷贝 vs 浅拷贝
    • 聊一聊typeof instanceof 实现原理.
    • 聊一聊闭包
    • 高阶函数map reduce filter
  • JavaScript Part 2

  • CSS

  • HTML

  • Jquery

  • ES6

  • 小程序

  • Vue

  • React

  • 深入React

  • React Native

  • NodeJS

  • Angular

  • TypeScript

  • Webpack

  • 浏览器

  • 移动端

  • 前端工程化

  • Electron

  • HTTP

  • Nginx

  • Linux

  • 数据结构与算法

  • LeetCode算法题

  • 综合

完整面试题地址:
作者:程序员poetry
扫码关注作者公众号:「前端进阶之旅」 每天分享技术干货
前端进阶之旅公众号二维码

浅析redux saga中间件及用法|博客系列

# 一、redux-thunk

# 1.1 redux的副作用处理

redux中的数据流大致是

UI—————>action(plain)—————>reducer——————>state——————>UI
@前端进阶之旅: 代码已经复制到剪贴板

  • redux是遵循函数式编程的规则,上述的数据流中,action是一个原始js对象(plain object)且reducer是一个纯函数,对于同步且没有副作用的操作,上述的数据流起到可以管理数据,从而控制视图层更新的目的
  • 如果存在副作用函数,那么我们需要首先处理副作用函数,然后生成原始的js对象。如何处理副作用操作,在redux中选择在发出action,到reducer处理函数之间使用中间件处理副作用

redux增加中间件处理副作用后的数据流大致如下:

UI——>action(side function)—>middleware—>action(plain)—>reducer—>state—>UI
@前端进阶之旅: 代码已经复制到剪贴板

在有副作用的action和原始的action之间增加中间件处理,从图中我们也可以看出,中间件的作用就是:

  • 转换异步操作,生成原始的action,这样,reducer函数就能处理相应的action,从而改变state,更新UI

# 1.2 redux-thunk源码

在redux中,thunk是redux作者给出的中间件,实现极为简单,10多行代码

function createThunkMiddleware(extraArgument) {
  return ({ dispatch, getState }) => next => action => {
    if (typeof action === 'function') {
      return action(dispatch, getState, extraArgument);
    }

    return next(action);
  };
}

const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;

export default thunk;
@前端进阶之旅: 代码已经复制到剪贴板

这几行代码做的事情也很简单,判别action的类型,如果action是函数,就调用这个函数,调用的步骤为

action(dispatch, getState, extraArgument);
@前端进阶之旅: 代码已经复制到剪贴板

发现实参为dispatch和getState,因此我们在定义action为thunk函数是,一般形参为dispatch和getState

# 1.3 redux-thunk的缺点

thunk的缺点也是很明显的,thunk仅仅做了执行这个函数,并不在乎函数主体内是什么,也就是说thunk使得redux可以接受函数作为action,但是函数的内部可以多种多样。比如下面是一个获取商品列表的异步操作所对应的action

export default ()=>(dispatch)=>{
    fetch('/api/goodList',{ //fecth返回的是一个promise
      method: 'get',
      dataType: 'json',
    }).then(function(json){
      var json=JSON.parse(json);
      if(json.msg==200){
        dispatch({type:'init',data:json.data});
      }
    },function(error){
      console.log(error);
    });
};
@前端进阶之旅: 代码已经复制到剪贴板

从这个具有副作用的action中,我们可以看出,函数内部极为复杂。如果需要为每一个异步操作都如此定义一个action,显然action不易维护

action不易维护的原因

  • action的形式不统一
  • 就是异步操作太为分散,分散在了各个action中

# 二、redux-saga 简介

redux-saga 是一个 redux 中间件,它具有如下特性

  • 集中处理 redux 副作用问题。
  • 被实现为 generator 。
  • 类 redux-thunk 中间件。
  • watch/worker(监听->执行) 的工作形式

redux-saga的优点

  • 集中处理了所有的异步操作,异步接口部分一目了然
  • action是普通对象,这跟redux同步的action一模一样
  • 通过Effect,方便异步接口的测试
  • 通过worker 和watcher可以实现非阻塞异步调用,并且同时可以实现非阻塞调用下的事件监听
  • 异步操作的流程是可以控制的,可以随时取消相应的异步操作

基本用法

  • 使用createSagaMiddleware方法创建saga 的Middleware,然后在创建的redux的store时,使用applyMiddleware函数将创建的saga Middleware实例绑定到store上,最后可以调用saga Middleware的run函数来执行某个或者某些Middleware。
  • 在saga的Middleware中,可以使用takeEvery或者takeLatest等API来监听某个action,当某个action触发后,saga可以使用call发起异步操作,操作完成后使用put函数触发action,同步更新state,从而完成整个State的更新。

# 三、redux-saga使用案例

  • redux-saga是控制执行的generator,在redux-saga中action是原始的js对象,把所有的异步副作用操作放在了saga函数里面。这样既统一了action的形式,又使得异步操作集中可以被集中处理
  • redux-saga是通过genetator实现的,如果不支持generator需要通过插件babel-polyfill转义。我们接着来实现一个输出hellosaga的例子

创建一个helloSaga.js文件

export function * helloSaga() {
  console.log('Hello Sagas!');
}
@前端进阶之旅: 代码已经复制到剪贴板

在redux中使用redux-saga中间件

在main.js中

import { createStore, applyMiddleware } from 'redux'
import createSagaMiddleware from 'redux-saga'
import { helloSaga } from './sagas'
const sagaMiddleware=createSagaMiddleware();
const store = createStore(
 reducer,
 applyMiddleware(sagaMiddleware)
);
sagaMiddleware.run(helloSaga);
//会输出Hello, Sagas!
@前端进阶之旅: 代码已经复制到剪贴板

和调用redux的其他中间件一样,如果想使用redux-saga中间件,那么只要在applyMiddleware中调用一个createSagaMiddleware的实例。唯一不同的是需要调用run方法使得generator可以开始执行

# 四、redux-saga使用细节

# 4.1 声明式的Effect

在redux-saga中提供了一系列的api,比如take、put、all、select等API,在redux-saga中将这一系列的api都定义为Effect。这些Effect执行后,当函数resolve时返回一个描述对象,然后redux-saga中间件根据这个描述对象恢复执行generator中的函数

redux-thunk的大体过程

action1(side function)—>redux-thunk监听—>执行相应的有副作用的方法—>action2(plain object)

转化到action2是一个原始js对象形式的action,然后执行reducer函数就会更新store中的state

redux-saga的大体过程

action1(plain object)——>redux-saga监听—>执行相应的Effect方法——>返回描述对象—>恢复执行异步和副作用函数—>action2(plain object)

对比redux-thunk我们发现,redux-saga中监听到了原始js对象action,并不会马上执行副作用操作,会先通过Effect方法将其转化成一个描述对象,然后再将描述对象,作为标识,再恢复执行副作用函数

# 4.2 Effect提供的具体方法

下面来介绍几个Effect中常用的几个方法,从低阶的API,比如take,call(apply),fork,put,select等,以及高阶API,比如takeEvery和takeLatest等

import {take,call,put,select,fork,takeEvery,takeLatest} from 'redux-saga/effects'
@前端进阶之旅: 代码已经复制到剪贴板

# 4.2.1 take

take这个方法,是用来监听action,返回的是监听到的action对象。比如

const loginAction = {
   type:'login'
}
@前端进阶之旅: 代码已经复制到剪贴板

在UI Component中dispatch一个action

dispatch(loginAction)
@前端进阶之旅: 代码已经复制到剪贴板

在saga中使用:

const action = yield take('login');
@前端进阶之旅: 代码已经复制到剪贴板

可以监听到UI传递到中间件的Action,上述take方法的返回,就是dipath的原始对象。一旦监听到login动作,返回的action为:

{
  type:'login'
}
@前端进阶之旅: 代码已经复制到剪贴板

# 4.2.2 call(apply)

call和apply方法与js中的call和apply相似,我们以call方法为例

call(fn, ...args)
@前端进阶之旅: 代码已经复制到剪贴板

call方法调用fn,参数为args,返回一个描述对象。不过这里call方法传入的函数fn可以是普通函数,也可以是generator。call方法应用很广泛,在redux-saga中使用异步请求等常用call方法来实现

yield call(fetch,'/userInfo',username)
@前端进阶之旅: 代码已经复制到剪贴板

# 4.2.3 put

redux-saga做为中间件,工作流是这样的

UI——>action1————>redux-saga中间件————>action2————>reducer..
@前端进阶之旅: 代码已经复制到剪贴板

从工作流中,我们发现redux-saga执行完副作用函数后,必须发出action,然后这个action被reducer监听,从而达到更新state的目的。相应的这里的put对应与redux中的dispatch,工作流程图如下

可以看出redux-saga执行副作用方法转化action时,put这个Effect方法跟redux原始的dispatch相似,都是可以发出action,且发出的action都会被reducer监听到。put的使用方法

 yield put({type:'login'})
@前端进阶之旅: 代码已经复制到剪贴板

# 4.2.4 select

put方法与redux中的dispatch相对应,同样的如果我们想在中间件中获取state,那么需要使用select。select方法对应的是redux中的getState,用户获取store中的state,使用方法:

← 浅析React高阶组件HOCReact中key的正确使用方式 →

fe
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片NEW
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 业务场景题真实业务问题与追问
  • 查漏补缺常见问题解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
    • AI 定制路线NEW按你的简历现排
    • AI 知识地图NEW串起全站知识点
  • 动态
    • AI 热点NEWAI 每日动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
AI 助手NEW
旧版
  • JavaScript Part 1

    • Ajax总结篇
    • Canvas 绘制八大行星
    • DOM编程之API学习总结篇
    • JavaScript事件机制
    • JavaScript代码片段100个
    • JavaScript作用域分析总结
    • JavaScript原型链回顾
    • JavaScript及jQuery中的各种宽高属性图解
    • JavaScript工程项目的一系列最佳实践
    • JavaScript常用API合集
    • JavaScript数组、字符串、对象常用方法
    • JavaScript数组方法总结篇
    • JavaScript深浅拷贝
    • JavaScript运动框架之速度时间版本
    • JavaScript运行机制Event Loop
    • JavaScript防抖节流原理
    • Javascript中的复制粘贴功能
    • Javascript数组详解
    • OOP之原型与原型链
    • Object.defineProperty详解
    • V8源码浅析JS数组常见方法
    • await 在 forEach 中不生效解决方案
    • iframe+表单跨域提交POST请求
    • javascript笔记总结篇
    • 业务中处理数据结构常用的JS方法
    • 作用域
    • 你真的掌握变量和类型了吗
    • 前后端分离之数据Mock
    • 原型与原型链
    • 原生JS补给(上)
    • 如何写出一个惊艳面试官的深拷贝
    • 带你填一些JS容易出错的坑
    • 彻底弄懂 JavaScript 执行机制
    • 执行上下文 执行栈
    • 正则回顾总结
    • 正则基础知识
    • 正则完整篇
    • 正则表达式
    • 浅析Promise原理
    • 浅谈JavaScript中的异步处理
    • 深拷贝 vs 浅拷贝
    • 聊一聊typeof instanceof 实现原理.
    • 聊一聊闭包
    • 高阶函数map reduce filter
  • JavaScript Part 2

  • CSS

  • HTML

  • Jquery

  • ES6

  • 小程序

  • Vue

  • React

  • 深入React

  • React Native

  • NodeJS

  • Angular

  • TypeScript

  • Webpack

  • 浏览器

  • 移动端

  • 前端工程化

  • Electron

  • HTTP

  • Nginx

  • Linux

  • 数据结构与算法

  • LeetCode算法题

  • 综合