Vue2中使用WeakMap提升表格渲染效率

204 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第1天,点击查看活动详情

什么是weakMap

WeakMap结构与Map结构类似,也是用于生成键值对的集合。 WeakMap只接受对象作为键名(null除外),不接受其他类型的值作为键名。WeakMap键名所引用的对象都是弱引用,即垃圾回收机制不将该引用考虑在内。 本文不多介绍weakMap相关内容,详情可以看这里

业务场景介绍

在常见的CRUD业务中,数据枚举值是比较常见的,比如说数据的状态字段。举个栗子:

<template>
    <el-table border height="100%" :data="tableData">
        <el-table-column label="状态" align="center" width="120" show-overflow-tooltip>
            <template #default="{row}">
                {{ getLabel(row.status, statusList) }}
            </template>
        </el-table-column>
    </el-table>
</template>
<script>
export default {
    data() {
        return {
            tableData: [],
            statusList: [{
                label: '正常',
                value: 1
            }, {
                label: '紧急',
                value: 2
            }, {
                label: '非常紧急',
                value: 3
            }]
        };
    },
    methods: {
        getLabel(status, statusList) {
            const statusEnum = statusList.find(item => item.value === status);
            return statusEnum?.label || status;
        }
    }
};
</script>

解释一下枚举数组为什么要设计成这样:是为了Selector组件选择使用,将来把枚举数组放在公共文件里,可以保证数据的唯一性。

大家可以想想看有什么问题。没错,如果状态量枚举比较多、数据量比较大的话,最多会执行一个N*M的循环嵌套,所以我们可以着手从这里进行优化。

优化第一步:冻结枚举数组

枚举数组不需要响应式

    data() {
        return {
            tableData: [],
            statusList: Object.freeze([{
                label: '正常',
                value: 1
            }, {
                label: '紧急',
                value: 2
            }, {
                label: '非常紧急',
                value: 3
            }])
        };
    },

优化第二步:改造getLabel函数

// utils/enum.js
const weakMap = new WeakMap();
function list2Map(list = [], option = {label: 'label', value: 'value'}) {
    const {label, value} = option;
    // 找到数据桶
    let bucket = weakMap.get(list);
    if (!bucket) {
        bucket = new Map();
        weakMap.set(list, bucket);
    }
    // 找到数据桶里的label-value对应的map
    const key = `label:${label},value:${value}`;
    if (!bucket.get(key)) {
        const map = {};
        list.forEach(item => {
            map[item[value]] = item[label];
        });
        bucket.set(key, map);
    }
    // 返回map
    return bucket.get(key);
}

/**
 * 通过value返回对应的label值
 * @param {*} val
 * @param {*[]} list
 * @param {{label: string, value: string}} option
 * @returns {*}
 */
export function getLabel(val, list = [], option = { label: 'label', value: 'value' }) {
    const obj = list2Map(list, option);
    return obj[val] || val;
}

然后在对应的页面引入getLabel方法即可。

欢迎大家提出自己的意见,也可以分享自己的优化方案。明天见!