最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

前端SSE流式請(qǐng)求的三種實(shí)現(xiàn)方案

 更新時(shí)間:2026年03月03日 08:53:56   作者:不可能的是  
本文深入對(duì)比了前端實(shí)現(xiàn) SSE(Server-Sent Events)流式請(qǐng)求的三種主流方案,包括原理剖析、代碼示例、優(yōu)劣對(duì)比和實(shí)戰(zhàn)踩坑經(jīng)驗(yàn),幫助開發(fā)者選擇最適合的技術(shù)方案,需要的朋友可以參考下

一、什么是 SSE?

SSE(Server-Sent Events)是 HTML5 標(biāo)準(zhǔn)的一部分,允許服務(wù)器主動(dòng)向客戶端推送數(shù)據(jù)。與 WebSocket 雙向通信不同,SSE 是單向的(服務(wù)器 → 客戶端),但實(shí)現(xiàn)更簡(jiǎn)單,且基于 HTTP 協(xié)議。

SSE 的典型應(yīng)用場(chǎng)景

  • AI 對(duì)話流式輸出:ChatGPT、Claude 等 AI 助手的打字機(jī)效果
  • 實(shí)時(shí)通知推送:消息提醒、系統(tǒng)通知
  • 實(shí)時(shí)數(shù)據(jù)更新:股票行情、體育比分
  • 進(jìn)度監(jiān)控:文件上傳進(jìn)度、任務(wù)執(zhí)行狀態(tài)

SSE 協(xié)議格式

data: 消息內(nèi)容\n\n

完整格式包含多個(gè)字段:

event: message\n id: 1\n retry: 3000\n data: 消息內(nèi)容\n\n
  • data: - 消息內(nèi)容(必需)
  • event: - 事件類型(可選,默認(rèn)為 message)
  • id: - 消息 ID(可選,用于斷線重連)
  • retry: - 重連間隔(可選,單位毫秒)
  • \n\n - 事件分隔符(兩個(gè)換行符)

二、三種實(shí)現(xiàn)方案對(duì)比

方案一:原生 EventSource API

基本用法

const eventSource = new EventSource('/api/sse');

eventSource.onmessage = (event) => {
  console.log('收到消息:', event.data);
};

eventSource.onerror = (error) => {
  console.error('連接錯(cuò)誤:', error);
  eventSource.close();
};

// 手動(dòng)關(guān)閉連接
eventSource.close();

完整封裝示例

class SSEClient {
  constructor(url, options = {}) {
    this.url = url;
    this.options = options;
    this.eventSource = null;
    this.isCompleted = false;
  }
  
  connect() {
    this.eventSource = new EventSource(this.url);
    
    this.eventSource.onmessage = (event) => {
      // 檢查結(jié)束信號(hào)
      if (event.data === '[DONE]') {
        this.close('done');
        return;
      }
      
      // 觸發(fā)消息回調(diào)
      this.options.onMessage?.(event.data);
    };
    
    this.eventSource.onerror = () => {
      if (!this.isCompleted) {
        this.options.onError?.(new Error('SSE 連接異常'));
      }
      this.close('error');
    };
  }
  
  close(reason = 'manual') {
    if (this.isCompleted) return;
    
    this.isCompleted = true;
    this.eventSource?.close();
    this.options.onComplete?.(reason);
  }
}

// 使用示例
const client = new SSEClient('/api/chat', {
  onMessage: (data) => {
    console.log('收到:', data);
  },
  onComplete: (reason) => {
    console.log('結(jié)束:', reason);
  },
  onError: (err) => {
    console.error('錯(cuò)誤:', err);
  },
});

client.connect();

// 手動(dòng)停止
client.close();

優(yōu)勢(shì)

? 零依賴:瀏覽器原生 API,無(wú)需引入任何庫(kù)
? 自動(dòng)解析:瀏覽器自動(dòng)處理 SSE 協(xié)議解析
? 自動(dòng)重連:連接斷開后自動(dòng)重連(可配置 retry 時(shí)間)
? 穩(wěn)定可靠:瀏覽器原生實(shí)現(xiàn),經(jīng)過(guò)充分測(cè)試
? 代碼簡(jiǎn)潔:核心邏輯僅需 20-30 行代碼

