如何利用装饰器提升开发效率

2,057 阅读5分钟

如何利用装饰器提升开发效率

前言:

众所周知在 python装饰器的应用非常广泛, 在 java 中也有类似的技术。然而,在javascript中 装饰器还处于提案阶段,但这丝毫不影响我们使用它。我司已经在生产环境上大面积使用(真香)。可以说,装饰器是我们的神器。著名的框架 Augular中也用到了装饰器。nodejs web框架 nestjs利用装饰器实现了和大名鼎鼎的 spring 相似的mvc框架。那么,下面我将简单介绍一下我司是如何使用装饰器来提高开发效率。如果不了解装饰器的可以看一下 阮一峰的es6。下面的实现会依赖vue,因为我司主要使用vue。

前置

在vue中使用装饰器非常简单。

1.首先 需要通过eslint 校验 将以下集成到eslint 配置中去

parserOptions: {
      parser: 'babel-eslint',
      ecmaFeatures:{
          // 支持装饰器
          legacyDecorators: true
      }
}
  1. 配置 vscode vetur不支持这个语法,对于我这个强迫症来说爆红无法仍受,所以直接关掉vetur的校验
在 .vscode/settings.json 中添加一行
关闭 vetur 对script 的校验 
"vetur.validation.script": false,

1. 装饰器实现节流(防抖也一样)

在项目开发中,为了避免用户重复提交,经常会遇到按钮需要进行节流限制。(我司要求所有具有后台操作的按钮,必须加限制)。所有的按钮的点击事件都要加上这一堆防止重复提交的代码,这些代码与我们的业务逻辑毫不相关,如果每次遇到按钮,都实现一下节流,岂不是太过于繁琐了。有了装饰器,so easy。

封装装饰器:
/**
 * @param {number} ms 毫秒数
 * @description 节流装饰器
 * 使用方法: @throttle() 或者 @throttle(2000) 代表2s执行一次
 */
export function throttle(ms = 1000) {
    let prev = 0
    return function(target, name, descriptor) {
        const fn = descriptor.value
        descriptor.value = function(...args) {
            let now = Date.now()
            if (now - prev > ms) {
                fn.apply(this, args)
                prev = now
            }
        }
    }
}

就这样,一个装饰器就封装好了。接下来只需要在你的项目中用就ok了。

@throttle(2000)
login() {
	// 登陆
}

哪里需要就 @哪里,是不是很方便。

2. 装饰器实现权限校验

目前我开发的项目,系统中有多个角色,每个角色可以进行不同的操作。有的只读,有的只能对其中几个页面进行操作。这就需要我们写大量的权限校验的代码。在我们项目初期的时候,曾今就是这么干的。(都是泪)。那么有了装饰器, 我们只需要编写对应的装饰器,在需要校验的方法上添加上装饰器,就可以省去很多的代码。

封装权限校验装饰器:
/**
 * @description 装饰器, 用做权限判断
 */
export function authValid(target, name, descriptor) {
    const fn = descriptor.value
    descriptor.value = function(...args) {
        // 从localStorage中取出角色
        const type = window.localStorage.getItem('type')
        // 如果是 user类型的用户,提示用户。
        if (type === 'USER') {
            return message.warn('请联系管理员升级账户权限')
        }
        fn.call(this, ...args)
    }
}
使用:
@authValid
delete() {
	// ...
}

就这样,我们对每种权限多封装几个装饰器,就能大大提高开发效率。再也不用因为老板改变角色的权限,去改这些代码了。

3. 装饰器实现弹框的封装

我们经常需要给用户弹各种框, 警告框、校验框... 例如:我司的项目在用户进行重要操作的时候,需要弹出一个登陆框,进行密码校验,才能进行后续的操作。 下面我们 将 vue和 装饰器结合使用。

封装校验密码装饰器
// 保存我们用 vue封装好的 弹框组件
let LoginComponent = null
/**
 *  @description 登陆校验装饰器
 */
export function loginValid() {
	// 在第一次打开弹框的时候, 把 LoginComponent赋值为 我们封装好的组件。
    if (LoginCompoent == null) {
    	// 这里的 LoginValid就是我们写好的vue的组件。
        LoginCompoent = Vue.extend(LoginValid)
    }
    return function(target, name, descriptor) {
        const fn = descriptor.value
        descriptor.value = function(...args) {
            // 这里将即将执行的操作封装成回调函数, 传入 LoginComponent组件中。当用户点击登陆按钮,通过校验后,执行这个callback
            const callback = () => {
                const params = args
                return () => {
                    fn.apply(this, params)
                }
            }
            // 这里实例化 vue组件, 然后将它的dom插入到body中。
            const instance = new LoginCompoent({
                data: {
                    show: true,
                    // 将 callback赋值到 LoginComponent组件中, 然后校验成功后调用它
                    callBack: callback()
                }
            }).$mount()
            const body = document.getElementsByTagName('body')[0]
            body.appendChild(instance.$el)
        }
    }
}
使用:
@loginValid()
delete() {
	// 这里的代码在用户输入密码校验成功之后, 才会执行
}

这里利用 vue的组件化封装弹框组件, 在利用装饰器实现与业务分离(解耦合)。是不是很nice。

总结

装饰器的应用场景非常之多, 大家可以根据实际业务需求进行扩展。想想自己在调取后端接口的时候,前几行永远是开启Loading加载效果,结束的时候在关掉, 完全可以利用装饰器解放你的双手。大家可以自己去实现,基本上原理都一样。

总结一下,个人认为装饰器的好处。

  • 装饰器将杂七乱八的与业务无关的代码高度封装,与业务逻辑分离,解耦合。
  • 装饰器可以动态的给函数添加功能。

我们甚至可以将 spring中 AOP的概念 拿到前端, 总之一句话,我们程序员就是为了而生的。

参考