Notes

实时与离线:WebSocket, PWA 与 Service Worker

一句话结论:通过 WebSocket 或 SSE 赋予应用实时通信的能力,并通过 Service Worker 缓存机制和 PWA 标准构建可离线访问、媲美原生体验的 Web 应用,是现代前端突破传统请求-响应模型、迈向更高用户体验维度的关键技术。


1. 实时通信 (Real-time Communication)

当应用需要即时地从服务器接收更新(如聊天消息、通知、实时股价)时,传统的轮询 (polling) 方式效率低下且延迟高。

A. WebSocket

WebSocket 提供了一个在单个 TCP 连接上进行全双工、双向通信的协议。一旦连接建立,客户端和服务器都可以随时向对方发送数据。

  • 优点
    • 真正的双向通信:客户端和服务器地位平等。
    • 低延迟:连接开销小,数据传输快。
    • 性能高:HTTP 头部开销非常小。
  • 缺点
    • 需要专门的服务器支持。
    • 在某些严格的网络环境下可能会被防火墙阻止。
  • 适用场景:在线聊天、多人协作编辑、实时游戏、需要客户端频繁向服务器发送数据的场景。

在 React 中使用: 通常我们会封装一个自定义 Hook 来管理 WebSocket 连接的生命周期、状态和消息收发。

// useWebSocket.js
import { useState, useEffect, useRef } from 'react';

export function useWebSocket(url) {
  const [messages, setMessages] = useState([]);
  const ws = useRef(null);

  useEffect(() => {
    ws.current = new WebSocket(url);
    ws.current.onopen = () => console.log('WebSocket connected');
    ws.current.onclose = () => console.log('WebSocket disconnected');

    ws.current.onmessage = (event) => {
      setMessages(prev => [...prev, JSON.parse(event.data)]);
    };

    return () => {
      ws.current.close();
    };
  }, [url]);

  const sendMessage = (message) => {
    if (ws.current.readyState === WebSocket.OPEN) {
      ws.current.send(JSON.stringify(message));
    }
  };

  return { messages, sendMessage };
}

B. Server-Sent Events (SSE)

SSE 是一个浏览器 API,它允许服务器单向地向客户端推送事件流。它建立在常规的 HTTP/HTTPS 连接之上。

  • 优点
    • 简单:基于标准 HTTP,无需特殊服务器,易于实现和调试。
    • 自动重连:浏览器会自动处理断线重连。
    • 兼容性好:能通过几乎所有的网络环境。
  • 缺点
    • 单向通信:只能从服务器到客户端。
    • 有连接数限制(浏览器对同域名下的 HTTP 连接数有限制)。
  • 适用场景:新闻源更新、股票价格推送、通知提醒、构建状态更新等只读的实时数据流。

在 React 中使用: 与 WebSocket 类似,可以封装一个 Hook 来处理。

// useEventSource.js
import { useState, useEffect } from 'react';

export function useEventSource(url) {
  const [data, setData] = useState(null);

  useEffect(() => {
    const eventSource = new EventSource(url);
    
    // 监听特定类型的事件
    eventSource.addEventListener('update', (event) => {
      setData(JSON.parse(event.data));
    });

    eventSource.onerror = () => {
      // 处理错误,可以关闭连接
      eventSource.close();
    };

    return () => {
      eventSource.close();
    };
  }, [url]);

  return data;
}

2. 离线能力:PWA 与 Service Worker

A. PWA (Progressive Web App)

PWA 不是一种单一的技术,而是一套使 Web 应用具备类似原生应用(如可安装、离线工作、推送通知)体验的最佳实践和 Web 标准的集合。

核心要求

  1. 必须通过 HTTPS 提供服务
  2. 必须有一个 Web App Manifest (清单文件):一个 JSON 文件,定义了应用的名称、图标、启动 URL、显示模式等,使其“可安装”。
  3. 必须注册一个 Service Worker:这是实现离线能力和推送通知的关键。

B. Service Worker

Service Worker 是一个在浏览器后台独立于主线程运行的 JavaScript 脚本。它充当了 Web 应用、浏览器和网络之间的网络代理

  • 核心能力拦截和处理网络请求。它可以检查请求,并决定是从网络获取响应,还是从缓存中提供响应。
  • 生命周期install -> activate -> fetch / message
  • 离线缓存策略
    • Cache First (缓存优先):优先从缓存中读取。如果缓存中没有,再发起网络请求,并将成功的结果存入缓存。适合静态资源(JS, CSS, 图片)。
    • Network First (网络优先):优先从网络获取。如果网络失败,再从缓存中读取。适合需要最新数据的 API 请求(如新闻列表)。
    • Stale-While-Revalidate (后台更新):同时从缓存和网络获取。立即返回缓存的数据(让用户快速看到内容),然后用网络请求回来的新数据更新缓存,并在下次访问时使用。这是平衡性能和数据新鲜度的绝佳策略。

示例:一个简单的 Service Worker (sw.js)

const CACHE_NAME = 'my-app-cache-v1';
const URLS_TO_CACHE = [
  '/',
  '/styles/main.css',
  '/script/main.js',
  '/images/logo.png'
];

// 安装时,缓存核心静态资源
self.addEventListener('install', event => {
  event.waitUntil(
    caches.open(CACHE_NAME)
      .then(cache => {
        return cache.addAll(URLS_TO_CACHE);
      })
  );
});

// 拦截 fetch 事件,实施 Cache First 策略
self.addEventListener('fetch', event => {
  event.respondWith(
    caches.match(event.request)
      .then(response => {
        // 如果缓存中有匹配的响应,则返回它
        if (response) {
          return response;
        }
        // 否则,通过网络获取
        return fetch(event.request);
      })
  );
});

在应用中注册 Service Worker

if ('serviceWorker' in navigator) {
  window.addEventListener('load', () => {
    navigator.serviceWorker.register('/sw.js')
      .then(registration => console.log('ServiceWorker registration successful'))
      .catch(err => console.log('ServiceWorker registration failed: ', err));
  });
}

C. 使用 Workbox

手动编写和管理 Service Worker 很复杂。Workbox 是 Google 推出的一个库,它封装了大量最佳实践,极大地简化了 Service Worker 的开发。

  • 功能:提供路由管理、预缓存、多种缓存策略(如 CacheFirst, NetworkFirst)、后台同步等高级功能。
  • 集成:可以与 Webpack, Vite 等构建工具深度集成,自动生成 Service Worker 文件。

4. 关联阅读

cd ..