大家好,我是渡一前端子辰老师。
众所周知,前端开发是一个不断追求优化的领域,无论是在代码质量、性能效率还是用户体验方面,都需要不断地探索和创新。
今天我们以 Vue 为例,介绍一个常见的前端优化问题:如何避免不必要的数据响应式处理,从而提升页面的加载速度和运行效率。
通过对比实验和代码分析,展示使用冻结对象(Object.freeze)的方法优化,从而有效地减少数据响应式处理的时间消耗,同时也会指出这种方法的局限性和适用场景。
优化
我们来看一个 Vue 的优化问题。
下面代码中有一个按钮,调用一个 loadDatas 函数,loadDatas 函数中调用 getDatas 函数并获得生成的 100 万条数据,然后保存到变量 datas 上。
datas 的变化会引起页面的重新渲染。
<template><div id="app"><button @click="loadDatas">loadDatas</button><h1>datas length:{{ datas.length }}</h1></div></template><script>export default {name: 'App',data() {return {datas: []}},methods: {loadDatas() {this.datas = this.getDatas()},getDatas() {const result = []for (let i = 0; i < 1000000; i++) {result.push({id: i,info: {avatar: `avatar${i}`,name: `name${i}`,age: `age${i}`,job: `job${i}`,address: `address${i}`,}})}return result}},}</script><template> <div id="app"> <button @click="loadDatas">loadDatas</button> <h1>datas length:{{ datas.length }}</h1> </div> </template> <script> export default { name: 'App', data() { return { datas: [] } }, methods: { loadDatas() { this.datas = this.getDatas() }, getDatas() { const result = [] for (let i = 0; i < 1000000; i++) { result.push({ id: i, info: { avatar: `avatar${i}`, name: `name${i}`, age: `age${i}`, job: `job${i}`, address: `address${i}`, } }) } return result } }, } </script><template> <div id="app"> <button @click="loadDatas">loadDatas</button> <h1>datas length:{{ datas.length }}</h1> </div> </template> <script> export default { name: 'App', data() { return { datas: [] } }, methods: { loadDatas() { this.datas = this.getDatas() }, getDatas() { const result = [] for (let i = 0; i < 1000000; i++) { result.push({ id: i, info: { avatar: `avatar${i}`, name: `name${i}`, age: `age${i}`, job: `job${i}`, address: `address${i}`, } }) } return result } }, } </script>
这段代码看起来很简单,但是它的效率却很低,为什么呢?
我们使用浏览器的性能工具记录一下,看看这段时间内它的主要性能消耗都到哪里去了。
你会发现整个时间里渲染的时间为 0,因为页面很简单,没有什么可渲染的。
而 JS 的加载就耗时 14852 毫秒,那是不是说主要损耗就在这 100 万次的循环呢?
现在马上去验证一下。
打开调用树找到函数,你会发现 loadDatas 函数耗时占用了 66.7%。
那么这个函数里边哪个地方执行时间最长呢?
我们发现根本不是 getDatas 函数,它循环了 100 万次,但是耗时只有 12.4%,所以说效率主要损耗不在这。
而是在 proxySetter 里,我们展开看看里边是什么。
学过 Vue 的同学一定都知道这个 observe 是什么。
Vue 就是靠它来完成数据的响应式的,它遍历数组,遍历数组里的每一个对象,遍历每一个属性,而且是深度遍历,把每一个属性使用 Object.defineProperty
来进行响应式处理,所以说时间主要损耗在这里了。
而我们有些数据压根不需要响应式,最常见的就是列表,或者一些表格数据基本是不需要响应式的。
数据响应式是用来监听数据数据变化的,那我们有没有办法告诉 Vue,我们给它的东西是不需要响应式的呢?
答案是有点的,就是使用冻结对象(Object.freeze)。
loadDatas() {this.datas = Object.freeze(this.getDatas())}loadDatas() { this.datas = Object.freeze(this.getDatas()) }loadDatas() { this.datas = Object.freeze(this.getDatas()) }
只需要将对象冻结,Vue 在处理数据时就会判断这个对象是否是冻结对象,判断的方式是使用 Object.isFrozen
这是 Vue 的内部实现,我们不用管。
当 Vue 发现你给它的数据是一个冻结对象的话,它就不会把对象变成响应式的。
我们再来看一下它的效率。
可以很明显的看到,性能有大幅度的提升。
我们将 this.datas
打印到控制台。
就会发现这些数据就是普普通通的对象,如果说它是一个响应式对象,数据应该是下图这样。
总结
随着效率提升的同时,对象也失去了响应式,你再去改动数据页面也不会收到通知。
所以说优化不能一概而论,学了优化之后把所有的优化手段全用上,要么代码不能看了,要么出的问题比解决的效率问题更大。
一定是因地制宜,具体情况具体分析的。
那么这就需要你懂得很多很多的优化手段,手上得有技术,不然你需要的时候就拿不出来了。
本文来源
本文来源自渡一官方公众号:Duing,欢迎关注,获取最新、最全、最深入的技术讲解
感谢你阅读本文,如果你有任何疑问或建议,请在评论区留言,如果你觉得这篇文章有用,请点赞收藏或分享给你的朋友!