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 应用架构

相关推荐
jacGJ5 小时前
记录学习--文件读写
java·前端·学习
毕设源码-赖学姐5 小时前
【开题答辩全过程】以 基于WEB的实验室开放式管理系统的设计与实现为例,包含答辩的问题和答案
前端
幻云20105 小时前
Python深度学习:从筑基到登仙
前端·javascript·vue.js·人工智能·python
我即将远走丶或许也能高飞7 小时前
vuex 和 pinia 的学习使用
开发语言·前端·javascript
钟离墨笺8 小时前
Go语言--2go基础-->基本数据类型
开发语言·前端·后端·golang
爱吃泡芙的小白白8 小时前
Vue 3 核心原理与实战:从响应式到企业级应用
前端·javascript·vue.js
卓怡学长8 小时前
m115乐购游戏商城系统
java·前端·数据库·spring boot·spring·游戏
老陈聊架构9 小时前
『AI辅助Skill』掌握三大AI设计Skill:前端独立完成产品设计全流程
前端·人工智能·claude·skill
Ulyanov9 小时前
从桌面到云端:构建Web三维战场指挥系统
开发语言·前端·python·tkinter·pyvista·gui开发
cypking9 小时前
二、前端Java后端对比指南
java·开发语言·前端