You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

*NgFor加载50个动态Angular组件初始化过慢(15秒)的优化求助

优化方案

针对一次性加载50个动态组件导致页面卡顿的问题,以下是几个落地性强的优化方向:

1. 利用折叠面板展开事件延迟加载组件

默认所有面板展开,但可以改为仅在面板展开时才加载对应动态组件,初始仅渲染面板头部,大幅降低初始DOM渲染压力。

修改步骤:

  • 模板中绑定展开状态到数组,控制组件加载时机:
<mat-expansion-panel [expanded]="expandedItems[i]" *ngFor="let item of formItemList; let i = index" id="{{item.id}}"
            class="pad-top-sm expansion-panel">
    <mat-expansion-panel-header id="{{'mat-header' + i}}">
        <div>
            <a id="{{'mat-help' + i}}" href="{{ item.help }}" class="inline" title="help" target="_blank">
                <s-icon name="help-circle" class="relative active small margin-right-xs">
                </s-icon>
            </a>
            <h3 id="{{'mat-expansion-label' + i}}" class="inline">{{ item.label }}</h3>
        </div>
    </mat-expansion-panel-header>
    <ng-container app-add-component [component]="item.component" [loadWhenExpanded]="expandedItems[i]"></ng-container>
</mat-expansion-panel>
  • 父组件初始化展开状态数组,可默认展开前N个:
expandedItems: boolean[] = Array(50).fill(false);
// 示例:默认展开前5个
// expandedItems = Array(50).fill(false).map((_, idx) => idx < 5);
  • 修改动态组件指令,仅在展开时创建组件:
@Directive({
    selector: '[app-add-component]',
})
export class AddComponentDirective implements OnChanges {

    @Input() component: Type<any> | undefined;
    @Input() loadWhenExpanded: boolean = false;

    constructor(public viewContainerRef: ViewContainerRef) {}

    ngOnChanges(changes: SimpleChanges) {
        if (changes['loadWhenExpanded'] && this.loadWhenExpanded && this.component) {
            this.viewContainerRef.clear();
            this.viewContainerRef.createComponent(this.component);
        }
    }
}

如果需要默认全部展开但仍延迟加载,可通过分批触发展开避免阻塞:

ngAfterViewInit() {
    // 每100ms展开一个,错开渲染时间
    this.formItemList.forEach((_, idx) => {
        setTimeout(() => {
            this.expandedItems[idx] = true;
        }, idx * 100);
    });
}

2. 分批异步加载组件

若必须一次性展示所有展开面板,可将组件创建逻辑分批执行,利用浏览器空闲时间加载,避免主线程阻塞。

父组件实现分批加载:

batchSize = 5; // 每批加载5个
currentBatch = 0;
loadedItems: boolean[] = Array(50).fill(false);

ngAfterViewInit() {
    this.loadNextBatch();
}

loadNextBatch() {
    const start = this.currentBatch * this.batchSize;
    const end = Math.min(start + this.batchSize, this.formItemList.length);
    
    // 标记当前批次组件为可加载
    for (let i = start; i < end; i++) {
        this.loadedItems[i] = true;
    }

    this.currentBatch++;
    if (end < this.formItemList.length) {
        // 利用浏览器空闲时间加载下一批
        requestIdleCallback(() => this.loadNextBatch());
        // 也可使用固定延迟:setTimeout(() => this.loadNextBatch(), 100);
    }
}

模板中仅加载标记为可加载的组件:

<ng-container *ngIf="loadedItems[i]" app-add-component [component]="item.component"></ng-container>

3. 优化动态组件变更检测

给FormComponent启用OnPush变更检测策略,减少不必要的变更检测循环:

@Component({
    selector: 'app-form-component',
    templateUrl: './form-component.html',
    changeDetection: ChangeDetectionStrategy.OnPush
})
export class FormComponent {}

4. 移除不必要的DOM属性

如果动态生成的id(如mat-header + i这类)没有实际交互或测试需求,直接删除,减少DOM渲染时的属性处理开销。

5. 确保动态组件预编译

确保FormComponent已在模块的declarations中声明,Angular 9+会自动预编译组件,避免运行时编译的额外开销。


内容的提问来源于stack exchange,提问作者Shashank Dhasal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 20:31:11