20230610----重返学习-问题思考-redux持久化存储-react淘系方案-iterator迭代器-generator生成器-串行发起请求的三种方案-redux中间件-redux-saga

118 阅读10分钟

day-089-eighty-nine-20230610-问题思考-redux持久化存储-react淘系方案-iterator迭代器-generator生成器-串行发起请求的三种方案-redux中间件-redux-saga

问题思考

  1. 常用为props和react-redux全局状态管理。

    • 父子常用为props,有时也用ref及用ref拿到useImperativeHandle暴露出来的api。
    • react-redux则是全局状态管理,也有一些是上下文。不过上下文一般是第三方vue组件内部使用的。
  2. 受控组件与非受控组件。

    • 受状态管控:

      • 受状态管控组件:
      • 非受状态管控组件:
    • 受路由管控:

      • 受路由管控组件:
      • 非受路由管控组件:
  3. 高阶组件是利用闭包和高阶函数来实现的,传入一个入参组件,返回的视图组件就是入参组件。

redux持久化存储

react淘系方案

  • react 淘系方案:阿里巴巴基于react封装的一套框架

    • 主要做管理系统的框架:AntdPro

    • 学习步骤

      • umi
      • dva
      • redux-saga
      • generator
      • iterator

iterator迭代器

  • iterator是一种机制或规范,并不是一个具体类。

    • 是ES6新增的规范。

      class Iterator {
        constructor(assemble) {
          this.assemble = assemble;
          this.index = -1;
        }
        next() {
          if (this.index >= this.assemble.length - 1) {
            return {
              value: undefined,
              done: true,
            };
          }
      
          let value = this.assemble[++this.index];
          let done = false;
          return {
            value,
            done,
          };
        }
      }
      let itor = new Iterator([10, 20, 30, 40]);
      console.log(`itor-->`, itor); //itor--> Iterator {assemble: Array(4), index: -1}
      console.log(`itor.next()-->`, itor.next()); // -> {value:10,done:false}
      console.log(`itor.next()-->`, itor.next()); // -> {value:20,done:false}
      console.log(`itor.next()-->`, itor.next()); // -> {value:30,done:false}
      console.log(`itor.next()-->`, itor.next()); // -> {value:40,done:false}
      console.log(`itor.next()-->`, itor.next()); // -> {value:undefined,done:true}
      console.log(`itor.next()-->`, itor.next()); // -> {value:undefined,done:true}
      
      • 上方例子中Iterator()就是一个迭代器类,而itor就是一个迭代器实例对象。
  • iterator迭代器:为各种不同的数据结构,提供统一的遍历/迭代机制

    • iterator是一个规范,也可以认为它是一种机制:

      • iterator是一个实例对象:

        • 数据结构必须具备next()方法。

          • 即iterator实例对象上一个名为next的函数属性。
        • 每一次执行next()都是为了迭代到数据中的每个成员。

        • 每一次执行next()返回一个对象:

          • value:当前迭代的这一项的值。
          • done:是否迭代完成。
      class Iterator {
        constructor(assemble) {
          this.assemble = assemble
          this.index = -1
        }
        next() {
          if (this.index === this.assemble.length - 1) {
            return {
              value: undefined,
              done: true
            }
          }
          let value = this.assemble[++this.index],
            done = false
          return {
            value,
            done
          }
        }
      }
      let itor = new Iterator([10, 20, 30, 40])
      console.log(itor.next()) //->{value:10,done:false}
      console.log(itor.next()) //->{value:20,done:false}
      console.log(itor.next()) //->{value:30,done:false}
      console.log(itor.next()) //->{value:40,done:false}
      console.log(itor.next()) //->{value:undefined,done:true}
      console.log(itor.next()) //->{value:undefined,done:true}
      

iterator示例类

class Iterator {
  constructor(assemble) {
    this.assemble = assemble;
    this.index = -1;
  }
  next() {
    if (this.index >= this.assemble.length - 1) {
      return {
        value: undefined,
        done: true,
      };
    }

    let value = this.assemble[++this.index];
    let done = false;
    return {
      value,
      done,
    };
  }
}
let itor = new Iterator([10, 20, 30, 40]);
console.log(`itor-->`, itor); //itor--> Iterator {assemble: Array(4), index: -1}
console.log(`itor.next()-->`, itor.next()); // -> {value:10,done:false}
console.log(`itor.next()-->`, itor.next()); // -> {value:20,done:false}
console.log(`itor.next()-->`, itor.next()); // -> {value:30,done:false}
console.log(`itor.next()-->`, itor.next()); // -> {value:40,done:false}
console.log(`itor.next()-->`, itor.next()); // -> {value:undefined,done:true}
console.log(`itor.next()-->`, itor.next()); // -> {value:undefined,done:true}

