picker(級聯)組件及組件封裝經驗

組件封裝的幾個經驗

  • 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更新將直接做用在用戶視覺上,因此更新頻率須要根據用戶視感須要作調整。

而數據更新,基於如下幾點條件,不宜太過頻繁:

  • a、touchmove爲過程操做,並不是touchend同樣,爲結果操做;
  • b、網絡請求須要消耗必定的資源,對服務器、web性能都會形成必定的影響;
  • c、過程操做中,用戶需對數據進行篩選,以選擇合適的結果,因此數據的更新仍然是有必要的;
  • d、綜上所述,限制數據更新的頻率,將更符合用戶體驗要求及性能要求。

picker

picker組件封裝了兩種,說是picekr組件,其實更傾向於級聯組件,相應的數據,具備必定的層級關係。相應的組件說明和使用方法以下。

picker-limited

功能特色
  • a、受UI控制,實現有限級的數據展示;
  • b、請求數據緩存,加速數據返回;
  • c、增長數據請求的時序控制;
  • d、交互touchmove的有效性控制;
  • e、限頻:UI更新50ms/touchmove,數據更新500ms/touchmove; touchend時,當即開啓數據請求及UI更新;
  • f、支持級聯;
  • g、支持列表單列dom自定義;
  • h、使用getComputedStyle獲取行高,保留了小數點更爲精確。
調用方法
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-limitless

功能特色
  • a、可實現無限級數據的展示;
  • b、請求數據緩存,加速數據返回;
  • c、增長數據請求的時序控制;
  • d、支持列表單列dom自定義。
調用方法

與picker-limited如出一轍

展示

流程圖

因沒有太複雜的交互,數據的異步、緩存等處理與picker-limited相近,因此功能流程可自行參考代碼。

代碼及說明連接

picker code

相關文章
相關標籤/搜索