您的位置:首页 > 健康 > 养生 > 淘宝运营公司哪家好_北京广告设计公司排行_百度指数网站_百度做网站推广电话

淘宝运营公司哪家好_北京广告设计公司排行_百度指数网站_百度做网站推广电话

2024/12/29 3:26:23 来源:https://blog.csdn.net/weixin_45821809/article/details/143688601  浏览:    关键词:淘宝运营公司哪家好_北京广告设计公司排行_百度指数网站_百度做网站推广电话
淘宝运营公司哪家好_北京广告设计公司排行_百度指数网站_百度做网站推广电话

需求简介

vue2+element-ui项目中,当el-select中数据量较大时,会导致页面加载和渲染卡顿。在现在的el-select的基础上使用分页或者虚拟列表的形式去处理大量的下拉菜单,保证页面的正常渲染及el-select的正常回显。

需求分析

主要涉及几个点:

  1. 下拉菜单主体实现虚拟展示,保证渲染效率
  2. 展开和关闭时要保证已选中的选项在虚拟列表内,保证回显的是 label,而不是 value
  3. select 清空时,虚拟列表回归到顶部
  4. 下拉菜单发生改变时,重新计算滚动条长度,并回归到顶部
  5. 多选场景暂不支持,因为无论如何都不可避免更多的js计算逻辑,两个数组去比较在极限场景下是无法避免的,所以我就砍掉了。
  6. 本地搜索时更新组件传入的下拉菜单。

完整代码

使用:

