在项目中,自定义了一个组件,在点击子组件时,触发选中事件,并经过$emit
,将子组件的数据传递给父组件,另外有一个全选按钮来触发子组件的所有选中。原本按照设想,子组件的事情交给子组件处理,在修改子组件的状态时经过$emit
来进行数据交互,可是在数据量超出必定程度时,$emit
很影响性能。html
其实这就是在组件开发中,如何处理onSelect
以及selectionChange
。性能优化
下面几段代码是父子组件的简单定义,在子组件中经过监听data.isSelect
的变化,来触发选中事件,设想是在使用组件的过程当中,依旧能够经过设置data.isSelect
字段,来更新组件状态,以及触发选中。dom
父组件主要是监听子组件的事件,组装selection
,而且将子组件的事件继续向外传递。性能
父组件template测试
<template>
<div>
<button v-model="checkedAll" @click="onSelectAll">全选</button>
<children v-for="item of children" @onSelect="onSelect" :data="item" ></children>
</div>
</template>
复制代码
父组件script大数据
export default {
data() {
return {
checkedAll: false,
children: [],
selection: [],
};
},
methods: {
onSelectAll() {
this.children.forEach((child) => {
child.isSelect = true;
});
},
onSelect(child, selectStatus) {
if (selectStatus) {
this.selection.push(child);
} else {
this.selection = this.selection.filter(item => item != child);
}
this.$emit('onSelect', child, selectStatus);
this.$emit('selectionChange', this.selection);
},
},
}
复制代码
子组件负责数据的展现,以及选中事件的处理,在 demo 中父组件只有一个,可是在实际项目中,有两个不一样类型的父组件使用,也是将共同逻辑抽出的一个良好的例子。优化
子组件templatethis
<template>
<div @click="onSelect">
<span>{{ data.isSelect ? 'checked' : 'unchecked' }}</span>
<span>{{ data.name }}</span>
</div>
</template>
复制代码
子组件scriptspa
export default {
props: ['data'],
methods: {
onSelect() {
this.data.isSelect = !this.data.isSelect;
},
},
watch: {
'data.isSelect': function() {
this.$emit('onSelect', this.data, this.data.isSelect);
},
},
}
复制代码
这个设计原本没有问题,可是在全选时,若是数据较多,如 2000+
,全交给watch
来处理$emit
事件,每次$emit
只有几百ms
,可是整个加起来一共会耗时几分钟。设计
没有解决办法时,选择参考优秀的开源项目,在el-table
的源码当中,toggleRowSelection
的源码以下,经过控制参数emitChange
来控制事件的触发与否,既能保证相同逻辑的复用,也能控制$emit
所带来的性能影响。虽然不知道 Element-UI 本意是否是控制性能,可是在实际测试中,对于大数据量的表格,选中事件的响应效果仍是很理想的,因此借鉴这个思路,在组件代码中增长控制事件相关的代码。
toggleRowSelection(row, selected, emitChange = true) {
const changed = toggleRowStatus(this.states.selection, row, selected);
if (changed) {
const newSelection = (this.states.selection || []).slice();
// 调用 API 修改选中值,不触发 select 事件
if (emitChange) {
this.table.$emit('select', newSelection, row);
}
this.table.$emit('selection-change', newSelection);
}
},
复制代码
因为父组件并非调用子组件的 API 修改子组件的状态,选择在子组件props
的data
中增长字段来控制,修改后的代码以下:
父组件 onSelectAll
onSelectAll() {
this.children.forEach((child) => {
child.updateByComponent = true;
child.isSelect = true;
this.$nextTick(() => {
child.updateByComponent = false;
});
});
}
复制代码
子组件 watch
watch: {
'data.isSelect': function() {
if (this.data.updateByComponent) return;
this.$emit('onSelect', this.data, this.data.isSelect);
},
}
复制代码
修改后的代码,将原来的响应时间由4分多钟,下降到了400毫秒,带来的实际体验仍是很好的。在一样的数据量状况下,选中效果基本上是及时生效。
这是项目中的一次性能优化的反思,也是一次开源项目源码的阅读过程,虽然组件是我本身编写的,坑是本身挖的,可是此次经历也是本身在组件封装上的一个成长。若是你有什么好的建议,欢迎在评论中提出。
在编写了最小 demo 之后,全选的响应在秒级,可是在项目中,更新isSelect
只涉及单个dom的更新,不知道为何会产生这样的影响。要了解透的话,得花时间一点一点的去尝试了。