页面如何减少请求次数,前端如何实现一个页面请求共用

大家好,我是Sven,最近遇到一个问题需要前端页面实现重复请求共用,我这边写了个简单的的方案文档,包含基于 Vue 2(ES5 语法)的实现代码,以及事件命名和定时刷新请求开关的考虑:

页面接口请求数据共用方案文档

一、方案背景

当前页面存在多个功能模块均需调用 data 接口获取数据的情况,原直接使用 ajax 的方式存在请求混乱、资源浪费等问题。为优化数据请求流程,提高数据获取效率,降低接口调用频率,决定将 data 接口调用从直接 ajax 方式改为发布订阅模式实现,并且基于 Vue 2(ES5 语法)进行开发。

二、核心目标

  1. 采用发布订阅模式统一管理页面内 data 接口调用,实现数据请求的有序处理。
  2. 当多个功能模块请求数据时,确保同一时间只有一个请求去调用 ajax 获取数据,其他请求排队等待,减少接口并发压力。
  3. 定义两种数据请求事件类型:即时请求和定时刷新请求,满足不同场景下的数据获取需求。
  4. 使用 JavaScript ES5 语法结合 Vue 2 实现方案,代码简洁高效,并添加注释和日志输出。
  5. 提供定时刷新请求开关,方便控制定时刷新功能。

三、方案设计

3.1 整体架构

  1. 事件中心:作为发布订阅模式的核心,负责接收各功能模块的订阅请求,管理请求队列,处理数据请求事件。在 Vue 2 中可借助 pmBusiness 对象的 emiton 方法实现事件的发布与订阅。
  2. 功能模块:向事件中心订阅 data 接口数据,根据自身需求发起即时请求或定时刷新请求。
  3. 数据获取模块:当事件中心确定需要调用 ajax 获取数据时,由数据获取模块执行 ajax 请求,并将获取到的数据返回给事件中心。

3.2 事件类型

  1. 即时请求事件:功能模块首次加载或有即时数据需求时,向事件中心发起即时请求。事件中心按时间顺序将请求加入队列,依次处理。事件名:IMMEDIATE_DATA_REQUEST
  2. 定时刷新事件:设定固定时间间隔(如 30 秒),由事件中心定时触发 data 接口数据请求,获取最新数据后通知所有订阅的功能模块。事件名:TIMED_DATA_REFRESH

3.3 请求处理流程

  1. 请求入队:当功能模块发起 data 接口数据请求时,无论即时请求还是定时刷新请求,均将请求加入事件中心的请求队列,事件中心记录请求时间戳,按时间顺序排序。
  2. 首位请求处理:事件中心优先处理队列中排在第一位的请求。若当前已有缓存数据,直接判断数据是否满足该请求需求(如数据是否过期)。若满足则直接返回数据给请求的功能模块;若不满足或无缓存数据,则调用 ajax 获取数据。
  3. 后续请求处理:首位请求调用 ajax 获取数据返回后,事件中心依次处理队列中后续请求。对于每个后续请求,检查当前缓存数据是否满足其需求,若满足则直接返回数据;若不满足,则继续判断该请求是否为即时请求。若是即时请求,则重新调用 ajax 获取数据;若是定时刷新请求且距离上次刷新时间未超过设定间隔(如 30 秒),则不调用 ajax,等待下一次定时刷新。
  4. 数据更新与通知:当 ajax 获取到新数据后,事件中心更新缓存数据,并通知所有订阅的功能模块数据已更新,功能模块根据新数据进行相应处理。

3.4 首次加载多请求排队机制

在首次加载时,多个功能模块可能同时向事件中心请求数据。为确保事件中心只查一次 data 接口,让后面的请求排队,事件中心在接收到第一个请求时,会立即标记为正在请求数据。后续请求进入队列时,若发现正在请求数据,则直接排队等待。当第一个请求获取到数据后,更新缓存数据并依次处理队列中的后续请求。

3.5 定时刷新请求开关

提供一个开关变量,用于控制定时刷新请求的开启和关闭。当开关开启时,事件中心按照设定的时间间隔触发定时刷新请求;当开关关闭时,停止定时刷新请求。

四、实现细节

4.1 事件中心实现

// 事件中心构造函数
function EventCenter(pmBusiness) {
    this.pmBusiness = pmBusiness;
    // 存储请求队列
    this.requestQueue = [];
    // 存储缓存数据
    this.cacheData = null;
    // 记录上次刷新时间
    this.lastRefreshTime = null;
    // 标记是否正在请求数据
    this.isRequesting = false;
    // 定时刷新请求开关
    this.timedRefreshEnabled = false;
    // 定时器 ID
    this.timerId = null;

    // 监听即时请求事件
    this.pmBusiness.on('IMMEDIATE_DATA_REQUEST', this.handleImmediateRequest.bind(this));
    // 监听定时刷新事件
    this.pmBusiness.on('TIMED_DATA_REFRESH', this.handleTimedRefreshRequest.bind(this));
}

