分享能提高開發(fā)效率,提高代碼質(zhì)量的八個(gè)前端裝飾器函數(shù)
裝飾器非常好用
前面給大家發(fā)了一篇文章xxx,給大家介紹了一遍JavaScript中的裝飾器,今天就給大家介紹一下在我項(xiàng)目中用到過的幾個(gè)裝飾器的思路吧!
代碼是偽代碼,只是提供個(gè)思路,實(shí)際上代碼不止這么少。
防抖裝飾器
當(dāng)我們?cè)谝恍┨厥鈭?chǎng)景時(shí),需要使用防抖這個(gè)優(yōu)化手段來進(jìn)行優(yōu)化,比如:
- 表單提交
- 數(shù)據(jù)的搜索
- 數(shù)據(jù)的篩選
- 某些數(shù)據(jù)的更改
- 數(shù)據(jù)改變時(shí)觸發(fā)的回調(diào)
這些函數(shù)都可以使用防抖裝飾器來進(jìn)行性能優(yōu)化,防抖的意思是,當(dāng)你頻繁執(zhí)行某一個(gè)操作時(shí),這個(gè)操作只執(zhí)行最后一次,確保不會(huì)因?yàn)轭l繁的執(zhí)行而損耗性能~下面是裝飾器的封裝:
// 裝飾器的封裝
function debounce(delay) {
return function(target, key, descriptor) {
const originalMethod = descriptor.value;
let timer;
descriptor.value = function(...args) {
clearTimeout(timer);
timer = setTimeout(() => {
originalMethod.apply(this, args);
}, delay);
};
return descriptor;
};
}
當(dāng)我們某個(gè)函數(shù)需要進(jìn)行防抖處理時(shí):
@debounce(500)
submit() {}
@debounce(500)
handleChange() {}
@debounce(500)
handleFilter() {}
節(jié)流裝飾器
節(jié)流跟防抖是不同的優(yōu)化手段,節(jié)流是保證在一段時(shí)間內(nèi)只執(zhí)行一次操作,適用在這些場(chǎng)景中:
- 監(jiān)聽窗口寬度變化的回調(diào)
- 監(jiān)聽滾動(dòng)條變化的回調(diào)
下面是裝飾器的封裝:
function throttle(delay) {
return function(target, key, descriptor) {
const originalMethod = descriptor.value;
let timer;
let lastExecTime = 0;
descriptor.value = function(...args) {
const now = Date.now();
if (now - lastExecTime >= delay) {
lastExecTime = now;
originalMethod.apply(this, args);
} else {
clearTimeout(timer);
timer = setTimeout(() => {
originalMethod.apply(this, args);
}, delay);
}
};
return descriptor;
};
}
當(dāng)我們某個(gè)函數(shù)需要進(jìn)行節(jié)流處理時(shí):
@throttle(200)
handleScroll() {}
@throttle(200)
handleResize() {}
日志輸出裝飾器
日志的輸出是很重要的,尤其是在 Nodejs 端,日志輸出會(huì)通過 pm2 等工具,記錄在一些日志文件里,尤其是一些比較公用的工具函數(shù),更是非常重要,一般需要記錄這些內(nèi)容。
- 執(zhí)行的函數(shù)名稱
- 執(zhí)行時(shí)傳入的參數(shù)
- 執(zhí)行后獲取到的結(jié)果
下面是裝飾器的封裝:
function log(target, key, descriptor) {
const originalMethod = descriptor.value;
descriptor.value = function(...args) {
console.log(`Entering ${key} with arguments:`, args);
const result = originalMethod.apply(this, args);
console.log(`Exiting ${key} with result:`, result);
return result;
};
return descriptor;
}
使用的時(shí)候:
class Common {
@log()
commonRequest(url, params) {
return request(url, params)
}
}
const common = new Common()
common.commonRequest('http://xxx.com', { name: 'l' })
Entering commonRequest with arguments: ['http://xxx.com', { name: 'l' }]
Exiting commonRequest with result: { 結(jié)果 }
錯(cuò)誤處理裝飾器
跟日志裝飾器一樣,錯(cuò)誤其實(shí)也是日志的一部分,錯(cuò)誤日志非常重要,因?yàn)?Nodejs 的線上報(bào)錯(cuò),大部分都需要通過查日志來進(jìn)行定位,所以我們也可以封裝一個(gè)錯(cuò)誤的處理裝飾器:
function errorHandler(target, key, descriptor) {
const originalMethod = descriptor.value;
descriptor.value = function (...args) {
try {
originalMethod.apply(this, args);
} catch (error) {
console.error(`Error occurred in ${key}:`, error);
}
};
return descriptor;
}
使用的時(shí)候:
class Common {
@log()
commonRequest(url, params) {
return request(url, params)
}
}
const common = new Common()
common.commonRequest('http://xxx.com', { name: 'l' })
Error occurred in commonRequest: Request Error
權(quán)限校驗(yàn)裝飾器
權(quán)限的校驗(yàn)在前端一般都不用裝飾器,但是在 Nodejs 管理接口時(shí),涉及到權(quán)限校驗(yàn)時(shí),用裝飾器是非常的方便的。
function authenticated(target, key, descriptor) {
const originalMethod = descriptor.value;
descriptor.value = function(...args) {
if (isAuthenticated()) {
originalMethod.apply(this, args);
} else {
console.log('Unauthorized access!');
}
};
return descriptor;
}
使用的時(shí)候,這樣就只有 admin 的身份可以訪問這個(gè)接口了。
class User {
@Get('/xx/xx')
@authenticated('admin')
getUser() {}
}
計(jì)時(shí)裝飾器
如果有一天,你們需要埋點(diǎn),計(jì)算一些比較重要函數(shù)的運(yùn)行性能時(shí),那么你肯定需要計(jì)算這些函數(shù)的執(zhí)行時(shí)間是多少,這時(shí)候封裝一個(gè)計(jì)時(shí)裝飾器會(huì)讓你非常方便。
function timing(target, key, descriptor) {
const originalMethod = descriptor.value;
descriptor.value = function(...args) {
const start = performance.now();
const result = originalMethod.apply(this, args);
const end = performance.now();
console.log(`Execution time of ${key}: ${end - start} milliseconds`);
return result;
};
return descriptor;
}
使用時(shí):
class Common {
@timing()
commonRequest(url, params) {
return request(url, params)
}
}
const common = new Common()
common.commonRequest()
Execution time of commonRequest: 20 milliseconds
緩存裝飾器
這個(gè)裝飾器適用在某一些場(chǎng)景,如果你有一個(gè)函數(shù)是用來計(jì)算值的,并且計(jì)算的過程非常復(fù)雜非常耗時(shí)間,那我建議你可以把這些計(jì)算結(jié)果儲(chǔ)存起來,而不是每次都重新計(jì)算,這能大大提升你的計(jì)算性能。
function cache(target, key, descriptor) {
const originalMethod = descriptor.value;
const cache = new Map();
descriptor.value = function(...args) {
const cacheKey = JSON.stringify(args);
if (cache.has(cacheKey)) {
return cache.get(cacheKey);
}
const result = originalMethod.apply(this, args);
cache.set(cacheKey, result);
return result;
};
return descriptor;
}
使用時(shí):
class Compute() {
@cache()
run(num1, num2) {
// 這里舉個(gè)簡(jiǎn)單例子
return num1 + num2
}
}
const c = new Compute()
c.run(1, 2) // 3 首次計(jì)算
c.run(1, 2) // 3 接下來都從緩存中拿
參數(shù)校驗(yàn)裝飾器
在老項(xiàng)目中,無法用到 typescript 這么好的東西時(shí),對(duì)于一些函數(shù)執(zhí)行時(shí),有必要用裝飾器對(duì)傳進(jìn)來的參數(shù)的類型進(jìn)行校驗(yàn)~沒辦法,沒有 typescript 真難受啊!
function validateArgs(...types) {
return function (target, key, descriptor) {
const originalMethod = descriptor.value;
descriptor.value = function (...args) {
for (let i = 0; i < types.length; i++) {
const type = types[i];
const arg = args[i];
if (typeof arg !== type) {
throw new Error(`Invalid argument type at index ${i}`);
}
}
originalMethod.apply(this, args);
};
return descriptor;
};
}
使用的時(shí)候需要傳入某個(gè)參數(shù)的類型限制。
class Common {
@validateArgs(['string', 'object'])
commonRequest(url, params) {
return request(url, params)
}
}
const common = new Common()
common.commonRequest(123, 123) // 報(bào)錯(cuò)