跳转到内容

灵活使用冻结对象提升代码效率

刀刀

4/7/2025

0 字

0 分钟

下面来看一个场景:

点击按钮模拟 100 万条数据,追加到响应式的数组内,然后页面显示该数组的长度。效果如下图所示:

xiaoguo

代码如下所示:

js
data() {
    return {
        datas: []
    }
},
methods: {
    loadDatas() {
        this.datas = this.getDatas()
    },
    getDatas() {
        const result = []
        for(let i = 0; i < 100000; i++) {
            result.push({
                id: i,
                name: `name${i}`,
                address: {
                    city: `city${i}`,
                    province: `province${i}`
                }
            })
        }
        return result
    }
}

可以看到,它需要一段时间才能执行完毕,这还是快进过的效果。

那么,这个时间与性能的消耗主要在哪里呢?从代码来看,好像是 getDatas 函数耗时最久,因为他要循环 100 万 次。通过调试工具查看结果是否如此:

getdatas函数耗时

可以看到,它实际上只耗时了 600 毫秒 ,占全部时间的 8.7%,主要消耗不在它。而它上面的 proxySetter 耗时才是大头,占全部时间的 81.6% 。

层层展开其文件夹,发现有一个 observe 文件夹,它才是耗时的大头。

observe

学过 Vue 底层逻辑和源码的都知道,它是用来做数据的响应式的,遍历数组、对象,通过 Object.defineProperty 方法实现代理和深度劫持,实现响应式数据处理。该方法会深度遍历、递归,因此性能相对较差,耗时更多。

如果去掉响应式,那么它的耗时会不会下降呢?通过 Object.freeze() 方法冻结对象。代码如下所示:

js
data() {
    return {
        datas: []
    }
},
methods: {
    loadDatas() {
        this.datas = Object.freeze(this.getDatas())
    },
    getDatas() {
        // ...
    }
}

如果使用了冻结对象,则数据不再设为响应式,此时运行发现速度变得很快,打印 datas 内的数据,已经没有 getset 做响应式的操作了。

打印

这个优化有利有弊,利处就是提高效率,弊处就是它失去了响应式,修改内容后页面不再更新。因此需要结合实际业务场景,有针对性的做优化。

该优化的实际业务场景可以用于数据给死、不需要再次变量的场景,如新闻列表页面、首页导航等。