a、參數:最佳方式,僅一個object參數,所須要的實際參數,做爲對象屬性傳入。javascript
如此,便於數據的處理和擴展。例如,後期擴展須要增長參數,或者調整參數時,若是使用的對象傳入,老的調用方法也能夠得到兼容;不然,容易出錯。java
class Picker { constructor(options) { // 參數處理 Object.assign(this, { style: defaultStyle, liTemplate: defaultLiTemplate, defaultTarget: [], isCascade: true }, options, { _target: [], _list: [], _pElem: null, _currSequenceNum: [], // 當前插入面板的數據爲第n個數據請求,用於處理異步請求時序 _latestSequenceNum: 0, // 最新請求的序號,用於處理異步請求時序 _requestRstBuff: new Map(), // 緩存數據請求的結果,加速數據返回,map結構 _touchIndex: -1, // 標記當前數據請求經過那個面板觸發,用於處理異步請求時序 若是有級聯,當高級面板觸發的請求未結束時,不能繼續操做面板 _translateY: [], _lineHeight: 0 }) } ...... } // 調用 new Picker({ getList: () => { return new Promise((resolve, reject) => { resolve(xxx) }) } })
以picker的封裝爲例子,傳入的爲options對象,經過Object.assign方法,能夠處理默認值、傳入參數、私有參數,而且能夠防止相互的覆蓋。傳入參數優先於默認值,私有參數又優先於傳入參數。當函數擴展時,參數數量、位置的變化,不會影響到函數的調用和處理。git
b、UI和數據的處理github
每個UI組件,均可以分離成UI部分,與數據部分。UI部分的變動,通常在數據變動後,驅動UI變動,將編譯後的DOM string 進行掛載mount。web
c、數據的處理promise
c一、獲取數據緩存
在作組件封裝時,沒法知曉相關業務場景涉及的數據是異步仍是同步,因此獲取數據時,可將數據的實際獲取交由業務端處理,組件內只需調用獲取數據的封裝函數即可。該函數返回的對象爲promise,從而將因業務場景不一樣而產生的變化做爲黑盒封裝起來。如上面的例子,調用時業務端須要封裝getList函數,返回相應的promise。服務器
// 業務邏輯中封裝 new Picker({ getList: (target = [], index = 0) => { return new Promise((resolve, reject) => { let rst = { list: [], isDone: false, success: true } ... resolve(rst) }) } }).init() // 返回的promise 對象對應數據爲obj,包含邏輯處理所需的三個參數:success, list, isDone this.getList(this._target, index).then(({success, list, isDone}) => { if (success) { ... } }).catch((err) => { console.log(err) })
c二、異步請求數據時序控制網絡
異步請求數據,每一個請求到達的時間不一樣,如發起請求的順序爲: 請求1 -> 請求2 -> 請求3,到達的順序爲:請求3 -> 請求1 -> 請求2,若是不作任何控制,最終使用的數據將會是最終到達的數據,但這個數據卻不是最新的結果,將致使錯誤。所以,須要作異步請求的時序控制,對請求進行編號,當到達的結果時序號小於上一到達的結果時序號時,丟棄。app
this._getDataByNet(index).then((rst) => { // 當請求數據的序列號 大於 已插入面板的數據序列號時,數據有效;不然無效丟棄 if (!mySequenceNum || mySequenceNum > this._currSequenceNum[index]) { // 存入內存中 this._requestRstBuff.set(targetValue, rst) resolve(rst) } })
c三、數據緩存
異步請求是很是消耗資源的,須要額外的網絡時間,而且須要進入時間循環當中。所以,若是能夠對相應的數據進行緩存,將必定程度上,提高性能。如sug、picker均可以用到。
// 若是buff中有,則取buff中數據,不然請求數據 if (this._requestRstBuff.has(targetValue)) { rst = this._requestRstBuff.get(targetValue) resolve(rst) } else { this._getDataByNet(index).then((rst) => { // 當請求數據的序列號 大於 已插入面板的數據序列號時,數據有效;不然無效丟棄 if (!mySequenceNum || mySequenceNum > this._currSequenceNum[index]) { // 存入內存中 this._requestRstBuff.set(targetValue, rst) resolve(rst) } }) }
d、交互處理
交互處理以前的一片blog有提到過,須要進行防抖、限頻的處理,特別是進行網絡請求和UI頻繁更新的交互操做。
_registerUlEvent(ulElem, index) { let renderTouchUi = throttle(this._renderTouchUi, 50, this) let handleWholePanel = throttle(this._handleWholePanel, 500, this) ...... ulElem.addEventListener('touchmove', (event) => { event.preventDefault() event.stopPropagation() if (!(this._touchIndex != -1 && (index + 1) > this._touchIndex && this.isCascade)) { this._touchIndex = index + 1 touchInfo.currTouchY = event.touches[0].clientY renderTouchUi(touchInfo, ulElem, index, 'move') handleWholePanel(index + 1) } }, false) ...... } // 限頻 _throttle(fn, delay, ctx) { let isAvail = true let movement = null return function() { let args = arguments if (isAvail) { fn.apply(ctx, args) isAvail = false clearTimeout(movement) movement = setTimeout(() => { isAvail = true }, delay) } } }
如picker中,經過throttle對touchmove進行限頻處理,UI更新50ms一次,數據更新500ms一次。
因爲UI更新將直接做用在用戶視覺上,因此更新頻率須要根據用戶視感須要作調整。
而數據更新,基於如下幾點條件,不宜太過頻繁:
picker組件封裝了兩種,說是picekr組件,其實更傾向於級聯組件,相應的數據,具備必定的層級關係。相應的組件說明和使用方法以下。
import Picker from 'src/libs/picker-limited' new Picker({ // 默認值 defaultTarget: [ {value: 'test1', id: 1}, {value: 'test2', id: 2}, {value: 'test3', id: 3}, {value: 'test4', id: 4} ], // 結束回調 done: (info) => { console.log('info', info) }, // 數據接口函數 返回promise getList: (target = [], index = 0) => { return new Promise((resolve, reject) => { let rst = { list: [], isDone: false, success: true } if (index === 4) { rst.isDone = true resolve(rst) return } rst.list = [{ value: 'test1', id: 1 }, { value: 'test2', id: 2 }, { value: 'test3', id: 3 }, { value: 'test4', id: 4 }] resolve(rst) }) } }).init()
與picker-limited如出一轍
因沒有太複雜的交互,數據的異步、緩存等處理與picker-limited相近,因此功能流程可自行參考代碼。