劣勢(shì)

? 僅支持 GET 請(qǐng)求:無(wú)法發(fā)送 POST 請(qǐng)求體
? 無(wú)法自定義 Header:除了 Cookie,無(wú)法添加其他 Header(如 Authorization)
? 認(rèn)證限制:只能通過(guò) URL 參數(shù)或 Cookie 傳遞認(rèn)證信息

適用場(chǎng)景

  • 后端接口支持 GET 請(qǐng)求
  • 認(rèn)證信息通過(guò) Cookie 自動(dòng)攜帶
  • 追求零依賴和最簡(jiǎn)實(shí)現(xiàn)
  • 不需要自定義 Header

常見踩坑

1. 跨域問(wèn)題

// 錯(cuò)誤:跨域請(qǐng)求會(huì)失敗
const es = new EventSource('https://other-domain.com/sse');

// 解決方案 1:服務(wù)器設(shè)置 CORS
// Access-Control-Allow-Origin: *
// Access-Control-Allow-Credentials: true

// 解決方案 2:使用代理
const es = new EventSource('/api/proxy/sse');

2. 自動(dòng)重連導(dǎo)致重復(fù)請(qǐng)求

// 問(wèn)題:連接斷開后會(huì)自動(dòng)重連,可能導(dǎo)致重復(fù)請(qǐng)求

// 解決方案:服務(wù)器端使用 id 字段去重
// event: message
// id: 123
// data: 消息內(nèi)容

// 客戶端重連時(shí)會(huì)自動(dòng)發(fā)送 Last-Event-ID header

3. 無(wú)法傳遞 POST 數(shù)據(jù)

// 錯(cuò)誤:EventSource 不支持 POST
const es = new EventSource('/api/chat', {
  method: 'POST',  // 無(wú)效!
  body: JSON.stringify({ message: 'Hello' })
});

// 解決方案:先通過(guò) POST 保存數(shù)據(jù),再用 GET 建立 SSE 連接
async function sendMessage(message) {
  // 1. POST 保存消息
  const res = await fetch('/api/messages', {
    method: 'POST',
    body: JSON.stringify({ message }),
  });
  const { messageId } = await res.json();
  
  // 2. GET 建立 SSE 連接
  const es = new EventSource(`/api/sse?messageId=${messageId}`);
}

方案二:Fetch API + ReadableStream

基本用法

async function fetchSSE(url, options = {}) {
  const response = await fetch(url, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': 'Bearer token',
    },
    body: JSON.stringify({ message: 'Hello' }),
  });

  const reader = response.body.getReader();
  const decoder = new TextDecoder();
  let buffer = '';

  while (true) {
    const { done, value } = await reader.read();
    
    if (done) {
      options.onComplete?.();
      break;
    }

    // 解碼二進(jìn)制數(shù)據(jù)
    buffer += decoder.decode(value, { stream: true });
    
    // 按行分割(SSE 以 \n\n 分隔事件)
    const lines = buffer.split('\n\n');
    buffer = lines.pop() || ''; // 保留不完整的行

    for (const line of lines) {
      if (line.startsWith('data: ')) {
        const data = line.slice(6);
        
        if (data === '[DONE]') {
          options.onComplete?.();
          return;
        }
        
        options.onMessage?.(data);
      }
    }
  }
}

// 使用示例
fetchSSE('/api/chat', {
  onMessage: (data) => {
    console.log('收到:', data);
  },
  onComplete: () => {
    console.log('完成');
  },
});

核心原理詳解

1. ReadableStream 流式讀取

const reader = response.body.getReader();

while (true) {
  const { done, value } = await reader.read();
  
  if (done) break;
  
  // value 是 Uint8Array 類型的二進(jìn)制數(shù)據(jù)
  console.log(value);
}

2. TextDecoder 解碼

const decoder = new TextDecoder();

// { stream: true } 很重要!
// 處理多字節(jié)字符被拆分到不同 chunk 的情況
const text = decoder.decode(value, { stream: true });

為什么需要 { stream: true }