迭代器规范

  • 如何知道那些数据结构具备迭代器规范呢?

    • 看数据结构是否具备Symbol.iterator属性。

      • 并且该Symbol.iterator属性的属性值是个函数。
    • 常用的具备迭代器规范的数据结构:

      • 数组。

        let arr = [10,20,30]
        
      • 部分伪数组,例如:arguments、HTMLCollection元素集合、NodeList节点集合…

      • 字符串。

        • 这个是它的包装对象上有,字符串本身是基础数据类型,并没有键值对这些概念。
      • Set/Map

    • 普通对象是不具备迭代器规范的。

    • 而具备迭代器规范的数据结构,可以使用for/of循环。

      • for-of循环只能获取获取数据的成员值。
  • for-of循环的底层机制:

    • for-of 循环,只能获取数据的成员值。

    • 基于for-of迭代一个数据结构的时候,首先调用数据结构的Symbol.iterator方法。

      • 如果不具备这个方法,则直接报错,说明不具备迭代器规范。
    • 执行Symbol.iterator方法,会获取到一个iterator对象,iterator对象中具备next()方法。

    • 每一轮循环都相当于在执行Symbol.iterator方法返回的iterator对象中的next()方法,而next()方法返回的是一个具备value/done的对象。

      • 把获取的 value 属性值,赋值给 value 这个变量。

        • 即把next()方法返回的对象的value属性值,赋值给当前for-of循环中的循环值。
      • 根据done的值是true/false,决定循环是否继续。

          • 即根据next()方法返回的对象的done属性值是否为false,决定是否进行下一轮次的for-of循环。
      let arr = [10, 20, 30]
      for (let value of arr) {
        console.log(value) //10 20 30
      } 
      
      /*let itor = arr[Symbol.iterator]()
        第一轮循环 
          itor.next() -> {value:10,done:false}
        第二轮循环 
          itor.next() -> {value:20,done:false}
        第三轮循环 
          itor.next() -> {value:30,done:false}
        第四轮循环 
          itor.next() -> {value:undefined,done:true}
      */
      
      let arr = [10, 20, 30];
      arr[Symbol.iterator] = function iterator() {
        // this:arr;
        let self = this;
        let index = -1;
        return {
          next() {
            console.log(`每一轮次for-of循环时`,);
            
            if (index >= self.length - 1) {
              return {
                value: undefined,
                done: true,
              };
            }
            let value = self[++index];
            let done = false;
            return {
              value,
              done,
            };
          },
        };
      };
      // let itor = arr[Symbol.iterator]//
      for (let value of arr) {
        console.log(`value-->`, value); //10 ;//20; //30;
      }
      

重写数组中的Symbol.iterator属性

let arr = [10, 20, 30];
arr[Symbol.iterator] = function iterator() {
  // this : arr
  let self = this,
    index = -1;
  return {
    next() {
      //console.log(`每一轮次for-of循环时: ${index}`);
      if (index === self.length - 1) {
        return {
          value: undefined,
          done: true,
        };
      }
      let value = self[++index],
        done = false;
      return {
        value,
        done,
      };
    },
  };
};
// let itor = arr[Symbol.iterator]()
for (let value of arr) {
  console.log(value); //10 20 30
}

