ES6問世的時間也不短了,並且不少時候對ES6所謂的「熟練應用」基本還停留在下面的幾種api應用:es6
(固然也多是我用的比較簡單)算法
最近也是看了不少大神寫的代碼,確實學到了不少東西,這也讓我下定決心要更深層次的應用ES6api
本次咱們介紹decrator(修飾器)在業務中的應用數組
首先咱們先看下decrator的用法:promise
1.類修飾器(只有一個參數):緩存
target -> 指向類,若是是類型是function,則指向MyFunction.prototypecookie
// 類修飾器 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.方法修飾器(有三個參數)網絡
target -> 方法所在的類app
key -> 方法名稱異步
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的應用,而且是結合實際業務。
後面也是打算對現有項目的公共庫進行算法優化升級。若是有機會再進行分享。