前端刷新处理数据的几种方式

文章目录

websocket

  • 一次握手 → 永久保持连接(直到主动关闭)
  • 双向通信:客户端 ↔ 服务器 随时互发消息
  • 服务器有新数据 → 立刻推给前端
  • 真正实时刷新数据
js 复制代码
// 连接 WebSocket
const ws = new WebSocket('ws://localhost:8080/ws');

// 收到服务端推送的新数据,自动刷新页面
ws.onmessage = (e) => {
  const newData = JSON.parse(e.data);
  renderPage(newData); 
};

// 发生错误/关闭
ws.onerror = () => {};
ws.onclose = () => {};

定时轮询(setInterval)

定时轮询是前端实现自动刷新数据最基础、最广为人知的方案。它通过 setInterval 定时器,每隔一段时间就向服务器发送一次请求,从而实现页面数据的自动更新。

js 复制代码
// 1. 定义数据请求函数
async function fetchData() {
  try {
    const res = await fetch('/api/your-data-endpoint');
    const data = await res.json();
    updateUI(data); // 更新页面
  } catch (err) {
    console.error('请求数据失败:', err);
  }
}

// 2. 设置定时器,每 3 秒(3000ms)请求一次
const intervalId = setInterval(fetchData, 3000);

// 3. 定义更新页面的方法(根据你的框架或原生DOM操作)
function updateUI(newData) {
  // 例如 Vue: this.list = newData;
  // 例如 原生: document.getElementById('data-container').innerText = newData.value;
  console.log('数据已更新:', newData);
}

页面离开或不再需要轮询时,必须清除定时器,否则会造成内存泄漏。

js 复制代码
// 清除定时器的两种常见时机

// 方式1:在某个事件中清除(如点击按钮)
function stopPolling() {
  clearInterval(intervalId);
  console.log('已停止轮询');
}

// 方式2:在页面卸载时清除(推荐)
window.addEventListener('beforeunload', () => {
  clearInterval(intervalId);
});

惰性轮询(setTimeout 递归)

setInterval 有一个致命缺点:如果接口请求耗时超过了定时时间,会导致多个请求堆积,阻塞主线程或造成服务器压力。

惰性轮询(递归 setTimeout)能完美解决这个问题。它的规则是:等上一次请求完成(成功或失败)后,再延迟指定时间,发起下一次请求。

js 复制代码
let timerId = null;

// 定义轮询函数
async function polling() {
  try {
    const res = await fetch('/api/your-data-endpoint');
    const data = await res.json();
    updateUI(data);
  } catch (err) {
    console.error('请求失败:', err);
  } finally {
    // 关键:无论成功失败,3秒后再执行下一次
    timerId = setTimeout(polling, 3000);
  }
}

// 启动轮询
polling();

