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

相关文章
相关标签/搜索