AOP与设计模式以及Vue

本文探讨了在Vue中使用AOP遇到的this执行问题,由于Vue实例对data的代理,导致在methods中使用箭头函数时无法正确获取数据。提出了通过包装和使用call绑定this的解决方案。同时,作者引入了Java中的AOP注解和装饰器模式,指出在ES7/8中的装饰器提案并不完全适用于Vue的组件,因为它们针对的是类和类方法。尽管如此,通过TypeScript在Vue中使用类可以利用装饰器实现AOP,但由于装饰器在对象字面量中的支持尚不成熟,目前推荐使用回调函数形式。作者从这个问题的研究中,学习到了很多,并发现了设计模式在实际开发中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

我们在上节中提到了,我们的代码实现还是存在问题,这个问题在哪里?

这个是需要去结合vue来看的,因为我写这个东西的时候就是希望在vue中使用,如果最后不能再vue中使用
那么就没有意义了。

而在vue中的使用的实际使用的时候给我出了一个难题,那就是vue的this的执行问题。我们知道vue对自己的实例是做了一层代理的。
我们在创建一个vue实例的时候,走的是data:{属性} 但是实际上我们可以通过this.属性 拿到这个属性值。这就说明了vue的实例对data做了一次代理,使得我们可以快速的拿到值。
因此,我们在使用写methods中的方法的时候,是不能使用箭头函数的,因为如果使用了箭头函数,那么this就会指向当前的环境,最终导致我们获取不到数据

而在使用AOP的时候,我们并没有去处理this的执行问题,因此如果我们在vue中使用了我们的Action

<script>
let strategy={
  strategy1(text='strategy1'){
    console.log(text); // eslint-disable-line
  },
  strategy2(text='strategy2'){
    console.log(text); // eslint-disable-line
  },
  strategy3(text='strategy3'){
    console.log(text); // eslint-disable-line
  },
}

async function actuator (obj) {
  if (obj) {
    if (typeof obj === 'function') {
      await obj()
    }
    if (obj instanceof Array) {
      for (let fn of obj) {
        if (typeof fn === 'function') {
          await fn()
        }
      }
    }
  }
}
function Action (actions) {
  return async function () {
    try {
      // 执行before
      await actuator(actions.before)
      // 执行 service
      await actuator(actions.service)
      // 执行after
      await actuator(actions.after)
    } catch (err) {
      console.error(err);// eslint-disable-line
      actions.error(err)
    }
  }
}
function operateAction (actions, params) {
  let defaultParams = {
    p1: '默认p1', p2: '默认p2', p3: '默认p3', 
  }
  Object.assign(defaultParams, params)
  let obj = {
    before:[strategy.strategy1(defaultParams.p1)],
    service:actions,
    after:strategy.strategy3.bind(null,defaultParams.p3),
    error:strategy.strategy3.bind(null,'错误')
  }
  if (actions instanceof Function) {
    obj.service = actions
  } else if (actions instanceof Object) {
    obj = actions
  }
  return Object.assign({}, obj)
}
export default {
  name: 'HelloWorld',
  props: {
    msg: String
  },
  data: function () {
    return {
      test: 'test'
    };
  },
  methods: {
    alert: Action(operateAction(()=>{
      console.log(this.test); // eslint-disable-line
    }))
  }
}
</script>

以上代码中 最终会走到错误处理阶段
错误代码为
TypeError: Cannot read property ‘data’ of undefined

要解决这个问题其实很简单

    alert () {
      Action(operateAction( ()=> {
        console.log(this.test); // eslint-disable-line
      }))()
    }

我们可以再包裹一层。
但是 这样有点丑。。。虽然他可以很好的进行问题的处理,但是我还是觉得这种做法很丑。。。
那么我们换第二种方式

async function actuator (obj) {
  if (obj) {
    if (typeof obj === 'function') {
      await obj.call(this)
    }
    if (obj instanceof Array) {
      for (let fn of obj) {
        if (typeof fn === 'function') {
          await fn.call(this)
        }
      }
    }
  }
}
function Action (actions) {
  return async function () {
    try {
      // 执行before
      await actuator(actions.before)
      // 执行 service
      await actuator.call(this,actions.service)
      // 执行after
      await actuator(actions.after)
    } catch (err) {
      console.error(err);// eslint-disable-line
      actions.error(err)
    }
  }
}

使用call来强行绑定this,然后再取消箭头函数

alert: Action(operateAction(function (){
      console.log(this.test); // eslint-disable-line
    }))

这样就可以做到一种比较优美的使用了。

那么 还有没有更加优雅的实现方式呢?
我们想想Java中的AOP是怎么做的?
是通过@ 这个的注解来实现的
同时 我们观察我们的代码,很容易就会想到一种设计模式 装饰器模式
而在ES7 ES8中 装饰器也作为一个提案出现了,也有了 @ 这种操作。
但是我们研究了下文档
发现了问题所在,装饰器针对的 只是类已经类的方法
而vue中 很明显组件不是类的使用 没有class关键字。
这么说我们就不能用最优雅的方式来实现了吗?
不见得 因为有ts vue的ts的写法 就大部分的都是用类来实现的了 那么我们给出一个例子

function log(target: any, name: String,descriptor:any) {
  let oldValue:Function=descriptor.value
  descriptor.value=function(){
    alert('log')
    console.log(target);
    oldValue.call(this)
  }
}

@Component
export default class HelloWorld extends Vue {
  @Prop()
  private msg!: string
  test: String = 'test'

  @log
  alert() {
    alert(this.test);
  }

  mounted() {
    // this.alert()
    console.log('mounted');
  }
}
</script>

这里给出了的是一个装饰器的例子,并没有在使用AOP了,意义不是很大,我们通过装饰器的写法 那么就可以把我们的AOP代码写成

@action()
service

这个样子的形式 相较回调函数,看起来就多少舒服了一点了。

不过鉴于decorator现在还不成熟 而且也不能在对象中使用(经过测试,部分babel实现可以在对象中使用,但是如果你使用的是vscode就会报错~)所以还是使用回调函数的形式吧。

但是现在在decorator的提案上,已经有人给了一个issue 希望在对象字面值中使用装饰器,考虑到这个需求应该是很合理的,所以我觉得 这个方案被纳入标准还是可以期待一下的。
添加对对象字面值的支持
可以看下上面这个链接

那么到这里,我们的AOP就算是走完了,其实在一开始思考这个问题的时候,看了很多的资料,很多都是直接在function上面实现after这些代码,总感觉不是很好的样子,然后经过跟人的讨论种种,才最终定下来一个小的解决方案吧 但是从中却学到了很多的东西,在最后看到ts的时候,突然有种被打开了新世界的大门的感觉,而且终于可以跟一些设计模式对上号了。果然解决问题的时候,还是要多想想,多思考思考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值