// 停止轮询
function stopPolling() {
  clearTimeout(timerId);
}
优缺点
特性 setInterval (定时轮询) setTimeout (惰性轮询)
执行逻辑 固定时间间隔执行,不受请求耗时影响 上一次完成后,延迟固定时间再执行
请求堆积风险 (请求慢时会堆积) (串行执行,安全)
适用场景 短请求、对时序要求不高的简单场景 绝大多数业务场景(推荐)
代码复杂度 简单 稍复杂(需使用 finally

Web Worker 轮询

Web Worker 最大作用:开一个独立后台线程,不受主线程阻塞、页面切后台也不会被浏览器严重节流,用来做轮询非常稳,是普通项目里最实用的常驻刷新方案。

为什么要用 Web Worker 做轮询?

普通 setInterval 缺点:

  • 页面切后台 → 浏览器会节流 / 变慢 / 暂停定时器
  • JS 执行卡顿、渲染阻塞 → 定时器不准
  • 大量计算时,轮询直接 "卡住不执行"

Web Worker 优点:

  • 独立线程,不阻塞主线程
  • 切后台、页面隐藏依然相对稳定执行
  • 不会被 DOM 渲染、JS 阻塞影响
  • 兼容性极好(IE10+、所有现代浏览器都支持
vue2 写法

安装依赖

复制代码
npm install worker-loader -D

vue.config.js 配置

复制代码
module.exports = {
  configureWebpack: {
    module: {
      rules: [
        {
          test: /\.worker\.js$/,
          use: { loader: 'worker-loader' }
        }
      ]
    }
  }
}

创建 src/utils/poll.worker.js

js 复制代码
// 后台轮询线程
let timer = null

// 接收主线程消息
self.onmessage = (e) => {
  const { type, interval } = e.data

  // 开始轮询
  if (type === 'start') {
    clearInterval(timer)

    timer = setInterval(async () => {
      try {
        // 请求接口
        const res = await fetch('/api/notice')
        const data = await res.json()

        // 发给 Vue 页面
        self.postMessage({
          status: 'success',
          data
        })
      } catch (err) {
        self.postMessage({ status: 'error', msg: err.message })
      }
    }, interval)
  }

  // 停止
  if (type === 'stop') {
    clearInterval(timer)
  }
}

页面使用(Vue2 示例)

js 复制代码
import PollWorker from '@/utils/poll.worker.js'

export default {
  mounted() {
    this.worker = new PollWorker()

    this.worker.onmessage = (e) => {
      console.log('新数据:', e.data.data)
      // this.list = e.data.data
    }

    this.worker.postMessage({
      type: 'start',
      interval: 3000
    })
  },

  beforeDestroy() {
    this.worker.postMessage({ type: 'stop' })
    this.worker.terminate()
  }
}
Vue3 + Vite 写法(最常用)

Vite 内置支持 Web Worker,超级简单。

js 复制代码
<script setup>
import { onMounted, onUnmounted } from 'vue'

// 直接引入 Worker(Vite 语法)
import PollWorker from '@/utils/poll.worker?worker'

let worker = null

onMounted(() => {
  // 1. 创建 Worker
  worker = new PollWorker()

  // 2. 监听后台返回的新数据
  worker.onmessage = (e) => {
    if (e.data.status === 'success') {
      console.log('后台刷新数据:', e.data.data)
      // 这里更新 Vue 数据 → 页面自动刷新
      // list.value = e.data.data
    }
  }

  // 3. 启动轮询:3 秒一次
  worker.postMessage({
    type: 'start',
    interval: 3000
  })
})

// 页面销毁时关闭 Worker
onUnmounted(() => {
  if (worker) {
    worker.postMessage({ type: 'stop' })
    worker.terminate() // 销毁线程
  }
})
</script>
使用场景

页面切到后台 / 最小化,你依然希望轮询稳定执行

  • 比如后台管理系统、监控页面、客服系统
  • 用户切走窗口、最小化,普通 setInterval 会被浏览器节流、变慢、甚至暂停
  • Worker 不会被轻易暂停,能保持基本定时精度

页面本身很卡、JS 执行重,定时器不准

  • 大数据表格渲染、图表、大量 DOM 操作
  • 主线程一卡,定时器就 "跳秒"
  • Worker 是独立线程,不受主线程卡顿影响

worker 在使用结束后必须销毁,否则会导致内存泄露问题

Periodic Background Sync

它是浏览器级别的定时任务调度器,基于 Service Worker 运行,不受页面生命周期影响,专门用于后台定时同步数据。

Periodic Background Sync(周期性后台同步) 是专为 PWA 设计的、能在页面完全关闭后仍在后台定时执行网络任务的浏览器 API,完美解决你之前担心的 Worker 销毁、后台轮询失效问题。

核心机制
  • 注册:在 Service Worker 注册时,指定唯一标签(tag)和最小间隔(minInterval)。
  • 调度:浏览器内核接管计时,在设备联网、充电、闲置等低干扰时机触发。
  • 执行:唤醒 Service Worker,触发 periodicsync 事件,执行同步逻辑。
  • 持久化:注册后跨会话生效,直到主动取消。
代码示例

注册(主线程)

js 复制代码
// 等待 Service Worker 就绪
navigator.serviceWorker.ready.then(async (registration) => {
  // 检查支持
  if (!registration.periodicSync) return;

  try {
    // 注册:每 4 小时同步一次(最小间隔)
    await registration.periodicSync.register('order-sync', {
      minInterval: 4 * 60 * 60 * 1000 // 14400000ms
    });
    console.log('周期性同步注册成功');
  } catch (err) {
    console.error('注册失败(权限/浏览器限制)', err);
  }
}); 

监听与执行(Service Worker)

js 复制代码
// sw.js
self.addEventListener('periodicsync', (event) => {
  if (event.tag === 'order-sync') {
    // 必须用 waitUntil 保证任务完成
    event.waitUntil(
      fetch('/api/order/sync')
        .then(res => res.json())
        .then(data => {
          // 缓存新数据、更新 IndexedDB 等
          return caches.open('order-cache').then(cache => {
            return cache.put('/api/order/latest', new Response(JSON.stringify(data)));
          });
        })
        .catch(err => console.error('同步失败', err))
    );
  }
});
  • 浏览器支持:Chrome、Edge 支持;Firefox、Safari 暂不支持。
  • 权限要求:需用户授予 "后台同步" 权限。
  • 触发不保证:minInterval 是下限,浏览器会根据用户活跃度、电量、网络等策略调整,低活跃应用可能很久不触发。
  • 网络限制:仅在已连接过的 Wi-Fi / 蜂窝网络下触发,陌生网络不执行。
  • 任务时长:Service Worker 有超时限制(通常几分钟),长任务需拆分。

requestIdleCallback

requestIdleCallback(简称 rIC)是浏览器提供的主线程闲时调度 API,专门用来执行非紧急、非阻塞的后台任务,避免长任务卡住渲染与交互。

把不重要的任务见缝插针地放在浏览器空闲时段执行,优先保障渲染、动画、用户输入的流畅度。

二、核心原理
浏览器每帧(约 16ms)的工作

  • JS 执行 → 样式计算 → 布局 → 绘制 → 合成
  • 如果一帧提前完成(比如只用了 10ms),剩余时间就是空闲时段
  • requestIdleCallback 回调就在这个时段执行

回调会收到一个 deadline 对象

  • deadline.timeRemaining():当前空闲周期还剩多少毫秒(动态)
  • deadline.didTimeout:是否因超时被强制执行
js 复制代码
// 注册一个空闲回调
const handle = requestIdleCallback((deadline) => {
  // 只要还有空闲时间,就处理任务
  while (deadline.timeRemaining() > 0 && taskQueue.length > 0) {
    const task = taskQueue.shift();
    doHeavyWork(task); // 单次任务要轻
  }
  // 没做完,下次空闲继续
  if (taskQueue.length > 0) {
    requestIdleCallback(handle);
  }
}, {
  timeout: 2000 // 可选:2秒内没空闲就强制执行
});

// 取消
// cancelIdleCallback(handle);

SharedWorker

SharedWorker = 可以被多个标签页 / 多个窗口共享的同一个后台线程

这是它和普通 WebWorker 最核心的区别。
优点

  • 全局轮询、多页同步、避免重复请求
  • 多页面共享同一个
  • 最后一个页面关闭 → 才销毁
  • 基于 port 通信

代码示例

共享线程文件:shared.worker.js

js 复制代码
let timer = null;
let ports = [];

// 连接
self.onconnect = (e) => {
  const port = e.ports[0];
  ports.push(port);

  // 监听页面消息
  port.onmessage = (e) => {
    if (e.data === 'start') {
      startPoll();
    }
  };

  port.start();
};

// 全局唯一轮询
function startPoll() {
  if (timer) return;

  timer = setInterval(async () => {
    const res = await fetch('/api/notice');
    const data = await res.json();

    // 发给所有页面
    ports.forEach(port => {
      port.postMessage(data);
    });
  }, 3000);
}

页面中使用(任意页面都一样)

js 复制代码
const worker = new SharedWorker('/shared.worker.js');
const port = worker.port;

// 开启
port.start();

// 接收共享 Worker 发来的数据
port.onmessage = (e) => {
  console.log('新消息:', e.data);
};

// 启动轮询
port.postMessage('start');
相关推荐
FlyWIHTSKY2 小时前
Vue3 插槽(Slot)使用
前端·javascript·vue.js
血玥珏2 小时前
血玥珏-BMP名字图片生成器
前端·html
weixin199701080162 小时前
《QX 游戏商城商品详情页前端性能优化实战》
前端·游戏·性能优化
方也_arkling2 小时前
【Vue-Day12】Vue组件的生命周期
前端·javascript·vue.js
森叶2 小时前
深入解析:Claude 桌面应用与 Chrome 扩展的 Native Messaging 通信机制
前端·chrome
苏武难飞2 小时前
分享一个THREE.JS中无限滚动的技巧
前端·javascript·css
bitbrowser2 小时前
2026 PC端多Chrome账号管理指南:从日常切换到防关联实战
前端·chrome
小陈工2 小时前
Python Web开发入门(二):Flask vs Django,项目结构大比拼
前端·数据库·python·安全·web安全·django·flask