decorator装饰器通过对类、对象、方法、属性进行修饰,对其添加一些其他行为,即对一段代码进行二次包装。
装饰器只能用于类和类的方法,不能用于函数,因为存在函数提升。

一、使用方法:

1
2
3
4
5
6
7
const decorator = (target,name,descriptor) => {
var oldValue = descriptor.value
descriptor.value = function(){
return oldValue.apply(this,arguments)
}
return descriptor
}

使用装饰器可以不需要关注代码内部的实现,增强了代码的可读性。

vue中使用装饰器:

项目中使用eslint,需要开启装饰器相关语法的检测。

1
2
3
4
5
6
7
// .eslintrc.js
parserOptions: {
parser: 'babel-eslint',
ecmaFeatures:{
legacyDecorators: true
}
},

二、Vue项目使用Element-Ui组件库进行二次弹窗确认相关操作:

1、工具函数decorator.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// 需安装element-ui
import { MessageBox, Message } from 'element-ui'

/**
* 确认框
* @param {String} title - 标题
* @param {String} content - 内容
* @param {String} confirmButtonText - 确认按钮名称
* @param {Function} callback - 确认按钮名称
* @returns
**/


export function confirm (title, content, confirmButtonText = '确定') {
return function(target, name, descriptor) {
// target:test()
// name:test1
// descriptor: 装饰器属性
// 包括 :configurable: true
// umerable: true
// value: ƒ ()
// writable: true
const originValue = descriptor.value
descriptor.value = function(...args) {
MessageBox.confirm(content, title, {
dangerouslyUseHTMLString: true,
distinguishCancelAndClose: true,
confirmButtonText: confirmButtonText
}).then(originValue.bind(this, ...args)).catch(error => {
if (error === 'close' || error === 'cancel') {
Message.info('用户取消操作')
} else {
Message.info(error)
}
})
}
return descriptor
}
}

2、页面引入装饰器函数

1
2
3
4
5
6
7
<template>
<div class="about">
<h1>This is an about page</h1>
<p>装饰器</p>
<button @click='test'>confirm</button><button>cancel</button>
</div>
</template>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import { confirm } from './decorator.js'

export default {
name: 'about',
methods: {
@confirm('删除', '确认删除?')
test () {
// do something 调用接口

this.$message.success('success!!')
}

}
}

三、前端API请求缓存

前端 API 请求缓存是前端性能优化的一个方案。

1、key值错误提示

1
const generateKeyError = new Error('Can not generate key from name and argument')

2、生成key值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
function generateKey(name,argument){
// 从argument 中获取数据然后变为数组
const params = Array.from(argument).join(',')
try{
return `${name}:${params}`
}catch{
return generateKeyError
}
}


function decorate(handleDescription,entryArgs) {
// 判断当前最后数据是否是descriptor ,如果是descriptor,直接使用
if(isDescriptor(entryArgs[entryArgs.length - 1])){
return handleDescription(...entryArgs,[])
}else{
return function(){
return handleDescription(...Array.prototype.slice.call(arguments),entryArgs)
}
}
}

function handleApiCache(target, name, descriptor, ...config) {
// 拿到函数体并保存
const fn = descriptor.value
// 修改函数体
descriptor.value = function () {
const key = generateKey(name, arguments)
// key无法生成,直接请求 服务端数据
if (key === generateKeyError) {
// 利用刚才保存的函数体进行请求
return fn.apply(null, arguments)
}
let promise = ExpriesCache.get(key)
if (!promise) {
// 设定promise
promise = fn.apply(null, arguments).catch(error => {
// 在请求回来后,如果出现问题,把promise从cache中删除
ExpriesCache.delete(key)
// 返回错误
return Promise.reject(error)
})
// 使用 10s 缓存,10s之后再次get就会 获取null 而从服务端继续请求
ExpriesCache.set(key, promise, config[0])
}
return promise
}
return descriptor;
}

// 制定 修饰器
function ApiCache(...args) {
return decorate(handleApiCache, args)
}

3、api接口处使用ApiCache()

1
2
3
4
5
6
7
class Api{
// 缓存10s
@ApiCache(10)
getData(params1,params2){
return request.get('/getList')
}
}