封装一个 vue3 通用组件,用于懒加载子组件
简介
某些场景下,容器组件会包含很多子组件,比如表格的列和表单的字段,而一旦数量上去而且列/字段组件还嵌套了其他组件,就会导致渲染时长急剧增加。因此,考虑封装一个通用的懒加载组件,分组延迟加载子组件,使得第一次渲染不会产生明显的卡顿;更多的子组件会在第一组子组件渲染完毕一段时间后再依次分段渲染。
实现示例
<script>
import { ref, h } from 'vue';
export default {
props: {
// 每组渲染的子组件数量
size: {
type: Number,
default: 5,
},
// 每段渲染完成后至下一段渲染开始之间的延迟时间
delay: {
type: Number,
default: 200, // 单位 ms
},
/**
* 默认根据延迟时间懒加载;或者通过元素滚动到 viewport 懒加载
* (暂未添加该功能;具体思路没有限制,可以是先渲染空状态,滚动后再渲染实际内容;
* 也可以像无限滚动一样,一开始不渲染,滚动后再渲染内容;
* 或者还可以组合两种模式,延迟加载只适用于一定数量的子组件,超过的部分则通过滚动触发渲染)
* */
lazyMode: {
type: String,
default: 'delay',
validator(value) {
return ['delay', 'scroll'].includes(value);
},
},
},
setup(props, context) {
const { size, delay } = props;
const leafs = ref([]);
const { default: defaultSlot } = context.slots;
const children = defaultSlot ? defaultSlot() : [];
const appendLeafs = (leafs, children, start, size, max) => {
// 剩余子节点少于 size ,直接到最后一个子节点,并设置 start = -1,表示结束
// 大于 size ,则取 size 数量的子节点,并设置 start += size
const slice = (children, start, end) => {
let list = Object.values(children);
return end === undefined ? list.slice(start) : list.slice(start, end);
};
const append = (newLeafs) => (leafs.value = leafs.value.concat(newLeafs));
if (max - start > size) {
let end = start + size;
append(slice(children, start, end));
start = end;
} else {
start = start - max;
append(slice(children, start));
}
return start;
};
let start = 0;
const max = children.length;
start = appendLeafs(leafs, children, start, size, max);
// 通过定时器延迟渲染
const delayAppend = (start) => {
if (start > -1) {
setTimeout(() => {
start = appendLeafs(leafs, children, start, size, max);
delayAppend(start);
}, delay);
}
};
delayAppend(start);
return { leafs };
},
render() {
return h('div', null, [this.leafs]);
},
};
</script>
注意事项
请确保每个分组能够不少于当前容器在一屏内所能显示的子节点数量,否则分组渲染之间会有明显的闪现,给用户不好的体验。
存在的问题
后续分组渲染时,会导致操作时有明显的卡顿。滚动操作比较明显,若是输入或点击按钮等操作则没有太大影响。