// 假設(shè) "你好" 的 UTF-8 編碼被拆分到兩個(gè) chunk
// chunk1: [0xE4, 0xBD]  // "你" 的前兩個(gè)字節(jié)
// chunk2: [0xA0, 0xE5, 0xA5, 0xBD]  // "你" 的最后一個(gè)字節(jié) + "好"

const decoder = new TextDecoder();

// 不使用 stream: true(錯(cuò)誤)
decoder.decode(chunk1);  // 亂碼或報(bào)錯(cuò)
decoder.decode(chunk2);  // 亂碼或報(bào)錯(cuò)

// 使用 stream: true(正確)
decoder.decode(chunk1, { stream: true });  // "" (等待完整字符)
decoder.decode(chunk2, { stream: true });  // "你好"

3. Buffer 緩沖區(qū)管理

let buffer = '';

// 收到 chunk1: "data: Hello\n"
buffer += "data: Hello\n";
const lines = buffer.split('\n\n');  // ["data: Hello\n"]
buffer = lines.pop();  // buffer = "data: Hello\n"

// 收到 chunk2: "\ndata: World\n\n"
buffer += "\ndata: World\n\n";  // "data: Hello\n\ndata: World\n\n"
const lines = buffer.split('\n\n');  // ["data: Hello", "data: World", ""]
buffer = lines.pop();  // buffer = ""

// 處理完整的事件
for (const line of lines) {
  if (line.startsWith('data: ')) {
    const data = line.slice(6);
    onMessage(data);  // "Hello", "World"
  }
}

4. 支持中斷

const controller = new AbortController();

fetch(url, {
  signal: controller.signal,
});

// 中斷請(qǐng)求
controller.abort();

完整封裝示例

class FetchSSEClient {
  constructor(url, options = {}) {
    this.url = url;
    this.options = options;
    this.controller = new AbortController();
  }
  
  async connect() {
    try {
      const response = await fetch(this.url, {
        method: this.options.method || 'POST',
        headers: this.options.headers || {},
        body: this.options.body,
        signal: this.controller.signal,
      });

      if (!response.ok) {
        throw new Error(`HTTP ${response.status}`);
      }

      const reader = response.body.getReader();
      const decoder = new TextDecoder();
      let buffer = '';

      while (true) {
        const { done, value } = await reader.read();
        
        if (done) {
          this.options.onComplete?.('done');
          break;
        }

        buffer += decoder.decode(value, { stream: true });
        
        const lines = buffer.split('\n\n');
        buffer = lines.pop() || '';

        for (const line of lines) {
          if (line.startsWith('data: ')) {
            const data = line.slice(6);
            
            if (data === '[DONE]') {
              this.options.onComplete?.('done');
              return;
            }
            
            this.options.onMessage?.(data);
          }
        }
      }
    } catch (error) {
      if (error.name === 'AbortError') {
        this.options.onComplete?.('abort');
      } else {
        this.options.onError?.(error);
        this.options.onComplete?.('error');
      }
    }
  }
  
  abort() {
    this.controller.abort();
  }
}

// 使用示例
const client = new FetchSSEClient('/api/chat', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer token',
  },
  body: JSON.stringify({ message: 'Hello' }),
  onMessage: (data) => {
    console.log('收到:', data);
  },
  onComplete: (reason) => {
    console.log('結(jié)束:', reason);
  },
  onError: (err) => {
    console.error('錯(cuò)誤:', err);
  },
});

client.connect();

// 手動(dòng)停止
client.abort();

優(yōu)勢(shì)

? 支持 POST 請(qǐng)求:可以發(fā)送請(qǐng)求體
? 自定義 Header:可以添加任意 Header(如 Authorization)
? 完全控制:可以自定義超時(shí)、重試等邏輯
? 零依賴:瀏覽器原生 API
? 支持中斷:使用 AbortController 中斷請(qǐng)求

劣勢(shì)

? 需要手動(dòng)解析 SSE 協(xié)議:需要處理 data:event:、id: 等字段
? 需要處理 chunk 邊界:一個(gè)事件可能被拆分到多個(gè) chunk
? 需要管理 buffer:手動(dòng)拼接不完整的數(shù)據(jù)
? 代碼復(fù)雜度高:核心邏輯需要 50+ 行代碼
? 容易出錯(cuò):邊界情況處理不當(dāng)會(huì)導(dǎo)致數(shù)據(jù)丟失或重復(fù)

