如何利用装饰器提升开发效率
前言:
众所周知在 python
中装饰器的应用非常广泛, 在 java
中也有类似的技术。然而,在javascript
中 装饰器还处于提案阶段,但这丝毫不影响我们使用它。我司已经在生产环境上大面积使用(真香)。可以说,装饰器是我们的神器。著名的框架 Augular
中也用到了装饰器。nodejs web框架 nestjs
利用装饰器实现了和大名鼎鼎的 spring
相似的mvc框架。那么,下面我将简单介绍一下我司是如何使用装饰器来提高开发效率。如果不了解装饰器的可以看一下 阮一峰的es6
。下面的实现会依赖vue,因为我司主要使用vue。
前置
在vue中使用装饰器非常简单。
1.首先 需要通过eslint 校验 将以下集成到eslint 配置中去
parserOptions: {
parser: 'babel-eslint',
ecmaFeatures:{
// 支持装饰器
legacyDecorators: true
}
}
- 配置 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
的概念 拿到前端, 总之一句话,我们程序员就是为了懒
而生的。