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

相关推荐
腾讯TNTWeb前端团队3 分钟前
helux v5 发布了,像pinia一样优雅地管理你的react状态吧
前端·javascript·react.js
范文杰3 小时前
AI 时代如何更高效开发前端组件?21st.dev 给了一种答案
前端·ai编程
拉不动的猪4 小时前
刷刷题50(常见的js数据通信与渲染问题)
前端·javascript·面试
拉不动的猪4 小时前
JS多线程Webworks中的几种实战场景演示
前端·javascript·面试
FreeCultureBoy4 小时前
macOS 命令行 原生挂载 webdav 方法
前端
uhakadotcom5 小时前
Astro 框架:快速构建内容驱动型网站的利器
前端·javascript·面试
uhakadotcom5 小时前
了解Nest.js和Next.js:如何选择合适的框架
前端·javascript·面试
uhakadotcom5 小时前
React与Next.js:基础知识及应用场景
前端·面试·github
uhakadotcom5 小时前
Remix 框架:性能与易用性的完美结合
前端·javascript·面试
uhakadotcom5 小时前
Node.js 包管理器:npm vs pnpm
前端·javascript·面试