性能分析
当服务端返回的选择项数据达到一定量级的时候,加载渲染时会异常的卡顿,甚至会引发整个页面的卡顿
此时的用户体验是及差的
优化方案
对数据进行处理,只展示前 n 百条数据,通过自定义检索可以检索所有的数据
由于初始化拉取的数据量相对来说比较大,所以将数据的拉取放到工作线程去处理,详情可参照 第05期 - 多线程共享Worker实现数据预获取
组件封装
-
配置项设计
export default { props: { data: { type: Array, required: true // 源数据 }, count: { type: Number, default: 100 // 前 n 百条数据 }, idAlias: { type: String, default: 'id' // 设置业务字段名称 }, nameAlias: { type: String, default: 'name' // 设置显示字段名称 }, placeholder: { type: String, default: '请选择' // 设置占位文本 } } } -
使用组件的 v-model 实现父子组件数据同步
父组件
<base-select :data="data" v-model="performer" />子组件
<template> <el-select v-model="value" @change="handleChange"> <el-option v-for="item of options" :key="item[idAlias]" :label="item[nameAlias]" :value="item[idAlias]"> </el-option> </el-select> </template>export default { model: { prop: 'value', event: 'change' }, props: { value: { type: Array, required: true }, }, methods: { handleChange(value) { this.$emit('change', value) }, } } -
通过监听对源数据进行初始化(考虑数据回显的问题)
export default { data() { return { options: [] } }, watch: { value: { handler(newVal, oldVal) { newVal && this.init() }, immediate: true } }, methods: { init() { const { data, count, idAlias, value: selectedIds } = this const allreadySelectedItems = data.filter(item => selectedIds.includes(item[idAlias])) const records = data.filter(item => !selectedIds.includes(item[idAlias])).slice(0, count - allreadySelectedItems.length) this.options = [...allreadySelectedItems, ...records] } } }把已选择的数据项放到最前边,方便后续选项多的优化扩展,只需要给模板添加
collapse-tags属性即可 -
实现自定义检索
<template> <el-select v-model="value" :filter-method="filterMethod"> <el-option v-for="item of options" :key="item[idAlias]" :label="item[nameAlias]" :value="item[idAlias]"> </el-option> </el-select> </template>export default { data() { return { options: [] } }, methods: { filterMethod(keyword = '') { const { data, count, nameAlias } = this const result = keyword && data.filter(item => item[nameAlias].includes(keyword)) if (result) { this.options = result.length > count ? result.slice(0, count) : result } } } }
完整代码
-
模板代码
<template> <el-select v-model="value" @change="handleChange" :filter-method="filterMethod" filterable clearable multiple :placeholder="placeholder"> <el-option v-for="item of options" :key="item[idAlias]" :label="item[nameAlias]" :value="item[idAlias]"> </el-option> </el-select> </template> -
脚本代码
export default { model: { prop: 'value', event: 'change' }, props: { data: { type: Array, required: true }, value: { type: Array, required: true }, count: { type: Number, default: 100 }, idAlias: { type: String, default: 'id' }, nameAlias: { type: String, default: 'name' }, placeholder: { type: String, default: '请选择' } }, data() { return { options: [] } }, watch: { value: { handler(newVal, oldVal) { newVal && this.init() }, immediate: true } }, methods: { init() { const { data, count, idAlias, value: selectedIds } = this const allreadySelectedItems = data.filter(item => selectedIds.includes(item[idAlias])) const records = data.filter(item => !selectedIds.includes(item[idAlias])).slice(0, count - allreadySelectedItems.length) this.options = [...allreadySelectedItems, ...records] }, handleChange(value) { this.$emit('change', value) }, filterMethod(keyword = '') { const { data, count, nameAlias } = this const result = keyword && data.filter(item => item[nameAlias].includes(keyword)) if (result) { this.options = result.length > count ? result.slice(0, count) : result } } } }一起学习,加群交流看 沸点