普通对象与for-of循环

  • 面试题:普通对象是否可以使用 for/of 循环进行迭代?如果不行是为啥?如何让其可与基于 for/of 处理?

    • 普通对象不能使用for/of 循环进行迭代。

      let obj = {
        name: "哈哈哈",
        age: 15,
        [Symbol("AA")]: 100,
      };
      Object.defineProperty(obj, "sex", {
        value: "男",
        // enumerable: false,
        // configurable: false,
        // writable: false
      });
      
      • 原因:普通不具备迭代器规范,也就是不具备 Symbol.iterator 这个方法。

        let obj = {
          name: "哈哈哈",
          age: 15,
          [Symbol("AA")]: 100,
        };
        Object.defineProperty(obj, "sex", {
          value: "男",
          // enumerable: false,
          // configurable: false,
          // writable: false
        });
        for (let value of obj) { //Uncaught TypeError: obj is not iterable
          console.log(value)
        } 
        // 原因:普通不具备迭代器规范,也就是不具备 Symbol.iterator 这个方法。
        
      • 解决办法:手动为其设置一个 Symbol.iterator。

        // 面试题:普通对象是否可以使用 for/of 循环进行迭代?如果不行是为啥?如何让其可与基于 for/of 处理?
        let obj = {
          name: "哈哈哈",
          age: 15,
          [Symbol("AA")]: 100,
        };
        Object.defineProperty(obj, "sex", {
          value: "男",
          // enumerable: false,
          // configurable: false,
          // writable: false
        });
        
        // 解决办法:手动为其设置一个 Symbol.iterator
        Object.prototype[Symbol.iterator] = function iterator() {
          // 获取对象所有的私有成员
          let self = this,
            keys = Reflect.ownKeys(self),
            index = -1;
          return {
            next() {
              if (index === keys.length - 1) {
                return { value: undefined, done: true };
              }
              let value = self[keys[++index]],
                done = false;
              return { value, done };
            },
          };
        };
        for (let value of obj) {
          console.log(value);
        }
        

generator生成器

普通函数

function fn(){
  return 10
}
console.log(`fn()-->`, fn());//10

generator生成器函数

  • 创建函数,并且加一个*号,则函数为生成器函数。

    • 生成器函数只能基于function关键字创建。
// 创建函数,并且加一个*号,则函数为生成器函数。
function* fn(){
  return 10
}
console.log(`fn()-->`, fn());//看似把函数执行,但是函数并没有执行。最起码函数体中的代码并没有执行,返回结果是:一个具备迭代器规范的对象(迭代器对象),具备next/throw/return三个方法。

generator规则

  • 生成器函数函数体中的yield与return。

    //生成器函数执行的时候,函数体中的代码并没有执行,只是返回一个迭代器对象。只有通过返回的迭代器对象,基于next方法执行的时候,函数体中的代码才会自上而下执行,而且遇到yield或return结束一次运行!
    // yield与return后面的值,就是返回对象中value属性的值。
    // 对象中的done属性是:遇到yield是false,遇到return是true。
    function* fn() {
      console.log(`A`);
      yield 10;
      console.log(`B`);
      yield 20;
      console.log(`C`);
      return 30;
    }
    let itor = fn();
    // console.log(`itor-->`, itor);
    console.log(`itor.next()-->`, itor.next()); //输出`A` ; 返回: {value:10,done:false}
    console.log(`itor.next()-->`, itor.next()); //输出`B` ; 返回: {value:20,done:false}
    console.log(`itor.next()-->`, itor.next()); //输出`C` ; 返回: {value:30,done:true}
    console.log(`itor.next()-->`, itor.next()); //返回: {value:undefined,done:true}
    // 生成器函数的作用:可以基于yield和迭代器规范,控制函数体中的代码一步步的去执行。
    
  • 生成器函数的作用:可以基于yield和迭代器规范,控制函数体中的代码一步步的去执行。

  • 返回的迭代器的throw函数。

    function* fn() {
      console.log(`A`);
      yield 10;
      console.log(`B`);
      yield 20;
      console.log(`C`);
    }
    let itor = fn();
    console.log(`itor.next()-->`, itor.next()); //输出`A` ; 返回: {value:10,done:false}
    console.log(`itor.throw("哈哈哈")-->`, itor.throw("哈哈哈")); //直接抛出异常错误,错误原因是"哈哈哈",后续代码都不会再执行。
    console.log(`itor.next()-->`, itor.next()); //这行代码也不再执行。
    
  • 返回的迭代器的return函数。

    function* fn() {
      console.log(`A`);
      yield 10;
      console.log(`B`);
      yield 20;
      console.log(`C`);
    }
    let itor = fn();
    console.log(`itor.next()-->`, itor.next()); //输出`A` ; 返回: {value:10,done:false}
    console.log(`itor.return("哈哈哈")-->`, itor.return("哈哈哈")); //说明函数执行已经结束,函数体中的后续代码都不会执行。本次返回是{value:"哈哈哈",done:true},后续代码都不会再执行。
    console.log(`itor.next()-->`, itor.next()); //返回{value:undefined,done:true}
    

