You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

分页组件向服务器发送过多请求的原因排查及修复咨询

问题原因分析

看起来你遇到的是重复订阅叠加导致的请求爆炸问题,我来帮你拆解一下核心原因:

  1. 重复创建订阅:每次分页变化时调用getUsers(),而这个方法内部会重新创建一个merge(this.sort.sortChange, this.paginator.page)的订阅,但之前的订阅并没有被销毁。随着翻页次数增加,订阅数量会等于当前页码——比如翻到第15页时,已经累计了15个订阅,每个订阅都会在分页事件触发时发起请求,直接导致请求次数翻倍增长,页面卡顿。
  2. 冗余触发逻辑onPaginateChange调用getUsers(),而getUsers()里的merge已经监听了paginator.page事件,相当于分页事件每触发一次,就新增一个订阅,同时触发所有已存在的订阅,进一步加剧了请求次数的增长。从最后一页切换时初始是2次请求,就是因为页面初始化时已经有一个订阅,切换时又新增一个,累计2次后每翻页一次就多一个订阅。
修复方案

这里提供两种最常用的修复方式,推荐第一种更简洁的写法:

方案1:移除重复的订阅创建(最推荐)

将监听排序和分页事件的逻辑移到ngOnInit中,只初始化一次订阅,不再在onPaginateChange中调用getUsers()。这样分页事件触发时,只有一个订阅会响应,自然只会发起一次请求。

修改后的组件代码:

import { Component, OnInit, ViewChild } from '@angular/core';
import { merge, observableOf } from 'rxjs';
import { switchMap, map, catchError } from 'rxjs/operators';
import { MatPaginator } from '@angular/material/paginator';
import { MatSort } from '@angular/material/sort';
// 导入你的UsersService和相关类型

@Component({
  // 组件元数据保持不变
})
export class YourComponent implements OnInit {
  // 你的变量声明:search, pageSize, totalItems等
  @ViewChild(MatPaginator) paginator!: MatPaginator;
  @ViewChild(MatSort) sort!: MatSort;

  ngOnInit() {
    // 监听排序变化,自动重置页码到第1页
    this.sort.sortChange.subscribe(() => {
      this.paginator.pageIndex = 0;
      this.page = 1; // 同步组件内的page变量
    });

    // 合并排序和分页事件,只初始化一次订阅
    merge(this.sort.sortChange, this.paginator.page)
      .pipe(
        startWith({}), // 页面加载时自动触发一次请求
        switchMap(() => {
          this.loading = true;
          // 直接从paginator获取最新页码,避免依赖组件变量不一致
          return this.usersService!.fetch(
            this.search,
            this.sort.active,
            this.sort.direction,
            this.pageSize,
            this.paginator.pageIndex + 1
          );
        }),
        map(data => {
          this.loading = false;
          this.isTotalReached = false;
          this.totalItems = data.rowCount;
          return data.users;
        }),
        catchError(() => {
          this.loading = false;
          this.isTotalReached = true;
          return observableOf([]);
        })
      )
      .subscribe(data => {
        this.users = data;
      });
  }

  onPaginateChange(event) {
    // 这里只需要同步组件内的分页变量,不需要调用getUsers()
    this.page = event.pageIndex + 1;
    this.pageSize = event.pageSize;
    // paginator.page事件已经被merge监听,会自动触发请求
  }

  // 如果有搜索功能,搜索时可以手动触发分页事件来刷新数据
  onSearchInputChange() {
    this.paginator.pageIndex = 0;
    this.page = 1;
    this.paginator.page.next({
      pageIndex: 0,
      pageSize: this.pageSize,
      length: this.totalItems
    });
  }
}

方案2:手动管理订阅生命周期(适合需保留getUsers()调用的场景)

如果因为业务逻辑需要保留getUsers()的调用方式,那么需要在每次调用前取消之前的订阅,避免重复订阅叠加:

import { Component, OnDestroy, ViewChild } from '@angular/core';
import { Subscription, merge, observableOf } from 'rxjs';
import { switchMap, map, catchError } from 'rxjs/operators';
import { MatPaginator } from '@angular/material/paginator';
import { MatSort } from '@angular/material/sort';

@Component({
  // 组件元数据保持不变
})
export class YourComponent implements OnDestroy {
  // 你的变量声明
  @ViewChild(MatPaginator) paginator!: MatPaginator;
  @ViewChild(MatSort) sort!: MatSort;
  private usersSubscription?: Subscription; // 用于管理订阅生命周期

  getUsers() {
    // 先取消之前的订阅,防止重复请求
    this.usersSubscription?.unsubscribe();

    this.sort.sortChange.subscribe(() => (this.paginator.pageIndex = 0));

    this.usersSubscription = merge(this.sort.sortChange, this.paginator.page)
      .pipe(
        startWith({}),
        switchMap(() => {
          this.loading = true;
          return this.usersService!.fetch(
            this.search,
            this.sort.active,
            this.sort.direction,
            this.pageSize,
            this.page
          );
        }),
        map(data => {
          this.loading = false;
          this.isTotalReached = false;
          this.totalItems = data.rowCount;
          return data.users;
        }),
        catchError(() => {
          this.loading = false;
          this.isTotalReached = true;
          return observableOf([]);
        })
      )
      .subscribe(data => {
        this.users = data;
      });
  }

  onPaginateChange(event) {
    this.page = event.pageIndex + 1;
    this.pageSize = event.pageSize;
    this.getUsers();
  }

  // 组件销毁时必须取消订阅,防止内存泄漏
  ngOnDestroy() {
    this.usersSubscription?.unsubscribe();
  }
}

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

火山引擎 最新活动