AOP 模式在前端的应用

是什么?

AOP(Aspect-Oriented Programming)面向切片编程,是一种软件开发方法,旨在提高代码的模块化性和可维护性。它通过将横切关注点(cross-cutting concerns)从主要业务逻辑中分离出来,并以独立的方式处理这些关注点。

为什么?

在项目中代码不仅要实现业务需求还要完成各种与业务无关的的其他功能,比如日志埋点、异常上报。通过 AOP,开发者可以将这些关注点定义为"切面",然后在需要的地方将这些切面织入到应用程序的特定位置,而不必直接修改主要业务逻辑。

怎么做?

例子1:计算一个事件的执行时间

页面中有一个插入DOM 的事件。

js 复制代码
function appendEmpty() {
  for (let index = 0; index < 100; index++) {
    let div = document.createElement('div')
    document.body.appendChild(div)
  }
}

为了计算事件执行的时间,需要在方法内部插入计算时间的代码。

js 复制代码
function appendEmpty() {
  let start = new Date().getTime()
  for (let index = 0; index < 100; index++) {
    let div = document.createElement('div')
    document.body.appendChild(div)
  }
  let end = new Date().getTime()
  console.log(end - start)
}

代码问题

计算时间的逻辑全部插入到了源码中,不仅入侵源码,还改变了 appenEmpty 的业务职能, 如果再有其他的需求(埋点)会使函数越来越难维护。

解决方案

在全局的 Function 上挂载 before after 方法,用于在函数执行前和执行后执行。

js 复制代码
Function.prototype.before = function (beforeFn) {
  let self = this
  return function () {
    beforeFn.apply(this, arguments)
    return self.apply(this, arguments)
  }
}
 
Function.prototype.after = function (afterFn) {
  let self = this
  return function () {
    let ret = self.apply(this, arguments)
    afterFn.apply(this, arguments)
    return ret
  }
}

声明函数 fnTime 方法,内部调用 after 函数计算传入函数参数的执行时间。

js 复制代码
function appendEmpty() {
  for (let index = 0; index < 100; index++) {
    let div = document.createElement('div')
    document.body.appendChild(div)
  }
}

const fnTime = function (fn) {
  let start = new Date().getTime()
  return fn.after(function () {
    console.log(new Date().getTime() - start)
  })
}

fnTime(appendEmpty)()

例子 2: 表单提交

在表单提交时,通常会校验表单数据后进行表单提交

js 复制代码
function validate (value) {
  if (!value.length) {
    return false
  }
  if (value.length > 10) {
    return false
  }
  return true
}
 
function submit (value) {
  if (!validate(value)) return
  form.submit(value)
}
submit(formValue)

如果除了表单验证,还有操作日志上传、其他业务逻辑插入,就会导致 submit 的动作变的不那么 单纯, 可以使用 AOP 模式把提交无关的过程交给切片处理。

js 复制代码
let submit = function (value) {
  form.submit(value)
}
// 添加before 切面函数
submit.before(validate)(value)

实现切片方法

和上面的切片方法相比, 下面的切片方法多了 if(!ret) return false 的写法,这样可以在出错时阻断切片执行。

js 复制代码
// before 切片
Function.prototype.before = function (beforeFn) {
  let self = this
  return function () {
    let ret = beforeFn.apply(this, arguments)
    if(!ret) return false
    return self.apply(this, arguments)
  }
}

Function.prototype.after = function (afterFn) {
  let self = this
  return function () {
    let ret = self.apply(this, arguments)
    if(!ret) return false
    return afterFn.apply(this, arguments)
  }
}

例子 3:操作埋点

在点击 btnClick 后打开弹出框,并上传埋点日志。

js 复制代码
methods: {
   track () {
     // 添加埋点
     this.$tracker({ elementCode: 104340 })
   },
   showDialog () {
     // 业务逻辑。。。
     this.showDialog = true
   },
   btnClick () {
    // 业务逻辑。。。。。
     this.showDialog.after(this.track)()
   }
 }

使用装饰器设计 AOP

除了使用传入函数参数的方式,装饰器用来实现切片写法上会优雅些许

js 复制代码
function log(target, name, descriptor) {
    var oldValue = descriptor.value;
 
    descriptor.value = function () {
        console.log(`Calling "${name}" with`, arguments);
        return oldValue.apply(null, arguments);
    }
    return descriptor;
}
 
 
// 日志应用
class Maths {
    @log
    add(a, b) {
        return a + b;
    }
}
const math = new Maths();
// passed parameters should get logged now
math.add(2, 4);

总结

AOP 的业务应用模式不难理解,相比于 IoC 有更多的业务应用场景。 在 NestJs 框架中,中间件,拦截器,守卫,异常过滤器,管道也都是基于 AOP 设计。

AOP 应用架构

相关推荐
小村儿4 分钟前
给 AI Agent 装上"长期记忆":Karpathy 的 LLM Wiki 思想,我做成了工具
前端·后端·ai编程
竹林8189 分钟前
用ethers.js连接MetaMask实现Web3钱包登录:从踩坑到稳定运行的完整记录
前端·javascript
heyCHEEMS12 分钟前
如何用 Recast 实现静态配置文件源码级读写
前端·node.js
心连欣13 分钟前
从零开始,学习所有指令!
前端·javascript·vue.js
review4454316 分钟前
大模型和function calling分别是如何工作的
前端
东东同学17 分钟前
耗时一个月,我把 Nuxt 首屏性能排障经验做成了一个 AI Skill
前端·agent
冴羽1 小时前
超越 Vibe Coding —— AI 辅助编程指南
前端·ai编程·vibecoding
梦想的颜色2 小时前
一天一个SKILL——前端最佳自动化测试 webapp-testing
前端·web app
SoaringHeart2 小时前
Flutter进阶:放弃 MediaQuery.of(context) 使用 NScreenManager
前端·flutter
openKaka_2 小时前
从 scheduleUpdateOnFiber 到 Root 微任务调度:React 如何把更新交给调度系统
开发语言·前端·javascript