適用場(chǎng)景

  • 需要 POST 請(qǐng)求和自定義 Header
  • 需要完全控制請(qǐng)求行為
  • 后端返回的不是標(biāo)準(zhǔn) SSE 格式
  • 需要讀取響應(yīng) Header 或狀態(tài)碼

常見踩坑

1. 忘記使用 { stream: true }

// 錯(cuò)誤:多字節(jié)字符會(huì)亂碼
const text = decoder.decode(value);

// 正確
const text = decoder.decode(value, { stream: true });

2. buffer 管理錯(cuò)誤

// 錯(cuò)誤:直接 split 會(huì)丟失不完整的數(shù)據(jù)
const lines = buffer.split('\n\n');
for (const line of lines) {
  // 處理...
}
buffer = '';  // 錯(cuò)誤!最后一個(gè)可能不完整

// 正確:保留最后一個(gè)元素
const lines = buffer.split('\n\n');
buffer = lines.pop() || '';  // 保留不完整的部分
for (const line of lines) {
  // 處理...
}

3. 沒(méi)有處理中斷

// 錯(cuò)誤:沒(méi)有處理 AbortError
try {
  await fetch(url, { signal });
} catch (error) {
  console.error(error);  // AbortError 也會(huì)被當(dāng)作錯(cuò)誤
}

// 正確:區(qū)分 AbortError
try {
  await fetch(url, { signal });
} catch (error) {
  if (error.name === 'AbortError') {
    console.log('用戶取消');
  } else {
    console.error('請(qǐng)求失敗:', error);
  }
}

4. 代理層緩沖問(wèn)題

// 問(wèn)題:Nginx 等代理可能會(huì)緩沖響應(yīng),導(dǎo)致消息延遲

// 解決方案:服務(wù)器端配置
// Nginx:
// proxy_buffering off;
// proxy_cache off;
// proxy_set_header Connection '';
// proxy_http_version 1.1;
// chunked_transfer_encoding off;

// Node.js:
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
res.flushHeaders();  // 立即發(fā)送 headers

方案三:@microsoft/fetch-event-source

基本用法

npm install @microsoft/fetch-event-source
import { fetchEventSource } from '@microsoft/fetch-event-source';

fetchEventSource('/api/chat', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer token',
  },
  body: JSON.stringify({ message: 'Hello' }),
  
  onmessage(event) {
    if (event.data === '[DONE]') {
      return;
    }
    console.log('收到:', event.data);
  },
  
  onclose() {
    console.log('連接關(guān)閉');
  },
  
  onerror(err) {
    console.error('錯(cuò)誤:', err);
    throw err; // 拋出錯(cuò)誤會(huì)停止重連
  },
  
  openWhenHidden: true, // 頁(yè)面隱藏時(shí)保持連接
});

高級(jí)用法

class MSFetchSSEClient {
  constructor(url, options = {}) {
    this.url = url;
    this.options = options;
    this.ctrl = new AbortController();
  }
  
  async connect() {
    await fetchEventSource(this.url, {
      method: this.options.method || 'POST',
      headers: this.options.headers || {},
      body: this.options.body,
      signal: this.ctrl.signal,
      
      onmessage: (event) => {
        if (event.data === '[DONE]') {
          this.abort();
          return;
        }
        this.options.onMessage?.(event.data);
      },
      
      onclose: () => {
        this.options.onComplete?.('done');
      },
      
      onerror: (err) => {
        this.options.onError?.(err);
        // 拋出錯(cuò)誤會(huì)停止重連
        throw err;
      },
      
      openWhenHidden: true,
    });
  }
  
  abort() {
    this.ctrl.abort();
  }
}

// 使用示例
const client = new MSFetchSSEClient('/api/chat', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer token',
  },
  body: JSON.stringify({ message: 'Hello' }),
  onMessage: (data) => {
    console.log('收到:', data);
  },
  onComplete: (reason) => {
    console.log('結(jié)束:', reason);
  },
  onError: (err) => {
    console.error('錯(cuò)誤:', err);
  },
});

client.connect();

