前言
最近被需求经理还有后台坑惨了:
- 需求经理要求下拉框最多一次能渲染10000条数据;
- 后台一次性给我返回了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>
效果如下,自己动手试试,身临其境的感觉更妙!
法二、在 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')
最后,还想说一句,你既然要做下拉框,渲染那么多数据有何意义,咱前端是做出来,你真的确定要拉一万条数据吗?数据辣么多,你搞个远程搜索不香吗?