前端写SSE请求,实现live Tail效果

本文介绍了如何在React应用中使用EventSource(SSE)实现类似IDE控制台的日志实时推送功能,包括事件源的创建、错误处理、节流渲染以及滚动效果的同步。重点讲解了Webpack配置和避免内存泄漏的注意事项。
摘要由CSDN通过智能技术生成

控制台的EventStream是什么意思?是后端有大量数据推送到view层

SSE是什么? SSE是server sent event 的缩写, 去搜索服务端推送数据SSE

需求: 实现像idea控制台输出日志功能, 所以需要在后端拿log数据, 点击开关开始live Tail

方法: 下面是点击按钮触发的函数

// 点击开始live Tail
  async function handleLiveTail(flag: boolean) {
    setLiveTailRefresh(false);
    setMoreBtn(false);
    setEmpty(false);
    isScroll.current = true;
    setLiveTailSwitch(flag);
    if (!flag) {
      setContentSource([]);
      setLoading(true);
      sessionId.current = String(dayjs().valueOf());
      const _eventSource = await new EventSource(
        `/skyeye/log/livetail/fetch?uniqueCode=${uniqueCode}&logFileName=${pathName}&searchText=${searchText}&sessionId=${sessionId.current}`,
        { withCredentials: true },
      );
      _eventSource.onopen = (_e) => {
        eventSource.current = _eventSource;
      };
      _eventSource.onerror = (e) => {
        console.log('error________', e);
        if (liveTailSwitch) {
          eventSource.current?.close();
        }
      };
      _eventSource.onmessage = (e) => {
        if (e && e?.data) {
          let view: string[] = [];
          setContentSource((value) => {
            const html = hljs.highlight(e?.data, { language: 'java' }).value;
            let newData = value;
            const len = value?.length;
            if (len >= 1000) {
              newData.shift();
            }
            newData?.push(html);
            view = newData;
            return newData;
          });
          viewData(view);
        }
      };
      setLoading(false);
    }
  }

  // 节流渲染数据 后端推送数据太快页面卡段
  const viewData = throttle((data: string[]) => {
    setViewList([...data]);
    const codeBox = document.getElementById('code_box')?.children;
    if (codeBox && codeBox?.length > 0 && codeBox[0]) {
      scrollTop.current = codeBox[0]?.scrollTop;
      if (isScroll.current) {
        codeBox[0].scrollTop = codeBox[0]?.scrollHeight;
      }
    }
  }, 2000);
渲染位置

这里用了react-window做了虚拟滚动, 因为最多可展示1000行

<code id={'code_box'} style={{ visibility: loading && !moreBtn ? 'hidden' : 'visible' }}>
  <List
    height={codeHeight?.height || 600}
    itemCount={viewList?.length}
    itemSize={24}
    width={1136}
    style={{ margin: '0 8px', overflowAnchor: 'none' }}
  >
    {({ index, style }) => {
      return <div key={index} style={style} dangerouslySetInnerHTML={{ __html: viewList[index] }}></div>;
    }}
  </List>
</code>
重点一: 是webpack配置文件中需要加上compress: false

不加这个请求就会一直在pending, 大概等一分钟才会有数据返回, 也可能是项目配置的问题

// 测试服务
  devServer: {
    compress: false,
}
重点二: 一定要在error方法中判断用户是否关闭了live Tail, 和在页面关闭页面销毁的时候调用EventSource.close()

后端返回较慢, 或者请求一直在pending, 或者是nginx配置错误的时候,

就会走到eventSource.onerror方法 并会自己发起重连请求, 假如与用户手动点击关闭错开, 就会造成内存泄漏,程序错误等问题

结果: 发送请求之后就是这样

模拟控制台的滚动效果

另外再写一个细节的需求, Live Tail时,后端返回的最新内容,滚动滑块也应该在最底部, 应当是一直向上滚动, 展示最新的数据; 当用户手动向上滚动后, 后端返回最新内容, 滚动滑块位置不改变。

实现方法:

一、监听滚动,判断滚动滑块的高度是否大于等于后端返回数据时保存的高度,

大于时: 设置isSrcoll为true. 表示后端返回新数据后,滚动滑块也滚动到最新位置

小于时: 为false. 表示用户向上查看, 后端返回新数据, 滚动滑块位置不改变

// 监听滚动条
  useEventListener(
    'scroll',
    (_e) => {
      const codeBox = document.getElementById('code_box')?.children;
      if (codeBox && codeBox?.length > 0 && codeBox[0] && codeBox[0]?.scrollTop >= scrollTop.current) {
        isScroll.current = true;
      } else {
        isScroll.current = false;
      }
    },
    { target: document.getElementById('code_box')?.children ? document.getElementById('code_box')?.children[0] : null },
  );

二、在后端返回数据的方法里判断isSrcoll, 并存储滚动条高度

const codeBox = document.getElementById('code_box')?.children;
if (codeBox && codeBox?.length > 0 && codeBox[0]) {
  scrollTop.current = codeBox[0]?.scrollTop;
  if (isScroll.current) {
     codeBox[0].scrollTop = codeBox[0]?.scrollHeight;
   }
}

以下是一个简单的实现锦鲤的前端代码: ```html <!DOCTYPE html> <html> <head> <title>锦鲤</title> <style> body { background-color: #F7F7F7; display: flex; align-items: center; justify-content: center; height: 100vh; margin: 0; } .koi { width: 200px; height: 100px; position: relative; } .koi .body { width: 150px; height: 100px; background-color: #FFC300; border-radius: 50% 50% 50% 50% / 60% 60% 40% 40%; position: absolute; top: 0; left: 25px; transform-origin: center right; animation: swim 2s ease-in-out infinite; } .koi .tail { width: 0; height: 0; border-top: 50px solid transparent; border-right: 50px solid #FFC300; border-bottom: 50px solid transparent; position: absolute; top: 25px; left: -50px; transform-origin: center right; animation: swish 2s ease-in-out infinite; } .koi .head { width: 50px; height: 50px; background-color: #FFC300; border-radius: 50%; position: absolute; top: 25px; left: 130px; transform-origin: center left; animation: nod 2s ease-in-out infinite; } .koi .eye { width: 20px; height: 20px; background-color: white; border-radius: 50%; position: absolute; top: 10px; left: 120px; transform-origin: center; animation: blink 2s ease-in-out infinite; } .koi .eye::before { content: ""; display: block; width: 10px; height: 10px; background-color: black; border-radius: 50%; position: absolute; top: 5px; left: 5px; } @keyframes swim { 0% { transform: rotate(0); } 50% { transform: rotate(30deg); } 100% { transform: rotate(0); } } @keyframes swish { 0% { transform: rotate(0); } 50% { transform: rotate(10deg); } 100% { transform: rotate(0); } } @keyframes nod { 0% { transform: rotate(0); } 50% { transform: rotate(10deg); } 100% { transform: rotate(0); } } @keyframes blink { 0%, 50%, 100% { transform: scale(1); } 25%, 75% { transform: scale(0.5); } } </style> </head> <body> <div class="koi"> <div class="tail"></div> <div class="body"></div> <div class="head"> <div class="eye"></div> </div> </div> </body> </html> ``` 这段代码使用了 HTML 和 CSS 实现了一个简单的锦鲤动画。在浏览器中打开这个 HTML 文件,你就可以看到一个游动的锦鲤了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值