// 手動(dòng)停止
client.abort();

優(yōu)勢(shì)

? 支持 POST 請(qǐng)求:可以發(fā)送請(qǐng)求體
? 自定義 Header:可以添加任意 Header
? 自動(dòng)解析 SSE 協(xié)議:無(wú)需手動(dòng)處理
? 內(nèi)置自動(dòng)重連:連接斷開后自動(dòng)重連
? 頁(yè)面隱藏時(shí)保持連接openWhenHidden 選項(xiàng)
? 體積小:~2KB gzip
? TypeScript 支持:完整的類型定義

劣勢(shì)

? 需要引入外部依賴:增加打包體積
? 自動(dòng)重連可能不符合需求:需要手動(dòng)控制
? 錯(cuò)誤處理復(fù)雜:需要拋出錯(cuò)誤才能停止重連
? 中斷機(jī)制不直觀:需要使用 AbortController

適用場(chǎng)景

  • 需要 POST 請(qǐng)求和自定義 Header
  • 需要自動(dòng)重連功能
  • 不介意引入外部依賴
  • 追求開發(fā)效率

常見踩坑

1. 自動(dòng)重連導(dǎo)致重復(fù)請(qǐng)求

// 問(wèn)題:連接斷開后會(huì)自動(dòng)重連,可能導(dǎo)致重復(fù)請(qǐng)求

// 解決方案 1:拋出錯(cuò)誤停止重連
onerror(err) {
  console.error(err);
  throw err;  // 停止重連
}

// 解決方案 2:使用 AbortController
const ctrl = new AbortController();
fetchEventSource(url, {
  signal: ctrl.signal,
  onerror(err) {
    ctrl.abort();  // 停止重連
  },
});

2. 無(wú)法獲取完成狀態(tài)

// 問(wèn)題:onclose 回調(diào)無(wú)法區(qū)分正常結(jié)束還是錯(cuò)誤結(jié)束

// 解決方案:自己維護(hù)狀態(tài)
let isCompleted = false;

fetchEventSource(url, {
  onmessage(event) {
    if (event.data === '[DONE]') {
      isCompleted = true;
      ctrl.abort();
    }
  },
  onclose() {
    if (isCompleted) {
      console.log('正常結(jié)束');
    } else {
      console.log('異常結(jié)束');
    }
  },
});

3. 頁(yè)面隱藏時(shí)連接斷開

// 問(wèn)題:默認(rèn)情況下,頁(yè)面隱藏時(shí)連接會(huì)斷開

// 解決方案:設(shè)置 openWhenHidden
fetchEventSource(url, {
  openWhenHidden: true,  // 頁(yè)面隱藏時(shí)保持連接
});

三、方案對(duì)比總結(jié)

維度EventSourceFetch + ReadableStream@microsoft/fetch-event-source
支持 POST? 僅 GET??
自定義 Header? 僅 Cookie??
SSE 協(xié)議解析? 瀏覽器原生? 需手動(dòng)實(shí)現(xiàn)? 自動(dòng)處理
自動(dòng)重連? 瀏覽器原生? 需手動(dòng)實(shí)現(xiàn)? 內(nèi)置
手動(dòng)中斷? close()? AbortController?? AbortController + throw
代碼復(fù)雜度低(~30行)高(~50行)中(~10行)
外部依賴零依賴零依賴~2KB
瀏覽器兼容性IE 10+現(xiàn)代瀏覽器現(xiàn)代瀏覽器
學(xué)習(xí)成本
適用場(chǎng)景GET + Cookie 認(rèn)證完全自定義POST + 自動(dòng)重連

四、選型建議

選擇 EventSource 的情況

  • ? 后端接口支持 GET 請(qǐng)求
  • ? 認(rèn)證信息通過(guò) Cookie 自動(dòng)攜帶
  • ? 追求零依賴和最簡(jiǎn)實(shí)現(xiàn)
  • ? 不需要自定義 Header

選擇 Fetch + ReadableStream 的情況

  • ? 需要完全控制請(qǐng)求行為
  • ? 后端返回的不是標(biāo)準(zhǔn) SSE 格式
  • ? 需要讀取響應(yīng) Header 或狀態(tài)碼
  • ? 需要自定義超時(shí)、重試邏輯

