ES6问世的时间也不短了,并且不少时候对ES6所谓的“熟练应用”基本还停留在下面的几种api应用:es6
(固然也多是我用的比较简单)算法
最近也是看了不少大神写的代码,确实学到了不少东西,这也让我下定决心要更深层次的应用ES6api
本次咱们介绍decorator(修饰器)在业务中的应用数组
首先咱们先看下decorator的用法:promise
1.类修饰器(只有一个参数):缓存
target -> 指向类,若是是类型是function,则指向MyFunction.prototypebash
// 类修饰器
const animalDecorator = (target) => {
target.isAnimal = true
target.prototype.nickname = 'nimo'
};
@animalDecorator
class Cat {
...
}
console.log(Cat.isAnimal); // true
console.log((new Cat()).nickname); // 'nimo'
复制代码
2.方法修饰器(有三个参数)cookie
target -> 方法所在的类网络
key -> 方法名称app
descriptor -> 描述对象
// 方法修饰器
const log = (target, key, descriptor) => {
const oriFunc = descriptor.value
descriptor.value = (...args) => {
console.log(`${key}:`, args)
oriFunc.apply(this, args)
}
return descriptor
};
class Util {
@log
static setParam (param) {
...
}
}
Util.setParam({name: 'xxx'}) // 'setParam: {name: "xxx"}'
复制代码
上面的用法没有传参数,若是须要传参数的话,内部须要return一个方法,以方法修饰器为例
// 方法修饰器
const log = (name) => {
return (target, key, descriptor) => {
const oriFunc = descriptor.value
descriptor.value = (...args) => {
console.log(`${key} ${name}:`, args)
oriFunc.apply(this, args)
}
return descriptor
}
};
class Util {
@log('forTest')
static setParam (param) {
...
}
}
Util.setParam({name: 'xxx'}) // 'setParam forTest: {name: "xxx"}'
复制代码
上面说的你们从网络上各类文章基本都能看到。
应用的话打日志也算是一种,可是感受应用场景有限,通常对关键业务操做才会用到。常规的业务感受应用并很少。
下面介绍几个常见的场景:
场景: 页面加载完成后,须要同时发送多个行为埋点统计(如:pv、某些模块曝光点)
特色: 每次发送埋点都要检查token是否存在,在本地cookie中没有token的时候,就会从接口获取,并种到本地。
看着逻辑好像没问题。
实际: 这些行为埋点方法调用的时机,基本上是同时发生。若是cookie中没用token,这几回api调用都会触发获取token接口的调用,这就致使屡次没必要要的请求。
目标: 咱们但愿,就请求一次接口就能够了。
那么,咱们就须要处理发送埋点的方法,通常有两种方式:
统计方法:
...
/**
* 上报埋点
* @param {string} actiontype
* @param {string, optional} pagetype
* @param {Object, optional} backup
*/
static report (actiontype, pagetype, backup = {}) {
try {
// 处理actiontype字段
if (!actiontype) return
actiontype = actiontype.toUpperCase() // 转为大写
// 处理pagetype字段
if (!pagetype) {
// 获取当前页面的页面名称
pagetype = Util.getPageName()
}
pagetype = pagetype.toUpperCase()
// 处理backup字段
if (backup && typeof backup !== 'object') {
console.error('[埋点失败] backup字段应为对象类型, actionType:', actiontype, 'pageType:', pagetype, 'backup:', backup)
return
}
let commonParams = LeStatic._options.commonBackup.call(this)
for (let param in backup) {
if (param in commonParams) {
console.warn(`[埋点冲突] 参数名称: ${param} 与统一埋点参数名称冲突,请注意检查`, `actionType:`, actiontype, 'pageType:', pagetype, 'backup:', backup)
}
}
backup = Object.assign(commonParams, backup)
backup = JSON.stringify(backup)
// 保证token的存在
ZZLogin.ensuringExistingToken().then(() => {
// 获取cookieid字段
let cookieid = Cookies.get('tk')
// 发送埋点请求
wx.request({
url: LeStatic._options.LOG_URL,
data: {
cookieid,
actiontype,
pagetype,
appid: 'ZHUANZHUAN',
_t: Date.now(),
backup
},
success: (res) => {
if (res.data === false) {
console.warn('[埋点上报失败] 接口返回false, actionType:', actiontype, 'pageType:', pagetype)
}
},
fail: (res) => {
console.warn('[埋点上报失败] 网络异常, res:', res)
}
})
})
} catch (e) {
console.warn('[埋点上报失败] 捕获代码异常:', e)
}
}
复制代码
这块看着好像没作缓存处理,别着急
关键点在:ZZLogin.ensuringExistingToken()的调用,咱们来看下ZZLogin中的ensuringExistingToken方法
lib/ZZLogin.js
import { mergeStep } from '@/lib/decorators'
class ZZLogin {
...
/**
* token机制,请求发起前,先确保本地有token,若是没有,调用接口生成一个临时token,登陆后
* @return {Promise}
*/
@mergeStep
static ensuringExistingToken () {
return new Promise((resolve, reject) => {
const tk = cookie.get('tk') || ''
// token已存在
if (/^wt-/.test(tk)) {
resolve()
return
}
// 获取用户token
ZZLogin.getToken().then(res => {
resolve()
})
})
}
}
复制代码
咱们在调用ensuringExistingToken 时加了修饰器,目的就是,即便同时刻屡次调用,异步请求也是被合并成了一次,其余次的调用也是在第一次异步请求完成后,再进行统一调用。
来看看修饰器是怎么写的(mergeStep)
lib/decorators.js
...
// 缓存对象
const mergeCache = {}
export function mergeStep (target, funcName, descriptor) {
const oriFunc = descriptor.value
descriptor.value = (...args) => {
// 若是第一次调用
if (!mergeCache[funcName]) {
mergeCache[funcName] = {
state: 'doing', // 表示处理中
fnList: []
}
return new Promise((resolve, reject) => {
// 进行第一次异步处理
oriFunc.apply(null, args).then(rst => {
// 处理完成后,将状态置为done
mergeCache[funcName].state = 'done'
resolve(rst)
// 将缓存中的回调逐一触发
mergeCache[funcName].fnList.forEach(fnItem => {
fnItem()
})
// 触发后将数组置空
mergeCache[funcName].fnList.length = 0
})
})
// 同时刻屡次调用
} else {
// 后面重复的调用的回调直接缓存到数组
if (mergeCache[funcName].state === 'doing') {
return new Promise((resolve, reject) => {
mergeCache[funcName].fnList.push(() => {
resolve(oriFunc.apply(null, args))
})
})
// 若是以前异步状态已经完成,则直接调用
} else {
return oriFunc.apply(null, args)
}
}
}
return descriptor
}
复制代码
原理:
其实修饰器你们知道么,基本上都了解,可业务里就是历来不用。包括es6中其它api也同样,会用了才是本身的。
最近也是全组一块儿从新深刻学习es6的应用,而且是结合实际业务。
后面也是打算对现有项目的公共库进行算法优化升级。若是有机会再进行分享。