Vue中使用$set导致性能卡顿的问题排查与解决方案咨询
我通过循环chosenNamespaces数组渲染了多组包含三个<select>的<div>组件,需求是修改对应namespace的Parameter选择框选项时,通过updateParameter方法同步更新Property选择框的值,因此使用$set更新currPropertyValues数组。但发现每次修改参数选项后页面响应需4-5秒,移除updateParameter中的$set则响应立即恢复正常,已经复现了该卡顿现象,请问如何解决此性能问题?
相关代码:
<div v-for="namespace in chosenNamespaces" v-bind:key="namespace.id"> <!-- Select the Parameter--> <select @change="updateParameter($event, namespace.id)" v-model="currParameterValues[namespace.id]"> <option v-for="parameter in getParametersForNamespace(namespace.id)">{{parameter}}</option> </select> <!-- Select Property --> <select @change="updatePropertyType($event, namespace.id)" v-model="currPropertyValues[namespace.id]"> <option v-for="property in getPropertiesForNamespace(namespace.id)">{{property}}</option> </select> <!-- Select Item --> <select v-model="currItemValues[namespace.id]"> <option v-for="item in getItemsForNamespace(namespace.id)">{{item}}</option> </select> </div>
methods: { updateParameter(data, id){ // ...其他逻辑 this.$set(this.currParameterValues, id, data.target.value) this.$set(this.currPropertyValues, id, someValue) }, updatePropertyType(data, id){ // ...其他逻辑 this.$set(this.currPropertyValues, someThing, val) } }
我来帮你分析下问题根源,再给出几个针对性的优化方案:
1. 把数组改成对象存储,避免无效响应式槽位
首先检查你的currPropertyValues初始化方式——如果它是数组而非对象,用id作为索引调用$set会导致Vue被迫扩展数组到对应长度,填充大量undefined的响应式槽位。比如如果id是1000,数组会被撑到1001个元素,这会让响应式系统和渲染过程产生极大的额外开销。
改成对象初始化就能解决这个问题:
data() { return { currPropertyValues: {}, // 用对象存储,key为namespace.id currParameterValues: {}, currItemValues: {} } }
这样更新时只会触发对应key的响应式更新,不会影响其他元素。
2. 不要在模板中用方法作为v-for数据源
你在模板里用getParametersForNamespace(namespace.id)这类方法生成选项,Vue会在每次重渲染时都调用这些方法——哪怕数据根本没变化。如果chosenNamespaces数量多,或者方法内部有复杂计算,这会直接拖慢渲染速度。
建议用计算属性提前缓存这些数据:
computed: { namespaceCachedData() { return this.chosenNamespaces.reduce((acc, ns) => { acc[ns.id] = { parameters: this.getParametersForNamespace(ns.id), properties: this.getPropertiesForNamespace(ns.id), items: this.getItemsForNamespace(ns.id) } return acc }, {}) } }
然后模板里改成:
<select @change="updateParameter($event, namespace.id)" v-model="currParameterValues[namespace.id]"> <option v-for="parameter in namespaceCachedData[namespace.id].parameters" :key="parameter">{{parameter}}</option> </select>
计算属性只会在依赖的chosenNamespaces或相关数据变化时重新计算,避免了重复调用方法的开销。
3. 减少$set的使用,直接赋值即可
如果currPropertyValues是在data里初始化的响应式对象,只有添加新key时才需要$set,更新已存在的key直接赋值就行:
updateParameter(data, id){ // ...其他逻辑 this.currParameterValues[id] = data.target.value this.currPropertyValues[id] = someValue }
直接赋值的响应式更新更轻量,不会触发$set带来的额外响应式系统处理。
4. 用v-once减少静态内容重渲染
如果某些select的选项不会动态变化,可以给对应的<option>加上v-once指令,让Vue只渲染一次该内容,后续不再重渲染:
<option v-for="parameter in namespaceCachedData[namespace.id].parameters" :key="parameter" v-once>{{parameter}}</option>
内容的提问来源于stack exchange,提问作者sharin gan