選擇 @microsoft/fetch-event-source 的情況

  • ? 需要 POST 請(qǐng)求和自定義 Header
  • ? 需要自動(dòng)重連功能
  • ? 不介意引入外部依賴
  • ? 追求開發(fā)效率

五、實(shí)戰(zhàn)經(jīng)驗(yàn)總結(jié)

1. 如何處理認(rèn)證

方案 A:Cookie(推薦 EventSource)

// 服務(wù)器設(shè)置 Cookie
res.cookie('token', 'xxx', { httpOnly: true });

// 客戶端自動(dòng)攜帶
const es = new EventSource('/api/sse');

方案 B:URL 參數(shù)(適用 EventSource)

const token = 'xxx';
const es = new EventSource(`/api/sse?token=${token}`);

方案 C:Authorization Header(需要 Fetch)

fetch('/api/sse', {
  headers: {
    'Authorization': `Bearer ${token}`,
  },
});

2. 如何處理超時(shí)

EventSource(無(wú)法設(shè)置超時(shí))

// 解決方案:手動(dòng)實(shí)現(xiàn)超時(shí)
const es = new EventSource('/api/sse');
const timeout = setTimeout(() => {
  es.close();
  console.log('超時(shí)');
}, 30000);

es.onmessage = () => {
  clearTimeout(timeout);
};

Fetch(使用 AbortController)

const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 30000);

fetch('/api/sse', {
  signal: controller.signal,
});

3. 如何處理重連

EventSource(自動(dòng)重連)

// 服務(wù)器端設(shè)置重連間隔
res.write('retry: 3000\n\n');  // 3 秒后重連

// 客戶端無(wú)法禁用自動(dòng)重連
// 只能通過(guò) close() 關(guān)閉連接

Fetch(手動(dòng)重連)

async function connectWithRetry(url, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      await fetchSSE(url, { /* ... */ });
      break;  // 成功,退出循環(huán)
    } catch (error) {
      if (i === maxRetries - 1) {
        throw error;  // 最后一次重試失敗
      }
      await new Promise(resolve => setTimeout(resolve, 3000));
    }
  }
}

4. 如何處理代理層緩沖

問(wèn)題: Nginx 等代理可能會(huì)緩沖響應(yīng),導(dǎo)致消息延遲

解決方案:

# Nginx 配置
location /api/sse {
    proxy_pass http://backend;
    proxy_buffering off;
    proxy_cache off;
    proxy_set_header Connection '';
    proxy_http_version 1.1;
    chunked_transfer_encoding off;
}
// Node.js 服務(wù)器
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
res.setHeader('X-Accel-Buffering', 'no');  // 禁用 Nginx 緩沖
res.flushHeaders();  // 立即發(fā)送 headers

5. 如何處理跨域

CORS 配置:

// 服務(wù)器端
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Credentials', 'true');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');

代理方案:

// 開發(fā)環(huán)境使用代理
// vite.config.js
export default {
  server: {
    proxy: {
      '/api': {
        target: 'https://backend.com',
        changeOrigin: true,
      },
    },
  },
};

六、總結(jié)

SSE 是實(shí)現(xiàn)服務(wù)器推送的簡(jiǎn)單而強(qiáng)大的技術(shù),三種實(shí)現(xiàn)方案各有優(yōu)劣:

  • EventSource:零依賴、最簡(jiǎn)單,適合 GET 請(qǐng)求場(chǎng)景
  • Fetch + ReadableStream:完全控制、最靈活,適合復(fù)雜場(chǎng)景
  • @microsoft/fetch-event-source:功能完善、開箱即用,適合快速開發(fā)

選擇哪種方案取決于具體需求,建議優(yōu)先考慮 EventSource,只有在需要 POST 或自定義 Header 時(shí)才使用其他方案。

