从一个简单的Loading封装来聊聊JS的AOP编程

20 篇文章 0 订阅
1 篇文章 0 订阅

第一次接触到AOP这个词的时候还是在学习Java的时候学习Spring的时候了解到的,面向切面编程,通过一些设置,我们可以在执行一个函数之前来执行其他的函数,在执行一个函数之后,执行其他的函数,常用于日志输出这些。
而在前端接触到这个名词就很偶然了,先说一下自己面临的问题。很多时候,我们在请求的时候,希望执行一个loading函数,来表示正在加载,而在请求结束后,我们希望隐藏这个loading,如果请求失败了,我们希望能输出错误信息,并且做出相应的toast提示,也就是说有。

loading.show
try{
    request
} catch(err){
    toast(err)
}
loading.hide()

这样可以给用户带来更好的体验效果。
但是,如果我们每个地方都这样写,那么很明显,我们要写的代码就太多了,而且不利于维护。这个时候我们就要去考虑一下封装了。怎么封装呢?
最简单的也是最容易想到的,我们直接封装我们的请求不就好了?我们的请求函数,就是在请求前执行loading,在请求后执行hide,然后进行统一的error处理不就好了吗?
如果你使用的是axios,那么我们有可能写出这样的代码


  request ({ method = 'post', url, data }) {
    let loadingInstance = Loading.service({
      text: '加载中'
    })
    return axios({
      url: url,
      method: method,
      data: data,
      // baseURL: 'http://localhost:8080/api' // test
      baseURL: 'http://localhost:2333/api' // prod
    })
      .then((result) => {
        return result.data
      }).catch((err) => {
        return err
      }).finally(() => {
        loadingInstance.close()
      })
  }

或者使用axios的拦截器,但是这个样子,我们又会面临另外一个问题,假如我们一部分的请求需要loading一部分的请求不需要呢?假如对同一个地址的请求,在某种情况下需要使用loading,在某种情况下不需要呢?
对于第一种。我们需要再加一个参数

request ({ method = 'post', url, data,config })

通过config.isShowLoading来进行判断。
而第二种,我们则需要在第一个的基础上再进行封装

getTest(config){
request(url,config)
}

这样问题就来了,我们现在只是针对一种情况,我们只需要写一个if-esle就好了,那如果多起来了呢?此外。request应该只是请求,而现在却又加入了显示loading的功能,这样就违背了 单一功能原则,是一种不好的设计,这种办法,解决起来很简单,但是,却也很脆弱,我们现在对这个问题进行一下升级,抛开loading跟请求,从一个更加宏观的角度来看这个问题,给出以下问题。


现在有
A
B
C
三种操作

在一部分的函数
a1,a2,a3,a4中
需要在执行这些函数前执行A函数
在b1,b2,b3函数中
需要在执行这些函数前执行 B操作

ab1,ab2,ab3前执行
AB操作

ac1,ac2,ac3前执行
AC操作

怎么实现这种形式呢


那么我们就需要进行一下分解。
before
service
after
我们一个函数执行的时候应该有这样三个步骤,以及当我们的函数执行出错的时候,我们应该有一个error组来进行处理,这样我们就可以创建一个通用的函数action,这个函数action可以接受三个回调函数组,分别表示before,service,after,这样我们就可以很灵活的进行控制了。给出函数形式。

function Action
{
before:[],
now:[],
after:[]
}
)

接下来我们要考虑的是,为什么我们需要这样一个东西吗,我们最开始的时候,不就是想处理一下loading吗?怎么感觉突然要写很多东西了???这是不是本末倒置了?为了灵活,反而忘记了自己要什么!这样我们就需要设置默认值,此外,我们可以考虑到,当我们考虑吧一个操作设置为一个函数的before这些,需要使用Action函数的时候,那么他应该是一个很常见的操作,且这个操作应该可能被很多的函数所使用到。这样我们可以考虑一下策略模式。接下来回到我们的问题,怎么设置默认值,这是一个需要考虑的问题吗?
当然,这是一个需要考虑的问题,因为我们依旧有两种解法。首先,我们都使用策略模式
得出一个策略组

let strategy={
  strategyDefalut(){

  },
  strategy2(){

  }
}

Action(actions){
if(!actions.before){
    strategy.strategyDefault()
    }
}

这里有什么问题吗?看起来好像没有问题。但是。他违背了我们设计模式中的 单一功能原则 Action 是用来处理AOP的,默认?关Action什么事情???
因此我们给出另外一种更好的解决方案,即,给出默认的actions,而不是直接在Action里面写默认值,这样还有另外一个好处,我们可以多个策略组合成一个新的策略,我们可以有多种试用与不同情况的actions策略对象,这样我们就可以应对不同的情景,比如loading情景,比如埋点情景。
最后,给出一版简单版本的Action实现


let strategy={
  strategy1(text='strategy1'){
    console.log(text);
  },
  strategy2(text='strategy2'){
    console.log(text);
    console.log(text);
    console.log(this.text);
  },
  strategy3(text='strategy3'){
    console.log(text);
    console.log(text);
    console.log(text);
  },
}

// 执行器
function actuator(obj){
  if(obj){
    if(typeof obj === 'function'){
      obj()
    }
    if(obj instanceof Array){
      obj.forEach(fn => {
        if(typeof fn === 'function'){
          fn()
        }
      });
    }
  }
}
function Action(actions){
  return function(){
    try{
      // 执行before
      actuator(actions.before)
      // 执行 service
      actuator(actions.service)
      // 执行after
      actuator(actions.after)
    } catch(err){
      actuator(actions.error)
    }
  }
}

let defalutActionsObj={
  before:[strategy.strategy1('before 执行')],
  service:[strategy.strategy2.bind({text:'this text'},'bind strategy2'),strategy.strategy2('service')],
  after:strategy.strategy3
}

let action1=Action(defalutActionsObj)

action1()

给出演示地址(可能失效)
https://codepen.io/aboyl/pen/YOYpgZ

当然,也有问题,我们对比Spring的,虽然挺久没有接触过Spring了,不过我记得Spring里面的AOP好像只需要加一个注解,然后可以进行统一的管理,此外,我们最少会需要一个回调函数,多少会有点抵触,写起来有的时候还会觉得麻烦。感觉还可以进一步优化,这就需要具体用起来再总结了。

PS:
感觉设计模式真的很有趣,这部分的东西还是跟群里面的人讨论的后,最终得出了一个感觉还不错的解决方案,群内的老兄提到了代理模式跟拦截器,但是自己感觉对设计模式的理解还不够,相对来说AOP更能让我理解,这里就使用了这样的一种说法,感觉这样一些设计模式的总结,确实能很大程度上的提高自己的工作效率,改变认知,确实很有趣

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值