你怕是疯了?区区选择框要渲染10000条数据呢?

465 阅读2分钟

前言

最近被需求经理还有后台坑惨了:

  1. 需求经理要求下拉框最多一次能渲染10000条数据;
  2. 后台一次性给我返回了10000条数据,不给 pageSize。

结果,不言而喻,页面卡死了。我跑去问导师,导师说10000条数据不卡死才怪,做不了。于是我跟需求经理说做不了。奈何需求经理非要做!于是导师就说用 table 做,效果果然很丝滑。并且还能做到全选、模糊搜索。牛啊牛啊,我愣是没看懂!

当我询问导师,能否用自定义指令,来监听下拉框的滚动距离,进而加载数据时,导师一口回绝了:就用 table 做,这个好些。耐不住疑惑,我硬着头皮试了下,果然不行。不知道为啥找不到下拉框那个元素,用了类名也找不到。不是 null 就是 undefined。再次询问导师未果(导师估计看不下去了,懒得解释),可能内部组件真的做不到吧。如果可以,导师这么牛逼,咋会选用更为复杂的 table 做呢?

困惑了好些天,既然做不到,那就回家用 el-select 组件做着玩吧。

法一、局部注册自定义指令,监听滚动距离,分段加载

<template>
  <div class="select">
    <el-select
      class="myselect"
      v-loadmore="loadMore"
      v-model="value"
      multiple
      collapse-tags
      style="margin-left: 20px"
      clearable
      placeholder="请选择"
    >
      <el-option
        v-for="(item, index) in options"
        :key="index"
        :label="item"
        :value="item"
      >
      </el-option>
    </el-select>
  </div>
</template>
<script>
export default {
  // ◆注册局部自定义指令 loadmore ,监听是否到底部
  directives: {
    loadmore: {
      bind: function (el, binding) {
        const SELECTWRAP = el.querySelector(
          ".el-select-dropdown .el-select-dropdown__wrap"
        );
        SELECTWRAP.addEventListener("scroll", function () {
          console.log(this.scrollHeight, this.scrollTop, this.clientHeight);
          const CONDITION =
            this.scrollHeight - this.scrollTop <= this.clientHeight;
          console.log(CONDITION);
          if (CONDITION) {
            binding.value();
          }
        });
      },
    },
  },
  data() {
    return {
      value: [],
      // 一次性获取的原始数据
      arr: [],
      // 咱们渲染的数据
      options: [],
      // 后台不传 pageSize ,咱就造一个
      pageSize: 50,
    };
  },
  mounted() {
    // ◆循环渲染 10000 条数据,写一万条数据是万万不可能的
    let len = 10000;
    for (let i = 0; i < len; i++) {
      this.arr.push(i);
    }
    // ◆初始加载前 50 条数据,不然一次性渲染 10000 条要卡死
    this.options = this.arr.slice(0, this.pageSize);
  },
  methods: {
    loadMore() {
      // ◆一到底部就往后加载 50 条数据,你可以提前一点加载
      this.options.push(...this.arr.slice(this.pageSize, this.pageSize + 50));
      // ◆此时 pageSize 要加 50,不然一直渲染 50~100 条数据
      this.pageSize = this.pageSize + 50;
    },
  },
};
</script>

效果如下,自己动手试试,身临其境的感觉更妙!

image.png

法二、在 main.js 里面全局注册自定义指令 loadmore

main.js

import Vue from 'vue';
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import App from './App.vue';
Vue.directive('loadmore',{
  bind: function (el, binding) {
    const SELECTWRAP = el.querySelector(
      ".el-select-dropdown .el-select-dropdown__wrap"
    );
    SELECTWRAP.addEventListener("scroll", function () {
      console.log(this.scrollHeight, this.scrollTop, this.clientHeight);
      const CONDITION =
        this.scrollHeight - this.scrollTop <= this.clientHeight;
      console.log(CONDITION);
      if (CONDITION) {
        binding.value();
      }
    });
  },
})
Vue.use(ElementUI);
Vue.config.productionTip = false

new Vue({
  render: h => h(App),
}).$mount('#app')

法三、把自定义指令单独抽离成一个 js 文件,再引入main.js

directive.js

import Vue from 'vue'
export default ()=>{
    Vue.directive("loadmore", {
        bind(el, binding) {
            const SELECTWRAP = el.querySelector(
                ".el-select-dropdown .el-select-dropdown__wrap"
            );
            SELECTWRAP.addEventListener("scroll", function () {
                console.log(this.scrollHeight, this.scrollTop, this.clientHeight);
                const CONDITION = this.scrollHeight - this.scrollTop <= this.clientHeight;
                console.log(CONDITION);
                if (CONDITION) {
                    binding.value();
                }
            });
        },
    });
}

main.js

import Vue from 'vue';
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import App from './App.vue';
// 引入 自定义指令
import Directive from './directive'
Vue.use(Directive)

Vue.use(ElementUI);
Vue.config.productionTip = false

new Vue({
  render: h => h(App),
}).$mount('#app')

最后,还想说一句,你既然要做下拉框,渲染那么多数据有何意义,咱前端是做出来,你真的确定要拉一万条数据吗?数据辣么多,你搞个远程搜索不香吗?