以上就是前端SSE流式請(qǐng)求的三種實(shí)現(xiàn)方案的詳細(xì)內(nèi)容,更多關(guān)于前端SSE流式請(qǐng)求實(shí)現(xiàn)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • javascript設(shè)計(jì)模式 – 代理模式原理與用法實(shí)例分析

    javascript設(shè)計(jì)模式 – 代理模式原理與用法實(shí)例分析

    這篇文章主要介紹了javascript設(shè)計(jì)模式 – 代理模式,結(jié)合實(shí)例形式分析了javascript代理模式相關(guān)概念、原理、用法及操作注意事項(xiàng),需要的朋友可以參考下
    2020-04-04
  • php的派發(fā)機(jī)制實(shí)現(xiàn)方法

    php的派發(fā)機(jī)制實(shí)現(xiàn)方法

    PHP是一種動(dòng)態(tài)類型的編程語(yǔ)言,它支持面向?qū)ο缶幊?在PHP中,派發(fā)指在運(yùn)行時(shí)確定要調(diào)用的方法或函數(shù)的過(guò)程,派發(fā)機(jī)制允許根據(jù)實(shí)際對(duì)象的類型來(lái)選擇要執(zhí)行的方法,這種靈活性使得PHP可以實(shí)現(xiàn)多態(tài)性,本文將給大家介紹php的派發(fā)機(jī)制是怎么實(shí)現(xiàn)的,需要的朋友可以參考下
    2023-10-10
  • bootstrap table操作技巧分享

    bootstrap table操作技巧分享

    這篇文章主要為大家分享了bootstrap table操作技巧,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-02-02
  • JavaScript事件處理的方式(三種)

    JavaScript事件處理的方式(三種)

    最近這段時(shí)間因?yàn)槊刻煲薷木W(wǎng)站,為網(wǎng)站做特效,所以看了很多的js接觸事件,自己只會(huì)使用一小部分,有時(shí)用的時(shí)候也比較混亂,現(xiàn)在系統(tǒng)的整理了一下,特此分享到腳本之家平臺(tái)供大家參考下
    2016-04-04
  • JavaScript 函數(shù)式編程實(shí)踐(來(lái)自IBM)

    JavaScript 函數(shù)式編程實(shí)踐(來(lái)自IBM)

    說(shuō)到函數(shù)式編程,人們的第一印象往往是其學(xué)院派,晦澀難懂,大概只有那些蓬頭散發(fā),不修邊幅,甚至有些神經(jīng)質(zhì)的大學(xué)教授們才會(huì)用的編程方式。
    2010-06-06
  • js防抖和節(jié)流的深入講解

    js防抖和節(jié)流的深入講解

    這篇文章主要給大家介紹了關(guān)于js防抖和節(jié)流的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用js具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2018-12-12
  • Servlet返回的數(shù)據(jù)js解析2種方法

    Servlet返回的數(shù)據(jù)js解析2種方法

    這篇文章主要介紹了Servlet返回的數(shù)據(jù)js解析2種方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-12-12
  • JSONP原理及應(yīng)用實(shí)例詳解

    JSONP原理及應(yīng)用實(shí)例詳解

    這篇文章主要介紹了JSONP原理及應(yīng)用實(shí)例詳解,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-09-09
  • layui之select的option疊加問(wèn)題的解決方法

    layui之select的option疊加問(wèn)題的解決方法

    下面小編就為大家分享一篇layui之select的option疊加問(wèn)題的解決方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2018-03-03
  • 防止文件緩存的js代碼

    防止文件緩存的js代碼

    原理其實(shí)就是通過(guò)在固定地址后面,加上一個(gè)不同值的日期數(shù)值,以達(dá)到地址不重復(fù)的目的,讓瀏覽器每次都實(shí)時(shí)加載,不從緩存中讀取文件
    2013-01-01

最新評(píng)論

凤山县| 齐齐哈尔市| 乌拉特前旗| 根河市| 大邑县| 巧家县| 汾西县| 平遥县| 宜丰县| 柳江县| 甘泉县| 和田市| 米林县| 中卫市| 芒康县| 南川市| 南召县| 梁山县| 永城市| 长治县| 乌拉特中旗| 运城市| 蕲春县| 旌德县| 江城| 镇安县| 沙洋县| 城步| 合江县| 乌审旗| 新巴尔虎左旗| 渭南市| 霍山县| 沙湾县| 广平县| 江西省| 个旧市| 泸水县| 兴业县| 榆中县| 丹寨县|