<template><div style="width: 100%;display: flex;align-items: center"><el-selectv-model="deviceValue"filterableclearable:filter-method="filterOption"@visible-change="changeVisible($event)"><virtual-optionsref="virtualRef":virtual-data="secList":select-value="deviceValue"/></el-select></div>
</template><script>
import { getXXX } from '@/api/xxx'
import VirtualOptions from './VirtualOptions.vue'export default {components: { VirtualOptions },props: {defaultValue: {type: String,default: ''},deviceType: {type: String,default: ''}},data() {return {deviceValue: '',secList: [],cloneSelList: [],queryType: 0,selectedObj: {}}},watch: {// 监听父组件传入的数据,更新到本地defaultValue(newVal) {this.deviceValue = newVal},// 监听本地数据的变化,通知父组件更新deviceValue(newVal) {for (const i of this.secList) {if (newVal === i.deviceId) {this.selectedObj = ibreak}}this.emitChange()},queryType() {this.emitChange()}},created() {this.getDeviceList()},methods: {emitChange() {const queryParamData = {};if (this.queryType === 0) {queryParamData['deviceId'] = this.selectedObj['deviceId'];} else if (this.queryType === 1) {queryParamData['sectionId'] = this.selectedObj['sectionId'];queryParamData['sectionName'] = this.selectedObj['sectionName'];}this.$emit('change', queryParamData)},filterOption(val) {if (val) {val = val.toUpperCase();this.secList = this.cloneSelList.filter((item) => {if (item.label.toUpperCase().indexOf(val) >= 0) {return true}})} else {this.secList = this.cloneSelList}},changeVisible(cb) {this.secList = this.cloneSelListthis.$nextTick(() => cb && this.$refs["virtualRef"].resetVirtual()); // 解决打开白屏问题,必须使用 $nextTick 延时处理},getDeviceList() {this.deviceValue = this.defaultValuegetXXX ({ deviceType: this.deviceType }).then(response => {const data = response.datathis.cloneSelList = this.secList = data})}}
}
</script>

VirtualOptions组件

<template><div class="option-wrap" :id="randomId"><!-- 真实dom --><div class="virtual-dom"><!-- 使用虚拟列表渲染el-option组件 --><el-option v-for="item in virtualOptions" :key="item.deviceId" :label="item.label" :value="item.deviceId" /><!-- 增加一个空的,解决第一次展示为空的问题 --><el-option v-if="virtualData && virtualData.length" disabled style="display: none" :value="''" :label="''" /></div></div>
</template><script>
import { getParents } from '@/utils/utils'export default {props: {virtualData: {type: Array,default: () => []},selectValue: {type: [String, Number],default: ''},isDevice: {type: Boolean,default: true}},data() {return {randomId: '', // 随机生成的ID值virtualOptions: [], // 虚拟列表的选项数据leafNumber: 0, // 选项的叶子节点数量optionHeight: 34, // 选项的高度};},watch: {// 监听selectValue的变化,当其发生变化时调用resetVirtual方法selectValue() {this.resetVirtual();},// 监听virtualData的变化virtualData() {this.resetVirtual()this.initWrapHeight()}},mounted() {// 初始化随机ID值,并在下一次DOM更新时调用initVirtual方法this.initId()this.$nextTick(() => this.initVirtual())},methods: {// 初始化虚拟列表容器的高度initWrapHeight() {const $wrap = document.getElementById(this.randomId);$wrap.style.height = this.virtualData.length * this.optionHeight + 100 + 'px'; // 设定虚拟列表的总高度this.initVirtual()},/*** 重置虚拟列表,使用场景有两个* 1- select选中时,保证选中数据正常为label值* 2- 下拉框展开保证下拉菜单回显正常*/resetVirtual() {const $wrap = document.getElementById(this.randomId);const $virtualDom = $wrap.querySelector('.virtual-dom');const $scroll = getParents($wrap, 'el-select-dropdown__wrap');const _scrollHeight = $scroll.offsetHeightlet vIndex = 0;if (this.selectValue !== '') {// 查找选中值在虚拟数据中的索引vIndex = this.virtualData.findIndex((item) => item.deviceId === this.selectValue);}// 计算可视区域内需要显示的选项数量const showNumber = parseInt(_scrollHeight / this.optionHeight) + this.leafNumber;// 更新虚拟列表的选项数据this.virtualOptions = this.virtualData.slice(vIndex, vIndex + showNumber);this.$nextTick(() => {// 更新虚拟列表的位置和滚动条的位置,实现选项的正确显示$virtualDom.style.transform = `translate(0, ${vIndex * this.optionHeight}px)`;$scroll.scrollTop = vIndex * this.optionHeight;});},// 初始化虚拟列表initVirtual() {const $wrap = document.getElementById(this.randomId);const $virtualDom = $wrap.querySelector('.virtual-dom');$wrap.style.height = this.virtualData.length * this.optionHeight + 100 + 'px'; // 设定虚拟列表的总高度const $scroll = getParents($wrap, 'el-select-dropdown__wrap');const scrollFunc = () => {const _scrollTop = $scroll.scrollTop;const showNumber = 8$virtualDom.style.transform = `translate(0, ${_scrollTop}px)`;// 保证选中项超出可视范围后导致回显选择项异常let vIndex = parseInt(_scrollTop / this.optionHeight);// 保证最后一屏显示正常  1 为 leafif (vIndex > this.virtualData.length - showNumber) {vIndex = this.virtualData.length - showNumber + 1;}if (this.selectValue !== '' &&this.virtualOptions.findIndex((item) => item.value === this.selectValue) > -1) {this.virtualOptions = [...this.virtualData.slice(vIndex, vIndex + showNumber),Object.assign({},this.virtualData.find((item) => item.deviceId === this.selectValue),{ hide: true },),];} else {if (this.virtualData.length < 8) {this.virtualOptions = this.virtualData} else {this.virtualOptions = this.virtualData.slice(vIndex, vIndex + showNumber);}}}// 重置virtualData时,清理滚动方法并初始化滚动容器高度this.$nextTick(() => $scroll.addEventListener('scroll', scrollFunc));this.$nextTick(scrollFunc())},// 生成一个随机的id值initId() {this.randomId = 'virtual_' + parseInt(Math.random() * 10 * 1024 * 1024 + '');}}
};
</script>

utils.js


/*** 根据class名称递归查询父节点* @param {HTMLElement} element 当前节点element* @param {string} className 需要查询的class值*/
export function getParents(element, className) {var returnParentElement = nullfunction getpNode(element, className) {// 创建父级节点的类数组const pClassList = element.parentNode.getAttribute('class').split(' ')const pNode = element.parentNodeif (!pClassList || !pClassList.length) {// 如果未找到类名数组,表示父类无类名,则再次递归getpNode(pNode, className)} else if (pClassList && !pClassList.includes(className)) {// 如果父类的类名中没有预期类名,则再次递归getpNode(pNode, className)} else if (pClassList && pClassList.includes(className)) {returnParentElement = pNode}}getpNode(element, className)return returnParentElement
}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com