在开发 Tabs
组件的过程中,我发现了一个有趣的问题:在使用 ForEach
遍历 TabContent
的时候,切换 tab
会导致页面闪烁。下面是具体的代码和解决方法。
问题描述
代码如下所示
@State selectIndex: number = 0; // 当前选中的 tab 索引
build() {
Row() {
Tabs({ index: this.selectIndex, controller: this.controller }) {
ForEach(this.tabListData, (item: tabList, index) => {
TabContent() {
if (0 === this.selectIndex) {
dayTwoDemo();
} else if (1 === this.selectIndex) {
Text(item.title).fontSize(50);
} else if (2 === this.selectIndex) {
Text(item.title).fontSize(50);
} else if (3 === this.selectIndex) {
Mine();
}
}.tabBar(this.tabBarItem(item));
}, (item: tabList) => item.id.toString());
}.barPosition(BarPosition.End)
.onChange((index: number) => {
this.selectIndex = index;
});
}
}
点击 tab
切换时,页面会闪烁,体验很差。通过分析代码发现,这个问题是由于 @State
修饰的变量 selectIndex
被 ForEach
的内容引用。每次 selectIndex
改变时,整个 ForEach
的内容都会重新渲染,导致了 TabContent
不必要的重绘。
问题分析
@State
是一个响应式装饰器,任何对其引用的代码都会因状态改变而重新计算。在 ForEach
中,selectIndex
被引用,因此每次切换 tab
,即便只是当前 TabContent
的内容需要更新,所有 TabContent
也会被重新渲染。
解决方案
将 @State
修饰的变量从 ForEach
的内容中移除,改用 index
参数判断当前渲染的 TabContent
,从而避免额外的重绘。
修改后的代码如下:
build() {
Row() {
Tabs({ index: this.selectIndex, controller: this.controller }) {
ForEach(this.tabListData, (item: tabList, index) => {
TabContent() {
if (0 === index) {
dayTwoDemo();
} else if (1 === index) {
Text(item.title).fontSize(50);
} else if (2 === index) {
Text(item.title).fontSize(50);
} else if (3 === index) {
Mine();
}
}.tabBar(this.tabBarItem(item));
}, (item: tabList) => item.id.toString());
}.barPosition(BarPosition.End)
.onChange((index: number) => {
this.selectIndex = index;
});
}
}
总结
优化关键点
- 避免在循环中直接使用响应式变量:在
ForEach
中避免直接引用@State
修饰的变量,以减少不必要的重绘。 - 善用循环参数:利用循环
index
来判断渲染内容,替代响应式状态,优化性能。
收获与启发
在开发中,我们常常会不经意地滥用响应式变量,导致性能问题。理解其渲染机制,减少不必要的重绘,可以显著提升应用的用户体验。
希望这个经验分享能帮助你在开发中少踩坑!🎉