// 处理即时请求方法
EventCenter.prototype.handleImmediateRequest = function (callback) {
    var request = {
        type: 'immediate',
        callback: callback,
        timestamp: new Date().getTime()
    };
    // 将请求加入队列
    this.requestQueue.push(request);
    console.log('即时请求已加入队列:', request);
    // 处理请求队列
    this.processQueue();
};

// 处理定时刷新请求方法
EventCenter.prototype.handleTimedRefreshRequest = function (callback) {
    var request = {
        type: 'timed',
        callback: callback,
        timestamp: new Date().getTime()
    };
    // 将请求加入队列
    this.requestQueue.push(request);
    console.log('定时刷新请求已加入队列:', request);
    // 处理请求队列
    this.processQueue();
};

// 处理请求队列方法
EventCenter.prototype.processQueue = function () {
    if (this.isRequesting || this.requestQueue.length === 0) {
        return;
    }
    // 取出队列首位请求
    var firstRequest = this.requestQueue.shift();
    this.isRequesting = true;
    console.log('开始处理请求:', firstRequest);
    // 检查缓存数据
    if (this.cacheData && this.isDataValid()) {
        console.log('使用缓存数据');
        firstRequest.callback(this.cacheData);
        this.isRequesting = false;
        // 继续处理后续请求
        this.processQueue();
    } else {
        console.log('调用 ajax 获取数据');
        // 调用数据获取模块获取数据
        this.fetchData(function (data) {
            this.cacheData = data;
            this.lastRefreshTime = new Date().getTime();
            firstRequest.callback(data);
            this.isRequesting = false;
            // 继续处理后续请求
            this.processQueue();
        }.bind(this));
    }
};

// 检查数据是否有效方法
EventCenter.prototype.isDataValid = function () {
    if (!this.lastRefreshTime) {
        return false;
    }
    var now = new Date().getTime();
    // 假设数据有效期为 30 秒
    return (now - this.lastRefreshTime) < 30000;
};

// 数据获取方法
EventCenter.prototype.fetchData = function (callback) {
    // 模拟 ajax 请求
    setTimeout(function () {
        var data = { message: '这是从 data 接口获取的数据' };
        console.log('成功获取数据:', data);
        callback(data);
    }, 1000);
};

// 开启定时刷新方法
EventCenter.prototype.startTimedRefresh = function () {
    this.timedRefreshEnabled = true;
    this.timerId = setInterval(function () {
        if (this.timedRefreshEnabled) {
            this.pmBusiness.emit('TIMED_DATA_REFRESH', function (data) {
                console.log('定时刷新接收到数据:', data);
            });
        }
    }.bind(this), 30000);
};

// 关闭定时刷新方法
EventCenter.prototype.stopTimedRefresh = function () {
    this.timedRefreshEnabled = false;
    clearInterval(this.timerId);
};

4.2 功能模块调用示例

// 假设 pmBusiness 是一个 Vue 2 对象,有 emit 和 on 方法
var pmBusiness = new Vue();

// 创建事件中心实例
var eventCenter = new EventCenter(pmBusiness);

// 功能模块 1 发起即时请求
function module1Callback(data) {
    console.log('功能模块 1 接收到数据:', data);
}
pmBusiness.emit('IMMEDIATE_DATA_REQUEST', module1Callback);

// 开启定时刷新
eventCenter.startTimedRefresh();

五、测试与验证

  1. 功能测试:模拟多个功能模块同时发起即时请求和定时刷新请求,验证事件中心是否按照设计流程处理请求,功能模块是否能正确获取数据。
  2. 性能测试:在高并发情况下,测试接口调用次数、响应时间等性能指标,评估方案是否达到优化效果,是否存在性能瓶颈。
  3. 数据一致性测试:检查不同功能模块获取到的数据是否一致,确保数据在更新和分发过程中不出现错误。
  4. 定时刷新开关测试:测试定时刷新请求开关的开启和关闭功能,验证开关状态是否能正确控制定时刷新请求的触发。

六、风险与应对措施

  1. 数据缓存问题:若缓存数据未及时更新或更新错误,可能导致功能模块获取到错误数据。应对措施:加强数据更新逻辑的测试,增加数据校验机制,确保缓存数据的准确性。
  2. 请求队列堵塞:在极端情况下,请求队列可能因大量请求而堵塞,影响数据获取效率。应对措施:设置请求队列长度限制,当队列满时,可根据策略(如丢弃旧请求或延迟处理)进行处理,同时优化请求处理流程,提高处理速度。
  3. 接口调用失败ajax 请求可能因网络问题或接口服务异常导致失败。应对措施:增加请求重试机制,设定合理的重试次数和间隔时间;同时在接口调用失败时,及时通知功能模块,以便进行相应的错误提示或业务处理。
  4. 定时刷新问题:定时刷新可能会导致不必要的接口调用,增加服务器压力。应对措施:合理设置定时刷新时间间隔,根据业务需求灵活调整开关状态。

以上方案详细说明了基于 Vue 2(ES5 语法)的实现思路和代码示例,包括事件命名、定时刷新请求开关等功能。当然了,还有完善的空间,可以根据实际情况对代码进行调整和扩展。

点击查看代码

posted @ 2025-05-15 15:10  程序员斯文  阅读(50)  评论(0)    收藏  举报