async-await语法的示例

const AsyncFunction = function AsyncFunction(generator, ...params) {
    return new Promise(resolve => {
        // generator:需要管控处理的生成器函数  params:以后执行 generator ,为其传递的实参
        let itor = generator(...params)
        // 递归一次次的执行 next ,控制生成器函数中的代码一步步执行
        const recursion = (x) => {
            let { value, done } = itor.next(x)
            if (done) {
                // 生成器函数执行完毕,结束递归操作,value就是生成器函数最后的返回值
                resolve(value)
                return
            }
            if (!(value instanceof Promise)) value = Promise.resolve(value) //确保每一次yield后面的值都是promise实例
            value.then(x => {
                // 等待上一步处理成功(x是成功的结果),则开启下一步的执行
                recursion(x)
            })
        }
        recursion()
    })
}

next函数中入参

// yiled 后面的值:作为本次next执行,返回对象中的value的属性值。
// next函数传递的值:作为上一次yield执行的返回结果。或者说,yield执行的返回结果,是下一次执行next,传递的实参信息,所以第一次执行next传递的值是没有用的。
// 形参接收的值:还是生成器函数执行的时候传递的值,和next执行传递的值没关系。
function* fn(x,y) {
  console.log(x,y);
  console.log(`A`);
  let n = yield 10;
  console.log(`B`,n);
  let m = yield 20;
  console.log(`C`,m);
}
let itor = fn(100,200);
console.log(`itor.next(1)-->`, itor.next(1)); //输出`100,200`/`A`; 返回{value:10,done:false}
console.log(`itor.next(2)-->`, itor.next(2)); //输出`B,2`;{value:20,done:false}
console.log(`itor.next(3)-->`, itor.next(3)); //输出`C,3`;{value:undefined,done:true}

串行发起请求的三种方案

redux中间件

redux-thunk

  • redux-thunk中间件

    • 底层机制上处理起来非常麻烦「非常绕」
    • 函数中的操作全部有开发者自行决定,不方便做统一的智能测试
  • 步骤:

redux-saga

  • redux-saga 是另外一个处理异步派发的中间件,从各方面都比 redux-thunk 更好一些,所以推荐大家使用 redux-saga 中间件。

redux-saga思路

  • redux-saga不同于redux-thunk或redux-promise中间件。

    1. 先到redux-thunk或redux-promise中间件,之后通过真正的dispatch到reducer。

    • 即便使用了redux-saga中间件,每一次组件中的dispatch派发,都会有一条线,直接到reducer中,多出一条线,会进入到中间件中!
  • 每一次在组件中派发的时候,派发的行为标识这样处理:

    • 如果是同步:让派发的标识和reducer中的一致,和saga中的不一致。

    • 如果是异步:让派发的标识和saga中监听的标识一致,和reducer中的标识不一致!

      • 等待saga中异步处理完毕,在saga中再次派发一个和reducer中判断一致的标识即可!
  • 真实项目中,reducer里面按照标识统一管理文件中管理的标识进行判断,saga中监听的标识可以在标识统一管理文件中标识的后面,统一设置@saga即可。

redux-saga步骤

面试题

  • 面试官问的是ES6。

    • 快速介绍一些常用的:

      • let/const/箭头函数/解构赋值/.../模板字符串
      • 数组对象中新增了一些方法。
    • 重点突出一些高级点的:

      • Promise/async/await : 处理同步异步。
      • Reflect:基础语法如delete-*/之类操作的函数化。
      • Fetch:新的前后端交互。
      • Proxy:代理。
      • iterator:迭代器。
      • generator:生成器。
      • ES6 Module:模块化,导入与导出。
  • 面试题:普通对象是否可以使用 for/of 循环进行迭代?如果不行是为啥?如何让其可与基于 for/of 处理?

    • 普通对象不能使用for/of 循环进行迭代。

      • 原因:普通不具备迭代器规范,也就是不具备 Symbol.iterator 这个方法。
      • 解决办法:手动为其设置一个 Symbol.iterator。

进阶参考

  1. redux-persist
  2. Ant Design Pro
  3. co.js - 用生成器函数与promise模拟async与await语法