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的應用,而且是結合實際業務。
後面也是打算對現有項目的公共庫進行算法優化升級。若是有機會再進行分享。