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

文章目录

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');
相关推荐
weixin_382395239 小时前
为小工厂量身打造:本地部署的物料管理系统带缺料计算
前端·制造
__zRainy__9 小时前
解决pnpm v10+不自动构建
前端·pnpm·工程化
猫猫不是喵喵.9 小时前
Vue3 Props 属性
前端·javascript·vue.js
醉城夜风~10 小时前
CSS元素显示模式(display)
前端·css
AI大模型-小华11 小时前
Codex 三方充值快速入门指南
java·前端·数据库·chatgpt·ai编程·codex·chatgpt pro
做前端的娜娜子13 小时前
同一链接实现 PC Web 与移动 H5 自适应
前端·掘金·金石计划
小帅不太帅13 小时前
架构没变、规模没变,DeepSeek V4 Flash 正式版凭什么暴涨 47 分?
前端·aigc·deepseek
jarvisuni14 小时前
DeepSeekFlash前端依旧拉垮,而且变慢了很多!
前端·javascript·算法
卷福同学15 小时前
AI编程出海第二步:验证关键词能否做站
前端·人工智能·后端