vue中使用自定义指令实现长列表优化

260 阅读1分钟

当后台返回数据量较大时,如果全部加载,性能会有巨大的影响。如何优化长列表也是前端工程师最为头疼的一个问题。现总结一种使用vue自定义指令来实现长列表性能优化的方案。

1. html模板:注意要写在需要优化元素的父级元素

// 条件判断时
<el-table stripe border height="550"
  :data="tableData"
  style="width: 100%"
  v-load-more.expand="{func: loadmore, target: '.el-table__body-wrapper', delay: 300}"
  :load-more-disabled="disabledLoad"
  @selection-change="handleSelectionChange"
  @select-all="handleSelectAll"
>
</el-table>

2. 自定义下拉显示更多指令

directives: {
    'load-more': {
      bind (el, binding, vnode) {
        const {
          expand
        } = binding.modifiers
        // 使用更丰富的功能,支持父组件的指令作用在指定的子组件上
        if (expand) {
          /**
             * target 目标DOM节点的类名
             * distance 减少触发加载的距离阈值,单位为px
             * func 触发的方法
             * delay 防抖时延,单位为ms
             * load-more-disabled 是否禁用无限加载
             */
          let {
            target,
            distance = 1,
            func,
            delay = 200
          } = binding.value
          if (typeof target !== 'string') return
          let targetEl = el.querySelector(target)
          if (!targetEl) {
            return
          }
          binding.handler = debounce(function () {
            const {
              scrollTop,
              scrollHeight,
              clientHeight
            } = targetEl
            let disabled = el.getAttribute('load-more-disabled')
            disabled = vnode[disabled] || disabled

            if (scrollHeight <= scrollTop + clientHeight + distance) {
              if (disabled) return
              func && func()
            }
          }, delay)
          targetEl.addEventListener('scroll', binding.handler)
        } else {
          binding.handler = debounce(function () {
            const {
              scrollTop,
              scrollHeight,
              clientHeight
            } = el
            if (scrollHeight === scrollTop + clientHeight) {
              binding.value && binding.value()
            }
          }, 200)
          el.addEventListener('scroll', binding.handler)
        }
      },

      unbind (el, binding) {
        let {
          arg
        } = binding
        // 使用更丰富的功能,支持父组件的指令作用在指定的子组件上
        if (arg === 'expand') {
          const {
            target
          } = binding.value
          if (typeof target !== 'string') return
          let targetEl = el.querySelector(target)
          targetEl && targetEl.removeEventListener('scroll', binding.handler)
          targetEl = null
        } else {
          el.removeEventListener('scroll', binding.handler)
          el = null
        }
      }
    }
  },

3. script标签中定义相关方法

// 加载显示更多请求
loadmore () {
    if (this.nextPage) {
        getActivedDeviceList({
            boardModel: '',
            departmentId: '',
            deviceStatus: this.idleStatus,
            pageNum: this.nextPage,
            pageSize: 20,
            searchKey: ''
        }).then(res => {
            if (res && res.result.length) {
                this.nextPage = res.nextPage
                this.tableData.push(...res.result)
            }
        })
    }
},

// 防抖
const debounce = function (func, delay) {
  let timer = null
  return function () {
    if (timer) clearTimeout(timer)
    timer = null
    let self = this
    let args = arguments
    timer = setTimeout(() => {
      func.apply(self, args)
    }, delay)
  }
}