以级联选择器为例,聊聊数据量过大造成的页面卡顿问题

前端小卡

共 4729字,需浏览 10分钟

 ·

2021-03-14 11:50

背景

列表、树、级联选择都是用来展示“大量”、“相似度高”的数据,它们都是需要在dom中生成大量的节点,这就造成了数据量大造成页面卡顿的原因,当然以elementui为例,其实el-tree和el-cascader都对外提供了允许数据懒加载提供了接口。还是以级联选择器为例,仅仅是提供了父节点到子节点的懒加载,若子节点数据量大,那就的需要我们自己来二次封装。下面就来简单实现一下这个二次封装的懒加载。下面实现的组件仅仅考虑子节点数据量大的情况

效果展示


考虑因素

  • 我们暂时称一级菜单为「类型」,二级菜单为「标签」
  • 初始化时,我们设置一个固定的标签个数n,如果每个类型大于等于n,则显示个数为n,如果小于n则显示已有的个数
  • 当我们滚动条滚动到底部时,继续加载n个标签,直到加载完毕。
  • 加载完新的数据之后,要让滚动条停留到上次滚动的位置
  • 假如我们设置了默认全选,则要在懒加载的时候,加载出来的新标签也要设置为选择状态
  • 尽管首次渲染仅仅加载了部分标签,但是允许搜索的时候,范围为所有的标签数据
  • 全选的时候要有一个标识来告诉用户现在是全选的状态

关键代码

在触发每个「类型」的click时候,需要为级联面板绑定滚动监听,并将当前的类型传到滚动回调。当然因为回调函数为同一个,所以不会造成多次监听的问题。

handleChange(value) {
    if (!this.lazy) {
      return
    }
    this.$nextTick(() => {
      const panel = document.querySelectorAll('.el-cascader-menu__wrap.el-scrollbar__wrap')
      const length = panel.length
      const currentPanel = panel[length-1]
      this.type = value[0]

      if (currentPanel) {
        currentPanel.addEventListener('scroll'this.scrollCallback(currentPanel))
      }
    })
},

大部分的逻辑都在滚动回调中

scrollCallback(el) {
  return () => {
    const scrollTop = el.scrollTop
    const clientHeight = el.clientHeight
    const scrollHeight = el.scrollHeight

    // 若scrollTop + clientHeight === scrollHeight 则说明滚动条到达了底部
    if (scrollTop + clientHeight === scrollHeight) {
      const tmp = []
      // 当前类型下所有的选项
      const allOptions = this.allOptions.filter(options=>options.value === this.type)[0]?.children

      this.options.forEach(element => {
        if (element.value === this.type) {

          const optionLen = element.children.length-1
          const allOptionsLen = allOptions.length-1

          if (optionLen >= allOptionsLen) {
            return
          }

          const loadsize = optionLen + this.loadSize
          // 当我们滚动条滚动到底部时,继续加载n个标签,直到加载完毕
          const size = loadsize > allOptionsLen ? allOptionsLen : loadsize
          for (var i=optionLen; i<=size; i++) {
            tmp.push(allOptions[i])
            if (this.isAllSelected) {
              this.values.push([this.type, allOptions[i].value])
            }
          }
          element.children.push(...tmp)
        }
      })
      
      //加载完新的数据之后,要让滚动条停留到上次滚动的位置
      setTimeout(() => {
        el.scrollTop = scrollTop
      })
    }
  }
}

源码与使用方式

  • 源码
  • npm install cu-vue-cascader
  • import CuVueCascader from 'cu-vue-cascader'
  • vue.use(CuVueCascader)
// 几个重要的参数
allOptions: array 所有的餐素
selectAll:  boolean 默认是否全选
allSelectedClass: {} 全选时候的样式
lazy: boolean 是否开启子选项的懒加载
loadSize: number 子选择每次加载的条数


浏览 319
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报