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相近,因此功能流程可自行参考代码。