picker(级联)组件及组件封装经验
组件封装的几个经验
a、参数:最佳方式,仅一个object参数,所需要的实际参数,作为对象属性传入。
如此,便于数据的处理和扩展。例如,后期扩展需要增加参数,或者调整参数时,如果使用的对象传入,老的调用方法也可以获得兼容;否则,容易出错。
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方法,可以处理默认值、传入参数、私有参数,并且可以防止相互的覆盖。传入参数优先于默认值,私有参数又优先于传入参数。当函数扩展时,参数数量、位置的变化,不会影响到函数的调用和处理。
b、UI和数据的处理
每一个UI组件,都可以分离成UI部分,与数据部分。UI部分的变更,一般在数据变更后,驱动UI变更,将编译后的DOM string 进行挂载mount。
c、数据的处理
c1、获取数据
在做组件封装时,无法知晓相关业务场景涉及的数据是异步还是同步,所以获取数据时,可将数据的实际获取交由业务端处理,组件内只需调用获取数据的封装函数便可。该函数返回的对象为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)
})
c2、异步请求数据时序控制
异步请求数据,每个请求到达的时间不同,如发起请求的顺序为: 请求1 -> 请求2 -> 请求3,到达的顺序为:请求3 -> 请求1 -> 请求2,如果不做任何控制,最终使用的数据将会是最终到达的数据,但这个数据却不是最新的结果,将导致错误。因此,需要做异步请求的时序控制,对请求进行编号,当到达的结果时序号小于上一到达的结果时序号时,丢弃。
this._getDataByNet(index).then((rst) => {
// 当请求数据的序列号 大于 已插入面板的数据序列号时,数据有效;否则无效丢弃
if (!mySequenceNum || mySequenceNum > this._currSequenceNum[index]) {
// 存入内存中
this._requestRstBuff.set(targetValue, rst)
resolve(rst)
}
})
c3、数据缓存
异步请求是非常消耗资源的,需要额外的网络时间,并且需要进入时间循环当中。因此,如果可以对相应的数据进行缓存,将一定程度上,提升性能。如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(级联)组件及组件封装经验的更多相关文章
- react初探(二)之父子组件通信、封装公共组件
一.前言 在组件方面react和Vue一样的,核心思想玩的就是组件,下面举两个组件常用的情景. 场景一:假如我们现在有一个页面包含表格以及多个弹框,这种时候如果将这个页面的业务代码写在一个组件中,那么 ...
- VUE组件 单独文件封装
https://www.cnblogs.com/SamWeb/p/6391373.html vuejs 单文件组件.vue 文件 vuejs 自定义了一种.vue文件,可以把html, css, ...
- uniapp自定义picker城市多级联动组件
uniapp自定义picker城市多级联动组件 支持多端--h5.app.微信小程序.支付宝小程序... 支持自定义配置picker插件级数 支持无限级 注意事项:插件传入数据格式为children树 ...
- 从零搭建react+ts组件库(封装antd)
为什么会有这样一篇文章?因为网上的教程/示例只说了怎么做,没有系统详细的介绍引入这些依赖.为什么要这样配置,甚至有些文章还是错的!迫于技术洁癖,我希望更多的开发小伙伴能够真正的理解一个项目搭建各个方面 ...
- Vue.js与ElementUI搭建无限级联层级表格组件
前言 今天,回老家了.第一件事就是回家把大屏安排上,写作的感觉太爽了,终于可以专心地写文章了.我们今天要做的项目是怎么样搭建一个无限级联层级表格组件,好了,多了不多说,赶快行动起来吧!项目一览 到底是 ...
- React 面向组件化编程 - 封装了webpack - npm run build 产生的包的 /static 引用路径问题
React 面向组件化编程 面向对象 ----> 面向模块 ----> 面向组件 套路: 注意: 组件名必须大写开头: 只能有一个根标签: <input />虚拟DOM 元素必 ...
- Vue.js学习 Item11 – 组件与组件间的通信
什么是组件? 组件(Component)是 Vue.js 最强大的功能之一.组件可以扩展 HTML 元素,封装可重用的代码.在较高层面上,组件是自定义元素,Vue.js 的编译器为它添加特殊功能.在有 ...
- $Django Rest Framework-认证组件,权限组件 知识点回顾choices,on_delete
一 小知识点回顾 #orm class UserInfo (models.Model): id = models.AutoField (primary_key=True) name = models. ...
- slot是标签的内容扩展,也就是说你用slot就可以在自定义组件时传递给组件内容,组件接收内容并输出
html 父页面<div id="app"> <register> <span slot="name">{{message. ...
- drf框架 - 三大认证组件 | 认证组件 | 权限组件 | 频率组件
RBAC 基于用户权限访问控制的认证 - Role-Based Access Control Django框架采用的是RBAC认证规则,RBAC认证规则通常会分为 三表规则.五表规则,Django采用 ...
随机推荐
- 12.equals()方法总结
超类Object中有这个equals()方法,该方法主要用于比较两个对象是否相等.该方法的源码如下: 我们知道所有对象都有表示(内存地址)和状态(数据),看上面代码是用"=="来比 ...
- 认识Hadoop
概述 开源.分布式存储.分布式计算 大数据生态体系 特点:开源.社区活跃 囊括了大数据处理的方方面面 成熟的生态圈 推荐系统 应用场景 搭建大型数据仓库,PB级数据的存储.处理.分析.统计 日志分析 ...
- shell 命令 --ps aux | grep
ps aux | grep 要查询的进程名 查询当前进程,如 ps aux | grep python 确认过需要查询的进程,就可以进行 kill -9 进程号等操作了.
- Git 安装 windows && linux
一.安装: windows下安装Git: 1.下载Git:https://git-scm.com/download/win 2.安装Git:默认安装,一直回车 Linux下安装Git: yum安装: ...
- Monkey学习网址
http://***/2015/12/24/Android-Monkey-Test/ http://bbs.pediy.com/showthread.php?t=189584 http://***/2 ...
- 【npm】利用npm安装/删除/发布/更新/撤销发布包
什么是npm? npm是javascript的包管理工具,是前端模块化下的一个标志性产物 简单地地说,就是通过npm下载模块,复用已有的代码,提高工作效率 1.从社区的角度:把针对某一特定问题 ...
- 【问题解决方案】Keras手写数字识别-ConnectionResetError: [WinError 10054] 远程主机强迫关闭了一个现有的连接
参考:台大李宏毅老师视频课程-Keras-Demo 在载入数据阶段报错: ConnectionResetError: [WinError 10054] 远程主机强迫关闭了一个现有的连接 Google之 ...
- Mongodb-- python中使用pymongo连接mongodb数据库
一.使用 通过pip或者pychrm下载pymongo模块 import json from pymongo import MongoClient from bson import ObjectId ...
- UVA12888 【Count LCM】(莫比乌斯反演)
题意:求\(\sum_{i=1}^{n}\sum_{j=1}^{m}[gcd(i,j)==1]\) \(assume\ n<m\) \(\sum_{i=1}^{n}\sum_{j=1}^{m}[ ...
- <转>php中heredoc与nowdoc的使用方法
http://www.361way.com/php-heredoc-nowdoc/3008.html 一.heredoc结构及用法 Heredoc 结构就象是没有使用双引号的双引号字符串,这就是说在 ...